This is an old revision of the document!


guard convert
#!/bin/sh
 
 
# Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
#
# Disclaimer:
#
# EXCEPT WHERE EXPRESSLY PROVIDED OTHERWISE, THE INFORMATION, SOFTWARE,
# PROVIDED ON AN \"AS IS\" AND \"AS AVAILABLE\" BASIS. ORACLE EXPRESSLY DISCLAIMS
# ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NON-INFRINGEMENT. ORACLE MAKES NO WARRANTY THAT: (A) THE RESULTS
# THAT MAY BE OBTAINED FROM THE USE OF THE SOFTWARE WILL BE ACCURATE OR
# RELIABLE; OR (B) THE INFORMATION, OR OTHER MATERIAL OBTAINED WILL MEET YOUR
# EXPECTATIONS. ANY CONTENT, MATERIALS, INFORMATION OR SOFTWARE DOWNLOADED OR
# OTHERWISE OBTAINED IS DONE AT YOUR OWN DISCRETION AND RISK. ORACLE SHALL HAVE
# NO RESPONSIBILITY FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR LOSS OF DATA THAT
# RESULTS FROM THE DOWNLOAD OF ANY CONTENT, MATERIALS, INFORMATION OR SOFTWARE.
#
# ORACLE RESERVES THE RIGHT TO MAKE CHANGES OR UPDATES TO THE SOFTWARE AT ANY
# TIME WITHOUT NOTICE.
#
# Limitation of Liability:
#
# IN NO EVENT SHALL ORACLE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL OR CONSEQUENTIAL DAMAGES, OR DAMAGES FOR LOSS OF PROFITS, REVENUE,
# DATA OR USE, INCURRED BY YOU OR ANY THIRD PARTY, WHETHER IN AN ACTION IN
# CONTRACT OR TORT, ARISING FROM YOUR ACCESS TO, OR USE OF, THE SOFTWARE.
 
 
###############################################################################
############    SCRIPT VARIABLES HERE   #######################################
###############################################################################
VERSION=0001                              # script version
MAX_RPCOUNT=2000                          # max permitted restore point entries
PHYSRU_DEBUG=0                            # set to 1 for additional tracing
 
# Customizable settings (proceed with caution)
LOC_BACKUP_FILES="$ORACLE_HOME/dbs/"      # location of backup files
 
LOG_PHYSRU_ENABLED=1                      # output to logfile enabled
LOG_PHYSRU_FILE="physru.log"              # filename of script output log
LOG_SQL_ERRORS=1                          # 1 to display sql error context
LOG_SQL_EXETMP="physru_sql.tmp"           # temp file for executing sql
LOG_UNSUPP_FILE="physru_unsupported.log"  # filename of unsupported types log
 
LSP_APPLY_LAG=30                          # apply lag target (sec) for lsp
LSP_APPLY_LAG_TIMEOUT=30                  # timeout (min) for mrp to sync
LSP_DICT_LOAD_INTERVAL=10                 # time (sec) in b/w dict load checks
LSP_DICT_LOAD_TIMEOUT=30                  # timeout (min) for dict load
LSP_START_TIMEOUT=10                      # timeout (min) for lsp to start
LSP_START_INTERVAL=5                      # time (sec) in b/w lsp start checks
 
MRP_APPLY_LAG=30                          # apply lag limit (sec) for mrp
MRP_APPLY_LAG_TIMEOUT=30                  # timeout (min) for mrp to sync
MRP_REDO_PROG_INTERVAL=15                 # time (sec) between progress checks
MRP_START_INTERVAL=5                      # time (sec) in b/w mrp start checks
MRP_START_TIMEOUT=10                      # timeout (min) for mrp to start
MRP_UPGRADE_TIMEOUT=15                    # timeout (min) mrp scn is stagnant
                                          #   during recovery of upgrade redo 
RPFX=PRU                                  # prefix for restore point names
 
STB_APPLY_LAG_INTERVAL=30                 # time (sec) in b/w apply lag checks
STB_EOR_INTERVAL=10                       # time (sec) in b/w eor done checks
STB_EOR_TIMEOUT=15                        # timeout (min) for eor to complete
STB_EOR_RESTART_AFTER=5                   # timeout (min) to restart lsp/mrp
STB_VIEW_INIT_INTERVAL=3                  # time (sec) in b/w view init checks
 
 
 
###############################################################################
# NAME:        autopru
#
# DESCRIPTION: 
#   This is the main entry point of the script.
#
# INPUT(S):
#   Arguments: 
#     $1: database user
#     $2: tns service name to primary
#     $3: tns service name to physical standby
#     $4: db_unique_name of primary (or any identifying string)
#     $5: db_unique_name of standby (or any identifying string)
#     $6: target rdbms version
#
#   Globals: 
#     $user $db1tns $db2tns $db1name $db2name $stage $task
#
# RETURN:
#   0: Success 1: Error
#
###############################################################################
autopru()
{
 
# Save tty settings
origstty=`stty -g`
 
# Declare signal handler
trap 'inthandler' 2
 
# We need all 6 parameters
if [ "$#" -ne "6" ]; then
  display_usage
  echo -e "\nERROR: $# of the 6 required parameters have been specified\n"
  exit 1
fi
 
# Display Oracle banner
# display_banner
 
# Prompt for password
stty -echo
echo "Please enter the sysdba password: "
read passwd
stty $origstty
 
# Global variables
user=$1                                 # username (assumes same on pri/stb)
db1tns=$2                               # db1 (orig pri) tns connect string
db2tns=$3                               # db2 (orig stb) tns connect string
db1name=$4                              # db1 db_unique_name (set in S1)
db2name=$5                              # db2 db_unique_name (set in S1)
upgver=$6                               # upgraded version number
stage=0                                 # current stage in the script
task=1                                  # current task within a stage 
suppress=0                              # suppress all output
suppresst=0                             # suppress terminal output
suppressl=0                             # supporess logfile output
db1iname=""                             # db1 instance name (assoc w/db1tns)
db2iname=""                             # db2 instance name (assoc w/db2tns)
db1iid=0                                # db1 instance id (assoc w/db1tns)
db2iid=0                                # db2 instance id (assoc w/db2tns)
db1version="0.0.0.0"                    # db1 complete rdbms version
db2version="0.0.0.0"                    # db2 complete rdbms version
db1ver="0.0"                            # db1 short rdbms version
db2ver="0.0"                            # db2 short rdbms version
sql_out=""                              # output from last sql_exec call
 
S0_initialization                       # set stage, task vars to start/resume
while [ "$stage" ]
do
  case "$stage" in
     1) S1_backup;;                     # enable full rollback on script abort
     2) S2_physical_to_logical;;        # convert physical to transient logical
 
     #  ... user upgrades db2 ...
 
     3) S3_post_upgrade_validation;;    # validate upgraded environment
     4) S4_switchover;;                 # make db2 the new primary
     5) S5_primary_to_physical;;        # convert old primary to physical
 
     #  ... user starts db1 on new binary ...
 
     6) S6_recover_through_upgrade;;    # run media recovery through upgrade
     7) S7_switchback;;                 # make db1 the new primary
     8) S8_statistics;;                 # report statistics
     *) break;;
  esac
 
  # Proceed to the next stage
  stage=`expr $stage + 1`
  task=1
 
done
S9_cleanup                              # cleanup after script
 
display_raw "\nSUCCESS: The physical rolling upgrade is complete\n"
}
 
 
###############################################################################
############   STAGE DEFINITIONS    ##+########################################
###############################################################################
 
 
###############################################################################
# NAME:        S0_initialization
#
# DESCRIPTION: 
#   In this stage we decide if we should start the rolling upgrade from scratch
#   or resume where the last invocation was interrupted.  If we start from 
#   scratch, an initial flashback restore point is created and the current 
#   control file is backed up.  We do this to give the user the option to 
#   cleanly recover from the actions taken by this script.  If we resume from a
#   prior invocation, this routine will set the stage and task global variables
#   so that control is transfered to the appropriate point of execution.
#   
#   A word about resuming... we achieve this by using guaranteed flashback 
#   restore points to track this script's progress.  Each restore point name
#   is encoded with a stage id and a task id which identifies a block of code
#   in this script.  When this script starts, it uses the stage and task from
#   the most recent restore point to initialize its execution.  To protect 
#   ourselves from doing harm, the very first restore point name is encoded
#   with the version number of the authoring script.  We only permit resuming
#   if an executing script has a matching version number.
#
#   N.B.: It might seem overkill to bother with version, but I can easily
#   imagine a scenario where a user runs into an error and decides to track 
#   down a more recent release of the script.  If a user was to then run the 
#   script atop the broken configuration, we could potentially do harm.
#
# INPUT(S):
#   Arguments:
#     None
#
#   Globals:
#     $user $passwd $db1tns $db2tns $db1name $db2name $stage $task $VERSION
#
# RETURN:
#   stage: the # of the initial stage or resume stage
#   task:  the # of the initial task or resume task
#
###############################################################################
S0_initialization()
{
display_raw "\n### Initialize script to either start over or resume execution"
isready=1
runningon=0
 
#
# Fetch primary and standby rdbms version 
#
display "Identifying rdbms software version"
 
get_rdbms_version $user $passwd $db1tns $db1name
db1version=$l_grv_val
display "database $db1name is at version $db1version"
 
get_rdbms_version $user $passwd $db2tns $db2name
db2version=$l_grv_val
display "database $db2name is at version $db2version"
 
# Save short form of database version (assume 2-digit major 1-digit minor)
db1ver=`echo $db1version | cut -d '.' -f 1-2`
db2ver=`echo $db2version | cut -d '.' -f 1-2`
 
#
# Fetch primary and standby db unique names
#
get_db_unique_name $user $passwd $db1tns $db1name
if [ "$?" -eq "0" ]; then
  db1name=$l_dun_val
fi
get_db_unique_name $user $passwd $db2tns $db2name
if [ "$?" -eq "0" ]; then
  db2name=$l_dun_val
fi
 
#
# Fetch primary and standby instance names
#
get_instance_name $user $passwd $db1tns $db1name
db1iname=$l_gin_val
 
get_instance_name $user $passwd $db2tns $db2name
db2iname=$l_gin_val
 
# 
# Flashback database must be enabled at both db1 and db2
#
display "verifying flashback database is enabled at $db1name and $db2name"
is_flashback_enabled $user $passwd $db1tns $db1name
if [ "$?" -eq "0" ]; then
  display "ERROR: flashback database disabled at $db1name"
  isready=0
fi
is_flashback_enabled $user $passwd $db2tns $db2name
if [ "$?" -eq "0" ]; then
  display "ERROR: flashback database disabled at $db2name"
  isready=0
fi
 
# Flashback retention is of sufficient size (is this predictable???)
 
# 
# Enough free restore point entries available to accommodate our use of them
#
display "verifying available flashback restore points"
get_flashback_restore_count $user $passwd $db1tns $db1name
if [ "$l_gfr_val" -gt "$MAX_RPCOUNT" ]; then
  display "ERROR: $l_gfr_val restore points at $db1name exceeds maximum of $MAX_RPCOUNT"
  isready=0
fi
 
get_flashback_restore_count $user $passwd $db2tns $db2name
if [ "$l_gfr_val" -gt "$MAX_RPCOUNT" ]; then
  display "ERROR: $l_gfr_val restore points at $db2name exceeds maximum of $MAX_RPCOUNT"
  isready=0
fi
 
#
# The Data Guard Broker must be disabled
#
display "verifying DG Broker is disabled"
is_dg_enabled $user $passwd $db1tns $db1name
if [ "$?" -eq "1" ]; then
  display "ERROR: parameter DG_BROKER_START on $db1name must be set to FALSE"
  isready=0
fi
is_dg_enabled $user $passwd $db2tns $db2name
if [ "$?" -eq "1" ]; then
  display "ERROR: parameter DG_BROKER_START on $db2name must be set to FALSE"
  isready=0
fi
 
#
# Exit now if one or more warnings have been found
#
if [ "$isready" -eq "0" ]; then
  display "exiting: errors must be addressed in order to proceed"
  exit 1
fi
 
#
# Check if we are to resume the rolling upgrade from the last invocation.  If 
# any resume state exists, an initial restore point will exist with the script
# version embedded in the name.
#
resuming=1
display "looking up prior execution history"
get_resume_version $user $passwd $db2tns $db2name
resumever=`echo $l_grv_val | awk '{print int(\$0)}'`
scriptver=`echo $VERSION | awk '{print int(\$0)}'`
if [ "$resumever" -ne "0" ]; then
  if [ "$resumever" -ne "$scriptver" ]; then
    #
    # The script versions do not match.  It's most likely that a prior rolling 
    # upgrade attempt has failed, and the user is now re-attempting the rolling
    # upgrade with a new version of this script.  This should be rare, and the 
    # user must revalidate their setup, and start from scratch.
    #
    display_raw "\nNOTE: A different version of this script has failed in the past leaving"
    display_raw "      progress-related state that may not correspond with this version of the "
    display_raw "      script.  This script will purge all state left by the older script, and "
    display_raw "      start the physical rolling upgrade from scratch."
    display_raw "\n      If you need to revisit your configuration to ensure it meets the "
    display_raw "      prerequisites for physical rolling upgrade or simply don't want to "
    display_raw "      continue, you should answer 'n' to the following question:\n"
 
    prompt "      Are you ready to start this script from scratch?" "y/n"
    if [ "$l_p_val" = "y" ]; then
      display "continuing"
      # Set flag to indicate start from scratch
      resuming=0
    else
      display "exiting"
      exit
    fi
 
  else
    # Fetch the stage and task that was last completed
    get_resume_state $user $passwd $db2tns $db2name
    resume_stage=`echo $l_grs2_stage | awk '{print int(\$0)}'`
    resume_task=`echo $l_grs2_task | awk '{print int(\$0)}'`
 
    # If found, the next task is where we want to start
    if [ "$resume_stage" -gt "0" ]; then
      display "last completed stage [${resume_stage}-${resume_task}] using script version $l_grv_val"
 
      # Resuming from other than these two states implies an error had occurred
      prompt_user=1
      if [ "$resume_stage" -eq "2" ] && [ "$resume_task" -eq "4" ]; then
        prompt_user=0
       fi
      if [ "$resume_stage" -eq "5" ] && [ "$resume_task" -eq "4" ]; then
        prompt_user=0
      fi
 
      # Prompt user
      if [ "$prompt_user" -eq "1" ]; then
        display_raw "\nWARN: The last execution of this script either exited in error or at the "
        display_raw "      user's request.  At this point, there are three available options:\n"
        display_raw "        1) resume the rolling upgrade where the last execution left off"
        display_raw "        2) restart the script from scratch"
        display_raw "        3) exit the script"
        display_raw "\n      Option (2) assumes the user has restored the primary and physical "
        display_raw "      standby back to the original configuration as required by this script.\n"
        prompt "Enter your selection" "1/2/3"
        if [ "$l_p_val" = "1" ]; then
          resuming=1
        else
          if [ "$l_p_val" = "2" ]; then
            resuming=0
            break
          else
            if [ "$l_p_val" = "3" ]; then
              exit
            fi
          fi
        fi
      fi
    else
      # A stage of 0 is possible if the script's version restore point is the only entry
      resuming=0
    fi
  fi
else
  resuming=0
fi
 
# Script will resume at the next task
if [ "$resuming" -eq "1" ]; then
  display "resuming execution of script"
  stage=$resume_stage
  task=$resume_task
  task=`expr $task + 1`
else
  # Script will start from scratch
  purge_resume_state $user $passwd $db1tns $db1name
  purge_resume_state $user $passwd $db2tns $db2name
  display "starting new execution of script"
  stage=1
  task=1
fi
}
 
 
###############################################################################
# NAME:        S1_backup
#
# DESCRIPTION: 
#   In this stage, we ensure that the user will be able to cleanly recover if 
#   they wish to abandon the rolling upgrade.  This involves creating initial 
#   flashback restore points, and backing up the control files on the primary 
#   and standby databases.  
#
# INPUT(S):
#   Arguments: 
#     None
# 
#   Globals:
#     $user $passwd $db1tns $db2tns $db1name $db2name $task $VERSION
#
# RETURN:
#   None
#
###############################################################################
S1_backup()
{
display_stage "Backup user environment in case rolling upgrade is aborted"
 
# 
# Step through each task
#
while [ "$task" ]
do 
  case "$task" in
 
  1)  # Create initial restore point and backup control files
 
      # Stop media recovery since we're about to create a restore point 
      stop_media_recovery $user $passwd $db2tns $db2name
 
      # Create initial restore point on standby
      create_restore_point $user $passwd $db2tns $db2name ${RPFX}_0000_${VERSION}
 
      # Create a backup control file on standby
      create_backup_ctlfile $user $passwd $db2tns $db2name ${RPFX}_${VERSION}_${db2name}_f.f
 
      # Create initial restore point on primary
      create_restore_point $user $passwd $db1tns $db1name ${RPFX}_0000_${VERSION}
 
      # Create a backup control file on primary
      create_backup_ctlfile $user $passwd $db1tns $db1name ${RPFX}_${VERSION}_${db1name}_f.f
 
      # Remind user of these safepoints
      display_raw "\nNOTE: Restore point ${RPFX}_0000_0001 and backup control file ${RPFX}_${VERSION}_${db2name}_f.f "
      display_raw "      can be used to restore ${db2name} back to its original state as a "
      display_raw "      physical standby, in case the rolling upgrade operation needs to be aborted "
      display_raw "      prior to the first switchover done in Stage 4."
      ;;
 
  *) break;;
  esac
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
done
}
 
###############################################################################
# NAME:        S2_physical_to_logical
#
# DESCRIPTION:
#   In this stage the physical standby is converted into a transient logical
#   standby database.  Before the conversion, the primary is checked for 
#   datatypes which may be problematic for the transient logical standby.
#   If found, these datatypes are written to an output log, so the user can
#   review them.
#
#   The actual conversion into a transient logical is achieved via the 
#   recover to logical standby database ddl.  This ddl runs media recovery
#   in a special mode responsible which looks specifically for the dictionary.
#   The section in the script below which performs this may perform multiple
#   builds in the event this script is interrupted.  This is necessary to avoid
#   the possibility that recovery was run by the user beyond the dictionary,
#   making instantiation not possible without a subsequent dictionary build.
#
# INPUT(S):
#   Arguments: 
#     None
# 
#   Globals:
#     $user $passwd $db1tns $db2tns $db1name $db2name $task
#     $LSP_APPLY_LAG $LSP_APPLY_LAG_TIMEOUT $MRP_APPLY_LAG 
#     $MRP_APPLY_LAG_TIMEOUT
#
# RETURN:
#   None
#
###############################################################################
S2_physical_to_logical()
{
display_stage "Create transient logical standby from existing physical standby"
 
# 
# RAC standbys must be reduced to a single instance for the duration of the
# instantiation.
#
l_s2_hint="unknown"
if [ "$task" -ge "1" ] && [ "$task" -le "4" ]; then
  while [ "1" ]
  do
    display "verifying RAC is disabled at $db2name"
    is_rac_database $user $passwd $db2tns $db2name
    if [ "$?" -eq "0" ]; then
      break
    fi
 
    l_s2_hint="rac"
    display_rac_demote $user $passwd $db2tns $db2name $db2iname "instantiation"
    display_raw "      Once these steps have been performed, enter 'y' to continue the script."
    display_raw "      If desired, you may enter 'n' to exit the script to perform the required"
    display_raw "      steps, and recall the script to resume from this point.\n"
    prompt "Are you ready to continue?" "y/n"
    if [ "$l_p_val" = "y" ]; then
      display "continuing"
    else
      display "exiting"
      exit
    fi
  done
fi
 
# 
# Step through each task
#
while [ "$task" ] 
do 
  case "$task" in
 
  1)  # Prerequisites
      isready=1
 
      # db1 should be the primary and db2 should be a physical standby
      display "verifying database roles"
      is_database_role $user $passwd $db1tns $db1name "PRIMARY"
      if [ "$?" -eq "0" ]; then
        fail "$db1name is not a primary database"
      fi
      is_database_role $user $passwd $db2tns $db2name "PHYSICAL STANDBY"
      if [ "$?" -eq "0" ]; then
        fail "$db2name is not a physical standby database"
      fi
 
      # Physical standby must be mounted
      display "verifying physical standby is mounted"
      is_open_mode $user $passwd $db2tns $db2name "MOUNTED"
      if [ "$?" -eq "0" ]; then
        display "ERROR: $db2name must be in MOUNTED mode"
        isready=0;
      fi
 
      # We do not allow maximum protection mode
      display "verifying database protection mode"
      get_protection_mode $user $passwd $db1tns $db1name
      if [ "$l_gpm_val" = "MAXIMUM PROTECTION" ]; then
        display "ERROR: maximum protection mode not permitted"
        isready=0
      fi
 
      # Exit if any requirements not met
      if [ "$isready" -ne "1" ]; then
        display "exiting: errors must be addressed in order to proceed"
        exit 1
      fi
 
      # Check for unsupported datatypes
      display "verifying transient logical standby datatype support"
      l_ptl1_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
      whenever sqlerror exit sql.sqlcode 
      select count(*) from dba_logstdby_unsupported; 
      exit;"
      sql_exec $user $passwd $db1tns "$l_ptl1_sql"
      chkerr $? "failed to lookup unsupported datatypes on $db1name"
 
      # Write out unsupported datatypes 
      l_ptl1=`echo $sql_out`
      if [ "$l_ptl1" -gt "0" ]; then
        l_ptl2_sql="spool $LOG_UNSUPP_FILE 
        set pagesize 50000 linesize 165 feedback off verify off heading on echo off tab off 
        whenever sqlerror exit sql.sqlcode 
        set heading on 
        select * from dba_logstdby_unsupported; 
        spool off; 
        exit;" 
        sql_exec $user $passwd $db1tns "$l_ptl2_sql"
        chkerr $? "failed to query unsupported datatypes"
 
        # Ask permission to continue
        display_raw "\nWARN: Objects have been identified on the primary database which will not be"
        display_raw "      replicated on the transient logical standby.  The complete list of "
        display_raw "      objects and their associated unsupported datatypes can be found in the"
        display_raw "      dba_logstdby_unsupported view.  For convenience, this script has written"
        display_raw "      the contents of this view to a file - $LOG_UNSUPP_FILE."
        display_raw "\n      Various options exist to deal with these objects such as: "
        display_raw "        - disabling applications that modify these objects"
        display_raw "        - manually resolving these objects after the upgrade"
        display_raw "        - extending support to these objects (see metalink note: 559353.1)"
        display_raw "\n      If you need time to review these options, you should enter 'n' to exit"
        display_raw "      the script.  Otherwise, you should enter 'y' to continue with the "
        display_raw "      rolling upgrade.\n"
        prompt "Are you ready to proceed with the rolling upgrade?" "y/n"
        if [ "$l_p_val" = "y" ]; then
          display "continuing"
        else
          display "exiting"
          exit
        fi
      fi
      ;;
 
  2)  # Logical build and recover to logical standby
 
      # N.B.: We bundle the build and recover together since it's possible a 
      #       failure could occur before the recover to logical reaches the 
      #       build marker.  Any manual startup of the MRP could result in 
      #       recovering beyond the build marker making instantiation 
      #       impossible without another build.
 
      is_database_role $user $passwd $db2tns $db2name "PHYSICAL STANDBY"
      if [ "$?" -eq "1" ]; then
 
        # Stop media recovery in case it was left running
        stop_media_recovery $user $passwd $db2tns $db2name
 
        # Start media recovery
        start_media_recovery $user $passwd $db2tns $db2name
 
        # Switch logs on the primary
        switch_logs $user $passwd $db1tns $db1name
 
        # Wait for media recovery to catch up to a recent scn
        minimize_apply_lag $user $passwd $db1tns $db1name $db2tns $db2name $db2ver $MRP_APPLY_LAG $MRP_APPLY_LAG_TIMEOUT
 
        # Stop media recovery
        stop_media_recovery $user $passwd $db2tns $db2name 
 
        # Dump the logminer dictionary
        start_logical_build $user $passwd $db1tns $db1name
 
        # Recover to logical standby
        recover_to_logical $user $passwd $db2tns $db2name
      fi
      ;;
 
  3)  # Open transient logical standby database
 
      # N.B.: A bug in v$dataguard_stats apply lag prevents direct open
      if [ "$db2ver" = "11.1" ]; then
        shutdown_database $user $passwd $db2tns $db2name
        mount_database $user $passwd $db2tns $db2name
      fi
 
      # Instance must be in mounted mode
      is_open_mode $user $passwd $db2tns $db2name "MOUNTED"
      if [ "$?" -eq "0" ]; then
        fail "$db2name must be in MOUNTED mode"
      fi
 
      # Open the transient logical database
      open_database $user $passwd $db2tns $db2name
      ;;
 
  4)  # Configure and startup logical standby
 
      # NOTE: If <task> is assigned a new number from 4, the code in 
      #       S6_recovery_through_upgrade (task == 2) which
      #       calculates media recovery's completion of the upgrade
      #       redo, must be modified to consider the new resulting 
      #       restore point.
 
      # Instance must be open read write
      is_open_mode $user $passwd $db2tns $db2name "READ WRITE"
      if [ "$?" -eq "0" ]; then
        fail "$db2name must be in READ WRITE mode"
      fi
 
      # Stop logical standby (in case an error left it running)
      stop_logical_apply $user $passwd $db2tns $db2name
 
      # Set rolling upgrade parameters
      set_rolling_upgrade_params $user $passwd $db2tns $db2name
 
      # Start logical standby
      start_logical_apply $user $passwd $db2tns $db2name
 
      # Wait until dictionary load has finished
      wait_logical_dictload $user $passwd $db2tns $db2name
 
      # Switch logs on the primary
      switch_logs $user $passwd $db1tns $db1name
 
      # Wait until apply lag is within 5 minutes
      minimize_apply_lag $user $passwd $db1tns $db1name $db2tns $db2name $db2ver $LSP_APPLY_LAG $LSP_APPLY_LAG_TIMEOUT
 
      # Force a checkpoint since we're about to exit
      checkpoint
 
      # Display message that we're ready for them to ugprade.
      display_raw "\nNOTE: Database $db2name is now ready to be upgraded.  This script has left the"
      display_raw "      database open in case you want to perform any further tasks before "
      display_raw "      upgrading the database.  Once the upgrade is complete, the database must"
      display_raw "      opened in READ WRITE mode before this script can be called to resume the "
      display_raw "      rolling upgrade."
 
      # Display instructions on how to re-enable RAC
      display_rac_promote $user $passwd $db2tns $db2name $db2iname "db2upgrade" "$l_s2_hint"
 
      exit
      ;;
 
  *)  break;;
  esac
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
 
done
} # S2_physical_to_logical()
 
###############################################################################
# NAME:        S3_post_upgrade_validation
#
# DESCRIPTION: 
#   When this stage is entered, the user has already completed the manual 
#   upgrade, and has re-called this script to resume the rolling upgrade.  In 
#   this stage, the upgraded physical standby is validated to be on the 
#   newer binary, and no longer upgrading.
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db2tns $db2name $task
#
# RETURN:
#   None
#
###############################################################################
S3_post_upgrade_validation()
{
display_stage "Validate upgraded transient logical standby"
 
# 
# Step through each task
#
while [ "$task" ] 
do 
  case "$task" in
  1)  # Validation
 
      # Database is no longer in open migrate mode
      is_open_mode $user $passwd $db2tns $db2name "OPEN MIGRATE"
      if [ "$?" -eq "1" ]; then
        fail "$4 must not be in OPEN MIGRATE mode"
      fi
      display "database $db2name is no longer in OPEN MIGRATE mode"
 
      # Database is on correct target version
      if [ "$db2version" != "$upgver" ]; then
        fail "$db2name is not at version $upgver"
      fi
      display "database $db2name is at version $upgver"
 
      # Future: EDS trigger deletion
      ;;
 
  *)  break;;
  esac 
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
done
} # S3_post_upgrade_validation
 
###############################################################################
# NAME:        S4_switchover
#
# DESCRIPTION: 
#   In this stage, the primary and upgraded, transient logical standby switch
#   roles.  This step is necessary since the former primary will be flashed 
#   back and converted into a physical standby which will then recover the 
#   upgrade redo.
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db1tns $db2tns $db1name $db2name $task
#
# RETURN:
#   None
#
###############################################################################
S4_switchover()
{
display_stage "Switch the transient logical standby to be the new primary"
 
# 
# Step through each task
#
while [ "$task" ]
do 
  case "$task" in
  1)  # Preparing logical standby for switchover
 
      is_database_role $user $passwd $db1tns $db1name "PRIMARY"
      if [ "$?" -eq "0" ]; then
        fail "$db1name is not a primary database"
      fi
      is_database_role $user $passwd $db2tns $db2name "LOGICAL STANDBY"
      if [ "$?" -eq "0" ]; then
        fail "$db2name is not a logical standby database"
      fi
 
      is_open_mode $user $passwd $db1tns $db1name "READ WRITE"
      if [ "$?" -eq "0" ]; then
        fail "$db1name must be in READ WRITE mode"
      fi
 
      is_open_mode $user $passwd $db2tns $db2name "READ WRITE"
      if [ "$?" -eq "0" ]; then
        fail "$db2name must be in READ WRITE mode"
      fi
 
      display "waiting for $db2name to catch up (this could take a while)"
      switch_logs $user $passwd $db1tns $db1name
      start_logical_apply $user $passwd $db2tns $db2name
      minimize_apply_lag $user $passwd $db1tns $db1name $db2tns $db2name $db2ver $LSP_APPLY_LAG $LSP_APPLY_LAG_TIMEOUT
      ;;
 
  2)  # Switch $db1name to the logical standby role
      switch_primary_to_logical $user $passwd $db1tns $db1name
      ;;
 
  3)  # Confirm $db2name has witnessed the role change
      wait_standby_eor $user $passwd $db2tns $db2name $db2ver
      ;;
 
  4)  # Switch $db2name to the primary role
      switch_logical_to_primary $user $passwd $db2tns $db2name
      ;;
 
  *)  break;;
  esac  
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
done
 
} # S4_switchover
 
###############################################################################
# NAME:        S5_primary_to_physical
#
# DESCRIPTION: 
#   In this stage, the former primary (now a logical standby as the result of a
#   switchover) is flashed back and converted into a physical standby.  The 
#   script exits after the flashback since the user is responsible for starting
#   up the physical standby using the newer binary.
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db1tns $db1name $task
#
# RETURN:
#   None
#
###############################################################################
S5_primary_to_physical()
{
display_stage "Flashback former primary to pre-upgrade restore point and convert to physical"
 
# 
# db1 must be reduced to a single instance for the conversion to a physical 
#
if [ "$task" -ge "1" ] && [ "$task" -le "4" ]; then
  is_rac_database $user $passwd $db1tns $db1name
  if [ "$?" -eq "1" ]; then
    while [ "1" ]
    do
      display "verifying instance $db1iname is the only active instance"
      display_rac_demote $user $passwd $db1tns $db1name $db1iname "convert"
      if [ "$?" -eq "0" ]; then
        break
      else
        display_raw "      Once these steps have been performed, enter 'y' to continue the script."
        display_raw "      If desired, you may enter 'n' to exit the script to perform the required"
        display_raw "      steps, and recall the script to resume from this point.\n"
        prompt "Are you ready to continue?" "y/n"
        if [ "$l_p_val" = "y" ]; then
          display "continuing"
        else
          display "exiting"
          exit
        fi
      fi
    done
  fi
fi
 
# 
# Step through each task
#
l_s5_db1israc=0
while [ "$task" ] 
do 
  case "$task" in
  1)  # After the last switchover, db1 should be a logical standby
      is_database_role $user $passwd $db1tns $db1name "LOGICAL STANDBY"
      if [ "$?" -eq "0" ]; then
        fail "$db1name is not a logical standby database"
      fi
 
      # After the last switchover, db2 should be the new primary
      is_database_role $user $passwd $db2tns $db2name "PRIMARY"
      if [ "$?" -eq "0" ]; then
        fail "$db2name is not a primary database"
      fi
 
      # Restart database in mounted mode
      shutdown_database $user $passwd $db1tns $db1name
      mount_database $user $passwd $db1tns $db1name
      ;;
 
  2)  # Flashing back former primary to pre-upgrade restore point
      is_open_mode $user $passwd $db1tns $db1name "MOUNTED"
      if [ "$?" -eq "0" ]; then
        fail "$db1name must be in MOUNTED mode"
      fi
      flashback_database $user $passwd $db1tns $db1name ${RPFX}_0000_${VERSION}
      ;;
 
  3)  # Converting primary to physical standby database
      convert_to_physical $user $passwd $db1tns $db1name
      ;;
 
  4)  # Save RAC status prior to shutting down the database
      is_rac_database $user $passwd $db1tns $db1name
      l_s5_db1israc=$?
 
      # Shutting down database $db1name
      shutdown_database $user $passwd $db1tns $db1name
 
      # Force a checkpoint since we're about to exit
      checkpoint
 
      # User must restart db1 with the new oracle binary
      display_raw "\nNOTE: Database $db1name has been shutdown, and is now ready to be started "
      display_raw "      using the newer version Oracle binary.  This script requires the "
      display_raw "      database to be mounted (on all active instances, if RAC) before calling "
      display_raw "      this script to resume the rolling upgrade."
 
      if [ "$l_s5_db1israc" -eq "1" ]; then
        display_rac_promote $user $passwd $db1tns $db1name $db1iname "db1upgrade"
      else
        display_raw " "
      fi
      exit
      ;;
 
  *)  break;;
  esac
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
done
 
} # S5_primary_to_physical
 
###############################################################################
# NAME:        S6_recover_through_upgrade
#
# DESCRIPTION: 
#   In this stage, the former primary (now a physical standby on a newer binary)
#   is monitored as it recovers up to and through the upgrade redo.  During 
#   this stage, media recovery's progress is frequently output along with
#   estimated completion times.
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db1tns $db2tns $db1name $db2name $task
#
# RETURN:
#   None
#
###############################################################################
S6_recover_through_upgrade()
{
display_stage "Run media recovery through upgrade redo"
 
# Nothing to do if task 3 was the last completed task
if [ "$task" -gt "3" ]; then
  return;
fi
 
# Defer exiting so the user gets as much repair context as possible up front
l_rtu_ready=1
 
# db1 must be physical standby, mounted, and on the upgrade version
is_database_role $user $passwd $db1tns $db1name "PHYSICAL STANDBY"
if [ "$?" -eq "0" ]; then
  display "ERROR: $db1name is not a physical standby database"
  l_rtu_ready=0
fi
is_open_mode $user $passwd $db1tns $db1name "MOUNTED"
if [ "$?" -eq "0" ]; then
  display "ERROR: $db1name must be in MOUNTED mode"
  l_rtu_ready=0
fi
if [ "$db1version" != "$upgver" ]; then
  display "ERROR: $db1name is not at version $upgver"
  l_rtu_ready=0
fi
 
# db2 must be a primary, open read write, and on the upgrade version
is_database_role $user $passwd $db2tns $db2name "PRIMARY"
if [ "$?" -eq "0" ]; then
  display "ERROR: $db2name is not a primary database"
  l_rtu_ready=0
fi
is_open_mode $user $passwd $db2tns $db2name "READ WRITE"
if [ "$?" -eq "0" ]; then
  display "ERROR: $db2name must be in READ WRITE mode"
  l_rtu_ready=0
fi
if [ "$db2version" != "$upgver" ]; then
  display "ERROR: $db2name is not at version $upgver"
  l_rtu_ready=0
fi
 
# Exit if any unsatisfied requirements
if [ "$l_rtu_ready" -eq "0" ]; then
  display "exiting: errors must be addressed in order to proceed"
  exit 1;
fi
 
# Wait for media recovery to recover through upgrade
# Obtain the scn boundaries which define the upgrade region.  We make 
# use of the scns associated with the restore points created at 
# stage 2, task 4 and stage 3, task 1 to define the upgrade region.  
 
# Get scn where upgrade starts
get_flashback_scn $user $passwd $db2tns $db2name "${RPFX}_0204"
l_rtu_begscn=$l_gfs_val
 
# Get scn where upgrade ends
get_flashback_scn $user $passwd $db2tns $db2name "${RPFX}_0301"
l_rtu_endscn=$l_gfs_val
 
# Use the start/end range as the scale for media recovery's progress
sql_eval $user $passwd $db2tns $db2name "( ${l_rtu_endscn} - ${l_rtu_begscn} )"
l_rtu_range=$l_se_val
display "upgrade redo region identified as scn range [${l_rtu_begscn}, ${l_rtu_endscn}]"
 
# Switch logs on the primary
switch_logs $user $passwd $db2tns $db2name
 
# Start media recovery 
start_media_recovery $user $passwd $db1tns $db1name
 
# Wait until v$recovery_progress has been initialized
display "waiting for media recovery to initialize v\$recovery_progress"
l_rtu_status=1
l_rtu_curtime=`perl -e 'print int(time)'`
l_rtu_exptime=`expr $MRP_START_TIMEOUT "*" 60`
l_rtu_maxtime=`expr $l_rtu_curtime + $l_rtu_exptime`
while [ "$l_rtu_curtime" -lt "$l_rtu_maxtime" ]
do
  get_recovery_scn $user $passwd $db1tns $db1name $db1ver
  l_rtu_curscn=$l_grs_val
  if [ "$l_rtu_curscn" != "0" ]; then
    l_rtu_status=0
    break
  fi
  sleep $STB_VIEW_INIT_INTERVAL
  l_rtu_curtime=`perl -e 'print int(time)'`
done
chkerr $l_rtu_status "timed out after $MRP_START_TIMEOUT minutes of inactivity"
l_rtu_lsttime=$l_rtu_curtime
l_rtu_lstscn=$l_rtu_curscn
 
# Incur one wait interval before taking the next snapshot
sleep $MRP_REDO_PROG_INTERVAL
 
# Report on the MRP's progress 
display "monitoring media recovery's progress"
l_rtu_status=1
l_rtu_curtime=`perl -e 'print int(time)'`
l_rtu_numrate=0
l_rtu_totrate=0
l_rtu_lstprog=0
while [ "$l_rtu_curtime" -lt "$l_rtu_maxtime" ]
do
  # Get media recovery's current scn (ie last applied) and current time
  get_recovery_scn $user $passwd $db1tns $db1name $db1ver
  l_rtu_curscn=$l_grs_val
 
  if [ "$PHYSRU_DEBUG" = "1" ]; then
    display "DEBUG: cur ($l_rtu_curscn) lst ($l_rtu_lstscn)"
  fi
 
  # Validate the just fetched recovery scn and time
  # N.B.: I've seen this on few occasions return dirty values (either 0 or an 
  #       scn less than the previously fetched scn).  In either case, discard
  #       the values and try at the next interval.
  if [ "$l_rtu_curscn" = "0" ] || (sql_lt $user $passwd $db1tns "$l_rtu_curscn" "$l_rtu_lstscn") ; then
 
    # A 0 scn could indicate the MRP is no longer active.
    if [ "$l_rtu_curscn" = "0" ]; then
      display "failed to determine the last applied scn by media recovery"
 
      # Wait for the MRP to become active
      wait_mrp_active $user $passwd $db1tns $db1name
    else
      display "WARN: last applied scn of $l_rtu_curscn is less than $l_rtu_lstscn"
    fi
 
    # Wait until the next interval for the next snapshot
    sleep $MRP_REDO_PROG_INTERVAL
    l_rtu_curtime=`perl -e 'print int(time)'`
    continue
  fi
 
  if [ "$l_rtu_curtime" -le "$l_rtu_lsttime" ]; then
    fail "current time of $l_rtu_curtime is not greater than last time of $l_rtu_lsttime"
  fi
 
  # Increment samples counter since we know we'll use the scn/time values
  l_rtu_numrate=`expr $l_rtu_numrate + 1`
 
  # MRP is beyond upgrade redo end
  if (sql_gt $user $passwd $db1tns "$l_rtu_curscn" "$l_rtu_endscn"); then
    if [ "$task" -le "3" ]; then
      task=3
      checkpoint
      task=`expr $task + 1`
    fi
 
    display "media recovery has finished recovering through upgrade"
    l_rtu_status=0
    break
  else
    # MRP is before upgrade redo start
    if (sql_lt $user $passwd $db1tns "$l_rtu_curscn" "$l_rtu_begscn"); then
      if [ "$task" -eq "1" ]; then
        checkpoint
        task=`expr $task + 1`
      fi
 
      if [ "$l_rtu_curscn" != "$l_rtu_lstscn" ]; then
        display "last applied scn $l_rtu_curscn is approaching upgrade redo start scn $l_rtu_begscn"
      fi
    else
      # MRP is currently recovering upgrade redo
      if [ "$task" -le "2" ]; then
        task=2
        checkpoint
        task=`expr $task + 1`
      fi
 
      # Calculate average apply rate
      sql_eval $user $passwd $db2tns $db2name "(( ${l_rtu_curscn} - ${l_rtu_lstscn} ) / ( ${l_rtu_curtime} - ${l_rtu_lsttime} ))"
      l_rtu_currate=$l_se_val
      l_rtu_totrate=`expr $l_rtu_totrate + $l_rtu_currate`
      l_rtu_avgrate=`expr $l_rtu_totrate / \( $l_rtu_numrate \)`
 
      # Calculate estimated time of completion
      sql_eval $user $passwd $db2tns $db2name "(( ${l_rtu_endscn} - ${l_rtu_curscn} ) / ${l_rtu_avgrate} )"
      l_rtu_sectime=$l_se_val
      l_rtu_dattime=`date "+%b %d %R:%S" -d "+${l_rtu_sectime} second"`
 
      # Report progress
      sql_eval $user $passwd $db2tns $db2name "((( ${l_rtu_curscn} - ${l_rtu_begscn} ) * 100 ) / ${l_rtu_range} )"
      l_rtu_curprog=$l_se_val
      l_rtu_strprog=`printf %02d $l_rtu_curprog`
      if [ "$l_rtu_curprog" -gt "$l_rtu_lstprog" ] && [ "$l_rtu_curprog" -lt "100" ]; then
        display "recovery of upgrade redo at ${l_rtu_strprog}% - estimated complete at $l_rtu_dattime"
      fi
      l_rtu_lstprog=$l_rtu_curprog
    fi
  fi
 
  # Reset timeout if scn has changed from last check
  if [ "$l_rtu_curscn" != "$l_rtu_lstscn" ]; then
    l_rtu_maxtime=`expr $l_rtu_curtime + $l_rtu_exptime`
  fi
 
  # Retain apply scn and apply time
  l_rtu_lstscn=$l_rtu_curscn
  l_rtu_lsttime=$l_rtu_curtime
 
  # Sleep before we check again
  sleep $MRP_REDO_PROG_INTERVAL
  l_rtu_curtime=`perl -e 'print int(time)'`
done
chkerr $l_rtu_status "timed out after $MRP_UPGRADE_TIMEOUT minutes of inactivity"
 
# Checkpoint our completion of the current task
checkpoint
 
} # S6_recover_through_upgrade
 
###############################################################################
# NAME:        S7_switchback
#
# DESCRIPTION: 
#   In this stage, the physical standby and primary databases switch roles to
#   restore the original roles prior to the roling upgrade.  By the time this
#   stage is entered, both databases have been fully upgraded.
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db1tns $db2tns $db1name $db2name $task
#
# RETURN:
#   None
#
###############################################################################
S7_switchback()
{
display_stage "Switch back to the original roles prior to the rolling upgrade"
 
if [ "$task" -eq "1" ]; then
  display_raw "\nNOTE: At this point, you have the option to perform a switchover"
  display_raw "     which will restore $db1name back to a primary database and "
  display_raw "     $db2name back to a physical standby database.  If you answer 'n'"
  display_raw "     to the question below, $db1name will remain a physical standby"
  display_raw "     database and $db2name will remain a primary database.\n"
  prompt "Do you want to perform a switchover?" "y/n"
  checkpoint
  if [ "$l_p_val" = "y" ]; then
    display "continuing"
    task=2
  else
    display "skipping final switchover"
    # Set task to switchover-end state
    task=7
  fi
fi
 
#
# Both db1 and db2 must be reduced to single instances for physical switchover
#
if [ "$task" -ge "1" ] && [ "$task" -le "6" ]; then
  while [ "1" ]
  do
    # Init flag which indicates prompt is needed
    l_s7_db1prompt=0
    l_s7_db2prompt=0
 
    # Determine if db2 (the current primary) is a RAC
    is_rac_database $user $passwd $db2tns $db2name
    if [ "$?" -eq "1" ]; then
      display "verifying instance $db2iname is the only active instance"
      display_rac_demote $user $passwd $db2tns $db2name $db2iname "primary"
      l_s7_db2prompt=$?
    fi
 
    # Only on 11.1 must db1 (the physical standby) reduce to a single instance
    if [ "$db1ver" = "11.1" ]; then
      is_rac_database $user $passwd $db1tns $db1name
      if [ "$?" -eq "1" ]; then
        display "verifying instance $db1iname is the only active instance"
        display_rac_demote $user $passwd $db1tns $db1name $db1iname "physical"
        l_s7_db1prompt=$?
      fi
    fi
 
    # Prompt user if either db1 or db2 needs to be reduced
    if [ "$l_s7_db1prompt" -eq "1" ] || [ "$l_s7_db2prompt" -eq "1" ]; then
      display_raw "      Once these steps have been performed, enter 'y' to continue the script."
      display_raw "      If desired, you may enter 'n' to exit the script to perform the required"
      display_raw "      steps, and recall the script to resume from this point.\n"
      prompt "Are you ready to continue?" "y/n"
      if [ "$l_p_val" = "y" ]; then
        display "continuing"
      else
        display "exiting"
        exit
      fi
    else
      # Not necessary to prompt
      break
    fi
  done
fi
 
while [ "$task" ]
do 
  case "$task" in
  2)  # Preparing media recovery for switchover
 
      is_database_role $user $passwd $db2tns $db2name "PRIMARY"
      if [ "$?" -eq "0" ]; then
        fail "$db2name is not a primary database"
      fi
      is_database_role $user $passwd $db1tns $db1name "PHYSICAL STANDBY"
      if [ "$?" -eq "0" ]; then
        fail "$db1name is not a physical standby database"
      fi
 
      is_open_mode $user $passwd $db2tns $db2name "READ WRITE"
      if [ "$?" -eq "0" ]; then
        fail "$db2name must be in READ WRITE mode"
      fi
 
      is_open_mode $user $passwd $db1tns $db1name "MOUNTED"
      if [ "$?" -eq "0" ]; then
        fail "$db1name must be in MOUNTED mode"
      fi
 
      # Start media recovery (typically it's already running)
      switch_logs $user $passwd $db2tns $db2name
      start_media_recovery $user $passwd $db1tns $db1name
      minimize_apply_lag $user $passwd $db2tns $db2name $db1tns $db1name $db1ver $MRP_APPLY_LAG $MRP_APPLY_LAG_TIMEOUT
      ;;
 
  3)  # Switch $db2name to the physical standby role
      switch_primary_to_physical $user $passwd $db2tns $db2name
 
      # Restart only if we've just switched
      if [ "$?" -eq "0" ]; then
        shutdown_database $user $passwd $db2tns $db2name
        mount_database $user $passwd $db2tns $db2name
      fi
      ;;
 
  4)  # Confirm $db1name has witnessed the role change
      wait_standby_eor $user $passwd $db1tns $db1name $db1ver
      ;;
 
  5)  # Switch $db1name to the primary role
      switch_physical_to_primary $user $passwd $db1tns $db1name
 
      # Open only if we've just switched
      if [ "$?" -eq "0" ]; then
        open_database $user $passwd $db1tns $db1name
      fi
      ;;
 
  6)  # Start media recovery
      # N.B.: We omit the 'through next switchover' option because it's possible for the MRP 
      #       to reprocess the EOR log file 
      start_media_recovery $user $passwd $db2tns $db2name "using current logfile disconnect"
 
      # Display a relevant message regarding how to startup the remaining RAC instances on db1
      is_rac_database $user $passwd $db1tns $db1name
      if [ "$?" -eq "1" ]; then
        if [ "$db1ver" = "11.1" ]; then
          # db1 was reduced to a single instance
          display_rac_promote $user $passwd $db1tns $db1name $db1iname "primary11.1"
        else
          # db1 has one open instance while peer instances are still mounted
          display_rac_promote $user $passwd $db1tns $db1name $db1iname "primary"
        fi
      fi
 
      # Display a relevant message regarding how to startup the remaining RAC instances on db2
      is_rac_database $user $passwd $db2tns $db2name
      if [ "$?" -eq "1" ]; then
        # db2 was reduced to a single instance
        display_rac_promote $user $passwd $db2tns $db2name $db2iname "physical"
      fi      
      ;;
 
  7)  # This task is a place-holder task which indicates switchover completion.  It is 
      # necessary since this particular checkpoint also represents the end of the script.
      ;;
 
  *)  break;;
  esac  
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
done
 
} # S7_switchback
 
###############################################################################
# NAME:        S8_statistics
#
# DESCRIPTION: 
#   In this stage, this script produces statistics regarding the upgrade.  
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db2tns $db2name $task
#
# RETURN:
#   None
#
###############################################################################
S8_statistics()
{
display_stage "Statistics"
 
# 
# Step through each task
#
while [ "$task" ]
do 
  case "$task" in
  1)  # Display physical rolling upgrade statistics
      display_stats $user $passwd $db2tns $db2name
      ;;  
 
  *)  break;;
  esac
 
  # Checkpoint our completion of the current task
  checkpoint
 
  # Proceed to next task
  task=`expr $task + 1`
done
 
} # S8_statistics
 
###############################################################################
# NAME:        S9_cleanup
#
# DESCRIPTION: 
#   In this stage, the script purges both databases of flashback restore points
#   that it created to maintain its execution state.
#
# INPUT(S):
#   Arguments: 
#     None
#
#   Globals:   $user $passwd $db1tns $db2tns $db1name $db2name $task
#
# RETURN:
#   None
#
###############################################################################
S9_cleanup()
{
# For now just suppress all output related to cleanup
suppress=1
display_stage "Cleanup"
 
# 
# Step through each task
#
while [ "$task" ]
do 
  case "$task" in
  1)  # With checkpointing no longer necessary, ensure MRP is left running
      is_database_role $user $passwd $db2tns $db2name "PHYSICAL STANDBY"
      if [ "$?" -eq "1" ]; then
        start_media_recovery $user $passwd $db2tns $db2name "using current logfile disconnect"
      fi
      ;;
 
  2)  # Purge restore points from test script
      purge_resume_state $user $passwd $db1tns $db1name
      purge_resume_state $user $passwd $db2tns $db2name
 
      # Delete temp files
      rm $LOG_SQL_EXETMP > /dev/null
      ;;
 
  *)  break;;
  esac  
 
  # No checkpoint since we've purged all restore points
 
  # Proceed to next task
  task=`expr $task + 1`
done
 
suppress=0
} # S9_cleanup
 
 
###############################################################################
############    UTILITY ROUTINES    ###########################################
###############################################################################
 
###############################################################################
# NAME:        checkpoint
#
# DESCRIPTION: 
#   Create a restore point to indicate a given task has been completed.  The 
#   majority of restore points are created on db2 with the exception of the 
#   restore point needed by the original primary to flashback before its 
#   conversion into a physical standby.  Note that in order to take a
#   checkpoint media recovery must not be running.
#
# INPUT(S):
#   Arguments:
#     None
#
#   Globals:
#     $user $passwd $db1tns $db2tns $db2name $stage $task $RPFX
#
# RETURN:
#   None
# 
###############################################################################
checkpoint()
{
stagestr=`printf %02d $stage`
taskstr=`printf %02d $task`
l_checkpoint_rpname="${RPFX}_${stagestr}${taskstr}" 
 
suppress=1
stop_media_recovery $user $passwd $db2tns $db2name
create_restore_point $user $passwd $db2tns $db2name $l_checkpoint_rpname
suppress=0
}
 
###############################################################################
# NAME:        chkerr
#
# DESCRIPTION: 
#   Check if $1 is non-zero, and if so, output the message supplied in $2 and
#   exit immediately.
# 
# INPUT(S):
#   Arguments:
#     $1: status code
#     $2: error message
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
chkerr()
{
if [ "$1" -ne "0" ]; then
  fail "$2"
  exit $1
fi
}
 
###############################################################################
# NAME:        convert_to_physical
#
# DESCRIPTION: 
#   Convert the flashed back database (currently only for the former primary) 
#   into a physical standby.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
###############################################################################
convert_to_physical()
{
display "converting $4 into physical standby"
# Convert the flashed back logical standby to a physical standby
l_ctp_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter database convert to physical standby; 
exit;"
sql_exec $1 $2 $3 "$l_ctp_sql"
chkerr $? "faied to convert to physical standby"
}
 
###############################################################################
# NAME:        create_backup_ctlfile
#
# DESCRIPTION: 
#   Create a backup control file for the specified database.  If one already
#   exists, it is deleted before the backup is created.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: name of backup control file
#
#   Globals:
#     $LOC_BACKUP_FILES
#
# RETURN:
#   None
#
###############################################################################
create_backup_ctlfile()
{
display "backing up current control file on $4"
 
# Delete the old backup controlfile to avoid an ORA-27038
if [ -f "${LOC_BACKUP_FILES}${5}" ]; then
  rm ${LOC_BACKUP_FILES}${5}
fi
 
# Create a new backup control file
l_cbc_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter database backup controlfile to '${LOC_BACKUP_FILES}${5}'; 
exit;"
sql_exec $1 $2 $3 "$l_cbc_sql"
chkerr $? "failed to backup control file on database $4"
display "created backup control file ${LOC_BACKUP_FILES}${5}"
}
 
###############################################################################
# NAME:        create_restore_point
#
# DESCRIPTION: 
#   Create a named, guaranteed flashback database restore point
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: restore point name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
create_restore_point()
{
display "creating restore point $5 on database $4"
l_crp_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
create restore point $5 guarantee flashback database; 
exit;"
sql_exec $1 $2 $3 "$l_crp_sql" 
chkerr $? "failed to create restore point"
}
 
###############################################################################
# NAME:        display
#
# DESCRIPTION: 
#   Display message to console and logfile, if enabled.  The suppress global,
#   if set, will suppress all output.  The $LOG_PHYSRU_ENABLED global controls
#   whether the message is also written to the log file.
#
# INPUT(S):
#   Arguments:
#     $1: message to display
#
#   Globals:
#     $stage $task $suppress $suppresst $suppressl $LOG_PHYSRU_ENABLED $LOG_PHYSRU_FILE
#
# RETURN:
#   None
#
###############################################################################
display()
{
if [ "$suppress" -eq "1" ]; then
  return 0
else
  ts=`date "+%b %d %R:%S %Y"`
  if [ "$suppresst" -eq "0" ]; then
    echo -e "$ts [$stage-$task] $1"
  fi
  if [ "$suppressl" -eq "0" ]; then  
    if [ "$LOG_PHYSRU_ENABLED" -eq "1" ]; then
      echo -e "$ts [$stage-$task] $1" >> $LOG_PHYSRU_FILE
    fi
  fi
  return 0
fi
}
 
###############################################################################
# NAME:        display_banner
#
# DESCRIPTION: 
#   Displays required Oracle banner
#
# INPUT(S):
#   Arguments:
#     None
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
display_banner()
{
echo ""
}
 
###############################################################################
# NAME:        display_rac_demote
#
# DESCRIPTION: 
#   Displays instructions to the user about reducing the RAC.  $6 identifies one
#   of four possible states where it may be necessary to reduce the RAC.  These 
#   states are 1) instantiation of the transient logical 2) primary to physical
#   standby conversion 3) primary to physical standby switchover, and 4) physical
#   to primary switchover.  
#
#   This routine returns a 1 to indicate instructions were displayed, and 0
#   otherwise.
#
#   This routine assumes that the caller has already verified the specified 
#   database is a RAC.  
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: instance name
#     $6: context - "instantiation", "convert", "primary", "physical"
#
#   Globals:
#     None
#
# RETURN:
#   0: Instructions were not displayed
#   1: Instructions were displayed
#
###############################################################################
display_rac_demote()
{
# Variable for instruction number
i=0
 
# Variable for return value
l_drd_val=0
 
# Create a list of RAC instance peers that need to be shutdown
get_instance_peers $1 $2 $3 $4 $5
l_drd_instcnt=$l_gip_val
l_drd_instlst="$l_gip_lst"
 
# Instantiation requires RAC to be disabled
if [ "$6" = "instantiation" ]; then
 
  # Display instructions to user
  display_raw "\nWARN: $4 is a RAC database.  Before this script can continue, you"
  display_raw "      must manually reduce the RAC to a single instance, disable the RAC, and"
  display_raw "      restart instance $5 in mounted mode.  This can be accomplished "
  display_raw "      with the following steps:"
 
  # Only indicate shutdown if one or more peer instances is active
  if [ "$l_drd_instcnt" -gt "0" ]; then
    i=`expr $i + 1`
    display_raw "\n        $i) Shutdown all instances other than instance $5."
    display_raw "           eg: srvctl stop instance -d $4 -i $l_drd_instlst -o abort"
  fi
 
  # cluster_database needs to be set to FALSE
  i=`expr $i + 1`
  display_raw "\n        $i) On instance $5, set the cluster_database parameter to FALSE."
  display_raw "           eg: SQL> alter system set cluster_database=false scope=spfile;"
  i=`expr $i + 1`
  display_raw "\n        $i) Shutdown instance $5."
  display_raw "           eg: SQL> shutdown abort;"
 
  # Restart in target mode
  i=`expr $i + 1`
  display_raw "\n        $i) Startup instance $5 in mounted mode."
  display_raw "           eg: SQL> startup mount;\n"
  l_drd_val=1
 
# Conversion to a physical standby requires the RAC to be reduced to a single instance
else if [ "$6" = "convert" ]; then
 
  # Only indicate shutdown message if one or more peer instances are active
  if [ "$l_drd_instcnt" -gt "0" ]; then
 
    # Display instructions to user
    display_raw "\nWARN: $4 is a RAC database.  Before this script can continue, you"
    display_raw "      must manually reduce the RAC to a single instance.  This can be "
    display_raw "      accomplished with the following step:"
 
    i=`expr $i + 1`
    display_raw "\n        $i) Shutdown all instances other than instance $5."
    display_raw "           eg: srvctl stop instance -d $4 -i $l_drd_instlst -o abort\n"
 
    l_drd_val=1
  fi
 
# A switchover to physical requires the primary peer instances to be shutdown 
# during the switchover.  A switchover to primary requires the standby peer 
# instances to be shutdown only on Oracle version 11.1.  In 11.2 and higher, the 
# instances may remain active.
else if [ "$6" = "primary" ] || [ "$6" = "physical" ]; then
 
  # Display instructions only if peer instances are active
  if [ "$l_drd_instcnt" -gt "0" ]; then
    i=`expr $i + 1`
    display_raw "\nWARN: $4 is a RAC database.  Before this script can continue, you "
    display_raw "      must manually reduce the RAC to a single instance.  This can be "
    display_raw "      accomplished with the following step:"
    display_raw "\n        $i) Shutdown all instances other than instance $5."
    if [ "$6" = "primary" ]; then 
      display_raw "           eg: srvctl stop instance -d $4 -i $l_drd_instlst\n"
    else
      if [ "$6" = "physical" ]; then 
        display_raw "           eg: srvctl stop instance -d $4 -i $l_drd_instlst -o abort\n"
      fi
    fi
    l_drd_val=1
  fi
fi
fi
fi
 
return $l_drd_val
}
 
###############################################################################
# NAME:        display_rac_promote
#
# DESCRIPTION: 
#   Displays a message to the user about upgrading the single instance to a RAC.
#   The $6 argument identifies the context from which this routine is called. 
#   
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: instance name
#     $6: context - "db2upgrade", "db1upgrade", "primary11.1", "primary", "physical" 
#     $7: hint (optional) - "rac", "unknown"
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
display_rac_promote()
{
#
# $6: db2upgrade
#
# In this state, db2 has completed the physical to transient logical setup and
# is now ready to be upgraded.  If db2 was a RAC we forced the user to disable 
# it for the instantiation.
#
if [ "$6" = "db2upgrade" ]; then
 
  # N.B.: $7 is a hint which allows us to present a more user-friendly message.  If the 
  #       script was uninterrupted, we'll know the user disabled RAC, and will want to
  #       re-enable it for the upgrade.  If the script was interrupted, we won't have any
  #       way of knowing, and will present a generic suggestion to enable the RAC.
  if [ "$7" = "unknown" ]; then
    display_raw "\nNOTE: If $4 was previously a RAC database that was disabled, it may be"
    display_raw "      reverted back to a RAC database upon completion of the rdbms upgrade."
    display_raw "      This can be accomplished by performing the following steps:\n"
  else if [ "$7" = "rac" ]; then
    display_raw "\nNOTE: Database $4 may be reverted back to a RAC database upon completion"
    display_raw "      of the rdbms upgrade.  This can be accomplished by performing the "
    display_raw "      following steps:\n"
  fi
  fi
 
  display_raw "          1) On instance $5, set the cluster_database parameter to TRUE."
  display_raw "          eg: SQL> alter system set cluster_database=true scope=spfile;\n"
 
  display_raw "          2) Shutdown instance $5."
  display_raw "          eg: SQL> shutdown abort;\n"
 
  display_raw "          3) Startup and open all instances for database $4."
  display_raw "          eg: srvctl start database -d $4\n"
 
#
# $6: db1upgrade
#
# In this state, db1 has completed the flashback and conversion to physical standby,
# and has been shutdown in preparation for startup on the newer version binary.
#
else if [ "$6" = "db1upgrade" ]; then
  display_raw "\nNOTE: Database $4 is no longer limited to single instance operation since"
  display_raw "      the database has been successfully converted into a physical standby."
  display_raw "      For increased availability, Oracle recommends starting all instances in"
  display_raw "      the RAC on the newer binary by performing the following step:\n"
 
  display_raw "        1) Startup and mount all instances for database $4"
  display_raw "        eg: srvctl start database -d $4 -o mount\n"
 
#
# $6: primary11.1
#
# In this state, db1 has switched back to the primary role from a physical standby on a
# target version of 11.1.X.  In 11.1, the switchover required the RAC standby to be reduced
# to a single instance. As a result, all that is needed is the startup of the inactive 
# instances.
#
else if [ "$6" = "primary11.1" ]; then
  display_raw "\nNOTE: Database $4 is no longer limited to single instance operation since"
  display_raw "      it has completed the switchover to the primary role.  For increased "
  display_raw "      availability, Oracle recommends starting the inactive instances in "
  display_raw "      the RAC by performing the following step:\n"
 
  display_raw "        1) Startup and open inactive instances for database $4"
  display_raw "        eg: srvctl start database -d $4"
 
#
# $6: primary
#
# In this state, db1 has switched back to the primary role from a physical standby on a
# target version of 11.2 or higher.  Starting in 11.2, a RAC physical standby does not
# need to be reduced to a single instance in order to switchover.  One side effect of this
# however is that upon switchover, the instance on which the switchover DDL was issued will
# be opened in READ/WRITE mode while the peer instances will remain in mounted mode.  Since
# there is no direct way to open a database from the mounted state using srvctl, we instruct
# the user to 1) shutdown the peer instances and then 2) start the database (which will 
# start only the inactive instances).  
#
else if [ "$6" = "primary" ]; then
  # Variable for instruction number
  i=0
 
  # Get a list of the peer instances
  get_instance_peers $1 $2 $3 $4 $5
  l_drp_instcnt=$l_gip_val
  l_drp_instlst="$l_gip_lst"
 
  # Display a message specific to one or more active peer instances
  if [ "$l_drp_instcnt" -gt "0" ]; then
    display_raw "\nNOTE: Database $4 has completed the switchover to the primary role, but"
    display_raw "      instance $5 is the only open instance.  For increased availability,"
    display_raw "      Oracle recommends opening the remaining active instances which are "
    display_raw "      currently in mounted mode by performing the following steps:\n"
    i=`expr $i + 1`
    display_raw "        $i) Shutdown all instances other than instance $5."
    display_raw "        eg: srvctl stop instance -d $4 -i $l_drp_instlst\n"
  else
    # Display a message specific to no active peer instances
    display_raw "\nNOTE: Database $4 has completed the switchover to the primary role, but"
    display_raw "      instance $5 is the only open instance.  For increased availability,"
    display_raw "      Oracle recommends opening all instances in the RAC by performing the"
    display_raw "      following step:\n"
  fi
 
  i=`expr $i + 1`
  display_raw "        $i) Startup and open all inactive instances for database $4."
  display_raw "        eg: srvctl start database -d $4"
 
#
# $6: physical
#
# In this state, db2 has switched back to a physical standby role from the primary role.
# Unlike the physical to primary switchover, in order to switchover the primary was 
# required to be reduced to a single instance.
#
else if [ "$6" = "physical" ]; then
  display_raw "\nNOTE: Database $4 is no longer limited to single instance operation since"
  display_raw "      it has completed the switchover to the physical standby role.  For "
  display_raw "      increased  availability, Oracle recommends starting the inactive "
  display_raw "      instances in the RAC by performing the following step:\n"
 
  display_raw "        1) Startup and mount inactive instances for database $4"
  display_raw "        eg: srvctl start database -d $4 -o mount"
 
fi
fi
fi
fi
fi
}
 
###############################################################################
# NAME:        display_raw
#
# DESCRIPTION: 
#   Similar to the display routine but without timestamp or stage (just echo)
# 
# INPUT(S):
#   Arguments:
#     $1: message to display
#
#   Globals:
#     $suppress $LOG_PHYSRU_ENABLED $LOG_PHYSRU_FILE
#
# RETURN:
#   None
#
###############################################################################
display_raw()
{
if [ "$suppress" -eq "1" ]; then
  return 0
else
  if [ "$suppresst" -eq "0" ]; then
    echo -e "$1"
  fi
  if [ "$suppressl" -eq "0" ]; then
    if [ "$LOG_PHYSRU_ENABLED" -eq "1" ]; then
      echo -e "$1" >> $LOG_PHYSRU_FILE
    fi
  fi
  return 0
fi
}
 
###############################################################################
# NAME:        display_stage
#
# DESCRIPTION: 
#   Similar to the display routine but unique to displaying the stage which
#   contains a banner.  This routine is unlike the other display routines as
#   it only produces output when task == 1.  This is to avoid multiple messages
#   since a stage routine may be re-entered numerous times if there are 
#   failures during this script.
#
# INPUT(S):
#   Arguments:
#     $1: message describing stage
#
#   Globals:
#     $stage $task $suppress $LOG_PHYSRU_ENABLED $LOG_PHYSRU_FILE
#
# RETURN:
#   None
#
###############################################################################
display_stage()
{
if [ "$suppress" -eq "1" ]; then
  return 0
else
  # Only produce stage header if first time entered
  if [ "$task" -eq "1" ]; then
    if [ "$suppresst" -eq "0" ]; then
      echo -e "\n### Stage $stage: $1"
    fi
    if [ "$suppressl" -eq "0" ]; then
      if [ "$LOG_PHYSRU_ENABLED" -eq "1" ]; then
        echo -e "\n### Stage $stage: $1" >> $LOG_PHYSRU_FILE
      fi
    fi
  fi
fi
}
 
###############################################################################
# NAME:        display_stats
#
# DESCRIPTION: 
#   Displays rolling upgrade statistics upon successful rolling upgrade.  This
#   routine works by performing date/time arithmetic on the flashback times 
#   associated with each of the flashback restore points created by the 
#   checkpoint routine.  This routine is heavily dependent on the assumptions
#   it makes about what event occurs for a given [stage-task].
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name of original physical standby
#     $4: database unique name of original physical standby
#
#   Globals:
#     $RPFX
#
# RETURN:
#   None
#
###############################################################################
display_stats()
{
# 
# script start time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0000_${VERSION}"
l_ds_sst=$l_gft_val
display_raw "script start time:                                           $l_ds_sst"
 
# 
# script finish time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0707"
l_ds_sft=$l_gft_val
display_raw "script finish time:                                          $l_ds_sft"
 
# 
# total script execution time
#
get_interval_time $1 $2 $3 "$l_ds_sft" "$l_ds_sst"
l_ds_set=$l_git_val
display_raw "total script execution time:                                       $l_ds_set"
 
# 
# user upgrade time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0204"
l_ds_uut1=$l_gft_val
get_flashback_time $1 $2 $3 $4 "${RPFX}_0301"
l_ds_uut2=$l_gft_val
get_interval_time $1 $2 $3 "$l_ds_uut2" "$l_ds_uut1"
l_ds_uut=$l_git_val
display_raw "wait time for user upgrade:                                        $l_ds_uut"
 
#
# user startup former primary as physical on new binary wait time
#
 
# 
# active script execution time
#
l_ds_ase_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select cast((to_dsinterval('$l_ds_set') - to_dsinterval('$l_ds_uut')) 
as interval day(2) to second(0)) from dual; 
exit;"
sql_exec $1 $2 $3 "$l_ds_ase_sql"
chkerr $? "failed to calculate active script time"
l_ds_ase=`echo $sql_out`
display_raw "active script execution time:                                      $l_ds_ase"
 
# 
# logical instantiation start time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0201"
l_ds_lis=$l_gft_val
display_raw "transient logical creation start time:                       $l_ds_lis"
 
# 
# logical instantiation finish time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0202"
l_ds_lif=$l_gft_val
display_raw "transient logical creation finish time:                      $l_ds_lif"
 
# 
# primary switchover to logical start time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0401"
l_ds_pls=$l_gft_val
display_raw "primary to logical switchover start time:                    $l_ds_pls"
 
# 
# logical switchover to primary finish time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0404"
l_ds_lpf=$l_gft_val
display_raw "logical to primary switchover finish time:                   $l_ds_lpf"
 
#
# total time primary services were offline
#
get_interval_time $1 $2 $3 "$l_ds_lpf" "$l_ds_pls"
l_ds_pso=$l_git_val
display_raw "primary services offline for:                                      $l_ds_pso"
 
#
# total time former primary was a physical standby
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0504"
l_ds_fpp1=$l_gft_val
get_flashback_time $1 $2 $3 $4 "${RPFX}_0701"
l_ds_fpp2=$l_gft_val
get_interval_time $1 $2 $3 "$l_ds_fpp2" "$l_ds_fpp1"
l_ds_fpp=$l_git_val
display_raw "total time former primary in physical role:                        $l_ds_fpp"
 
#
# time to reach upgrade redo
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0601"
l_ds_bfp1=$l_gft_val
get_flashback_time $1 $2 $3 $4 "${RPFX}_0602"
l_ds_bfp2=$l_gft_val
get_interval_time $1 $2 $3 "$l_ds_bfp2" "$l_ds_bfp1"
l_ds_bfp=$l_git_val
display_raw "time to reach upgrade redo:                                        $l_ds_bfp"
 
#
# time to recover through upgrade redo
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0602"
l_ds_rtu1=$l_gft_val
get_flashback_time $1 $2 $3 $4 "${RPFX}_0603"
l_ds_rtu2=$l_gft_val
get_interval_time $1 $2 $3 "$l_ds_rtu2" "$l_ds_rtu1"
l_ds_rtu=$l_git_val
display_raw "time to recover upgrade redo:                                      $l_ds_rtu"
 
#
# no more stats if the final switchover wasn't performed
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0705"
l_ds_ppf=$l_gft_val
if [ "$l_ds_ppf" = "" ]; then
  return 0
fi
 
# 
# primary switchover to physical start time
#
get_flashback_time $1 $2 $3 $4 "${RPFX}_0701"
l_ds_pps=$l_gft_val
display_raw "primary to physical switchover start time:                   $l_ds_pps"
 
#
# physical switchover to primary finish time
#
display_raw "physical to primary switchover finish time:                  $l_ds_ppf"
 
#
# total time primary services were offline
#
get_interval_time $1 $2 $3 "$l_ds_ppf" "$l_ds_pps"
l_ds_pso2=$l_git_val
display_raw "primary services offline for:                                      $l_ds_pso2"
}
 
###############################################################################
# NAME:        display_usage
#
# DESCRIPTION: 
#   Displays usage description for physru
#
# INPUT(S):
#   Arguments:
#     None
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
display_usage()
{
echo -e "\nUsage: physru <username> <primary_tns> <standby_tns>"
echo -e "              <primary_name> <standby_name> <upgrade_version>"
echo -e "\nPurpose:"
echo -e "  Perform a rolling upgrade between a primary and physical standby database.\n"
echo -e "  This script simplifies a physical standby rolling upgrade.  While numerous"
echo -e "  steps have been automated, this script must be called at least three times "
echo -e "  in order to complete a rolling upgrade.  When this script reaches a point"
echo -e "  where user intervention is required, it outputs a message indicating what"
echo -e "  is expected of the user.  Once the user action is complete, this script can "
echo -e "  be called to resume the rolling upgrade.  In the event of an error, a user"
echo -e "  can take corrective action, and simply call this script again to resume the"
echo -e "  rolling upgrade.  In the event one wishes to abandon the rolling upgrade, and"
echo -e "  revert the configuration back to its pre-upgrade state, this script creates "
echo -e "  guaranteed flashback database restore points on both the primary and standby"
echo -e "  databases, and backs up each databases' associated control file.  The names "
echo -e "  of the restore points and backup control files are output to the console and"
echo -e "  logfile when they are initially created.\n"
echo -e "  When this script is called, it assumes all databases to be either mounted or "
echo -e "  open.  It requires flashback database to be enabled on both the primary and "
echo -e "  standby instances.  RAC configurations are permitted but there is limited "
echo -e "  automation provided by the script.  At specific points it may become "
echo -e "  necessary to manually shutdown/startup instances and change init.ora "
echo -e "  parameter values.  When appropriate, the script will output when these "
echo -e "  requirements are expected of the user.  RAC configurations are also required"
echo -e "  to define static tns services since this script expects a given tns service"
echo -e "  name to contact the same instance on successive calls."
echo -e "\nArguments:"
echo -e "  <username>        = dba username"
echo -e "  <primary_tns>     = tns service name to primary"
echo -e "  <standby_tns>     = tns service name to physical standby"
echo -e "  <primary_name>    = db_unique_name of primary"
echo -e "  <standby_name>    = db_unique_name of standby"
echo -e "  <upgrade_version> = target rdbms version"
echo -e "\nExample:"
echo -e "  physru sys hq_tnspri hq_tnsstb hq_primary hq_standby 11.2.0.2.0"
echo -e "\n  NOTE: This script performs role transitions, and it is not necessary to "
echo -e "        adjust the tns and db name arguments to their respective database roles"
echo -e "        on successive calls.  That is, the arguments must remain the same from"
echo -e "        first-invocation to completion."
}
 
###############################################################################
# NAME:        sql_comp
#
# DESCRIPTION: Uses SQL to compare $4 and $6 given operator in $5.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: num1
#     $5: operator = { < > <= >= != }
#     $6: num2
#
#   Globals:
#     None
#
# RETURN:
#   l_comp_val: 0 if ($4 $5 $6) expression is true and 1 otherwise
#
###############################################################################
sql_comp() 
{
l_comp_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select case when (${4} ${5} ${6}) then 0 else 1 end from dual;
exit;"
sql_exec $1 $2 $3 "$l_comp_sql"
l_comp_val=`echo $sql_out | awk '{print int(\$1)}'`
return $l_comp_val
}
 
###############################################################################
# NAME:        sql_gt/sql_lt/sql_eq
#
# DESCRIPTION: Uses sql_comp to perform the associated (gt == greater than,
#              lt == less than, eq == equals) comparison between $4 and $5.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: num1
#     $5: num2
#
#   Globals:
#     None
#
# RETURN:
#   l_gt/lt/eq_val: 0 if true and 1 otherwise
#
###############################################################################
sql_gt() 
{
sql_comp $1 $2 $3 $4 ">" $5
l_gt_val=$?
return $l_gt_val
}
 
sql_lt()
{
sql_comp $1 $2 $3 $4 "<" $5
l_lt_val=$?
return $l_lt_val
}
 
sql_eq()
{
sql_comp $1 $2 $3 $4 "=" $5
l_eq_val=$?
return $l_eq_val
}
 
###############################################################################
# NAME:        sql_eval
#
# DESCRIPTION: 
#   Evaluate an integer expression via SQL.  At various times this script needs to
#   perform arithmetic on SCNs which are larger than 32bits.  For this reason, the
#   script passes expression to the database to perform the arithmetic.  The 
#   resultant value is returned as a string.  The results are always truncated.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: expression 
#
#   Globals:
#     None
#
# RETURN:
#   l_se_val:  result as a string
#
###############################################################################
sql_eval() {
l_se_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select trunc(${5}) from dual;
exit;"
sql_exec $1 $2 $3 "$l_se_sql"
chkerr $? "failed to evaluate expression on database $4"
l_se_val=`echo $sql_out | awk '{print \$1}'`
}
 
 
###############################################################################
# NAME:        fail
#
# DESCRIPTION: 
#   Display $1 as error message and exit with error status
#
# INPUT(S):
#   Arguments:
#     $1: error message
#
#   Globals:
#     None
# RETURN:
#   None
#
###############################################################################
fail()
{
  errormsg="ERROR: $1"
  display "$errormsg"
  exit 1
}
 
###############################################################################
# NAME:        flashback_database
#
# DESCRIPTION: 
#   Flashback database to named restore point $5
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: restore point name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
flashback_database()
{
display "flashing back database $4 to restore point $5"
l_fd_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
flashback database to restore point $5; 
exit;"
sql_exec $1 $2 $3 "$l_fd_sql"
chkerr $? "failed to flashback to restore point $5 on database $4"
}
 
###############################################################################
# NAME:        get_applied_scn
#
# DESCRIPTION: 
#   Get the last applied scn of the LSP.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_gas_val: scn of last applied redo as string
#
###############################################################################
get_applied_scn() {
l_gas_sql="  set pagesize 0 feedback off verify off heading off echo off tab off numwidth 20 
  whenever sqlerror exit sql.sqlcode 
  select nvl(applied_scn, 0) from v\$logstdby_progress;
  exit;"
sql_exec $1 $2 $3 "$l_gas_sql"
chkerr $? "failed to query last applied scn by logical standby on database $4"
l_gas_val=`echo $sql_out | awk '{print \$1}'`
}
 
###############################################################################
# NAME:        get_db_unique_name
#
# DESCRIPTION: 
#   Gets the db_unique_name for the database.  If one exists, the name is returned
#   in l_dun_val, and 0 is returned from this routine.  A 0 is returned if a 
#   db_unique_name does not exist.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name (passed to script)
#
#   Globals:
#     None
#
# RETURN:
#   0: db_unique_name was found, l_dun_val: db_unique_name
#   1: db_unique_name not found
#
###############################################################################
get_db_unique_name() {
l_dun2_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(db_unique_name) from v\$database; 
exit;" 
sql_exec $1 $2 $3 "$l_dun2_sql"
chkerr $? "failed to query db_unique_name from database $4"
l_dun2_val=`echo $sql_out`
if [ "$l_dun2_val" -eq "0" ]; then
  return 1
else
  l_dun_sql="  set pagesize 0 feedback off verify off heading off echo off tab off
  whenever sqlerror exit sql.sqlcode
  select db_unique_name from v\$database;
  exit;"
  sql_exec $1 $2 $3 "$l_dun_sql"
  chkerr $? "failed to query db_unique_name from database $4"
  l_dun_val=`echo $sql_out`
  return 0
fi
}
 
###############################################################################
# NAME:        get_primary_scn
#
# DESCRIPTION: 
#   Get the current scn on the specified database.  The current scn is the 
#   value obtained from v$database.current_scn.  This procedure is currently 
#   used to obtain a starting point on the primary from which to measure the 
#   time until the redo is recovered or applied.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_gps_val:  scn as string
#
###############################################################################
get_primary_scn() {
l_gps_sql="  set pagesize 0 feedback off verify off heading off echo off tab off numwidth 20 
whenever sqlerror exit sql.sqlcode 
select nvl(current_scn,0) from v\$database;
exit;"
sql_exec $1 $2 $3 "$l_gps_sql"
chkerr $? "failed to query current scn on database $4"
l_gps_val=`echo $sql_out | awk '{print \$1}'`
}
 
###############################################################################
# NAME:        get_flashback_restore_count
# DESCRIPTION: 
#   Get the total number of flashback restore point entries on the specified
#   database.  The maximum number Oracle supports is 2048 so we need to ensure
#   there are enough free entries for this script's use.  
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_gfr_val: no. of existing restore point entries
#
###############################################################################
get_flashback_restore_count() {
l_gfr_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(1) from v\$restore_point; 
exit;"
sql_exec $1 $2 $3 "$l_gfr_sql"
chkerr $? "failed to query total number of flashback restore points on database $4" 
l_gfr_val=`echo $sql_out`
}
 
###############################################################################
# NAME:        get_flashback_scn
#
# DESCRIPTION: 
#   Get the scn associated with a flashback restore point.
#
# INPUT(S):
#   $1: database user
#   $2: user password
#   $3: tns service name
#   $4: database unique name
#   $5: restore point name
#
# RETURN:
#   l_gfs_val: scn value associated with restore point $5
#
###############################################################################
get_flashback_scn() {
l_gfs_sql="set pagesize 0 feedback off verify off heading off echo off tab off numwidth 20 
whenever sqlerror exit sql.sqlcode 
select nvl(max(scn),0) from v\$restore_point where name = '$5'; 
exit;"
sql_exec $1 $2 $3 "$l_gfs_sql"
chkerr $? "failed to query flashback scn of restore point $5 on database $4"
l_gfs_val=`echo $sql_out | awk '{print \$1}'`
}
 
###############################################################################
# NAME:        get_flashback_time
#
# DESCRIPTION: 
#  Get the time associated with a flashback restore point.
#
# INPUT(S):
#   $1: database user
#   $2: user password
#   $3: tns service name
#   $4: database unique name
#   $5: restore point name
#
# RETURN:
#   l_gft_val: time associated with restore point $5
#
###############################################################################
get_flashback_time() {
l_gft_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select to_char(time, 'DD-Mon-RR HH24:MI:SS') from v\$restore_point where name = '$5'; 
exit;"
sql_exec $1 $2 $3 "$l_gft_sql"
chkerr $? "failed to query flashback time of restore point $5 on database $4"
l_gft_val=`echo $sql_out`
}
 
###############################################################################
# NAME:        get_instance_id
#
# DESCRIPTION: 
#   Gets the instance id from the instance specified at $3.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_gii_val: instance_number from v$instance
#
###############################################################################
get_instance_id()
{
l_gii_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select instance_number from v\$instance;
exit;" 
sql_exec $1 $2 $3 "$l_gii_sql"
chkerr $? "failed to query instance_number from database $4"
l_gii_val=`echo $sql_out`
}
 
###############################################################################
# NAME:        get_instance_name
#
# DESCRIPTION: 
#   Gets the instance name from the specified database.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_gin_val: instance_name from v$instance
#
###############################################################################
get_instance_name()
{
l_gin_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select instance_name from v\$instance;
exit;" 
sql_exec $1 $2 $3 "$l_gin_sql"
chkerr $? "failed to query instance_name from database $4"
l_gin_val=`echo $sql_out`
}
 
###############################################################################
# NAME:        get_instance_peers
#
# DESCRIPTION: 
#   Gets a comma delimited list of instances that are peers with instance $5 
#   which is supplied to this routine.  This routine assumes that the specified 
#   database has already been verified to be a RAC.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: instance name
#
#   Globals:
#     None
#
# RETURN:
#   l_gip_val: # of instance peers in $l_gip_lst
#   l_gip_lst: comma delimited list of instances that are peers with $5
#
#select instance_name from gv\$instance where instance_name != '$5';
###############################################################################
get_instance_peers()
{
l_gip_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select instance_name from gv\$instance where instance_name != '$5' 
order by instance_number;
exit;" 
sql_exec $1 $2 $3 "$l_gip_sql"
chkerr $? "failed to query active instances for database $4"
l_gip_lst=`echo $sql_out | tr '\n' ' '`
l_gip_lst=`echo $l_gip_lst`
l_gip_val=`echo $l_gip_lst | wc -w`
l_gip_lst=`echo $l_gip_lst | tr ' ' ','`
}
 
###############################################################################
# NAME:        get_interval_time
#
# DESCRIPTION: 
#   Return $4 - $5 as a day to second interval
#
# INPUT(S):
#   $1: database user
#   $2: user password
#   $3: tns service name
#   $4: timestamp 1
#   $5: timestamp 2
#
# RETURN:
#   l_git_val: difference between $4 and $5
#
###############################################################################
get_interval_time() 
{
l_git_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select cast((to_timestamp('$4', 'DD-Mon-RR HH24:MI:SS') 
- to_timestamp('$5', 'DD-Mon-RR HH24:MI:SS')) 
as interval day(2) to second(0)) from dual;
exit;"
sql_exec $1 $2 $3 "$l_git_sql"
chkerr $? "failed to calculate interval between $4 and $5"
l_git_val=`echo $sql_out`
}
 
###############################################################################
# NAME:        get_protection_mode
#
# DESCRIPTION: 
#   Get database protection mode for the specified database.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_gpm_val: value of protection_mode from v$database
#
###############################################################################
get_protection_mode() {
l_gpm_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select protection_mode from v\$database; 
exit;"
sql_exec $1 $2 $3 "$l_gpm_sql"
chkerr $? "failed to query database protection mode on database $4"
l_gpm_val=$sql_out
}
 
###############################################################################
# NAME:        get_rdbms_version
#
# DESCRIPTION: 
#   Get the software version of the Oracle RDBMS
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   l_grv_val: version from v$instance
# 
###############################################################################
get_rdbms_version() {
l_grv_sql="set pagesize 0 feedback off verify off heading off echo off tab off
whenever sqlerror exit sql.sqlcode 
select version from v\$instance; 
exit;"
sql_exec $1 $2 $3 "$l_grv_sql"
chkerr $? "failed to query rdbms version on database $4"
l_grv_val=`echo $sql_out`
}
 
###############################################################################
# NAME:        get_recovery_scn
#
# DESCRIPTION: 
#   Get the last applied scn of the MRP.  If the v$recovery_progress view has
#   not been initialized, the scn is returned as 0.  This query varies 
#   depending on database version.  In 11.1, the scn is displayed in the SOFAR
#   column while in 11.2, the scn must be parsed out of the COMMENTS column.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: database version
#
#   Globals:
#     None
#
# RETURN:
#   l_grs_val: scn of last applied redo as a string
#
###############################################################################
get_recovery_scn() {
 
if [ "$5" = "11.1" ]; then
  l_grs_sql="  set pagesize 0 feedback off verify off heading off echo off tab off numwidth 20
  whenever sqlerror exit sql.sqlcode 
  select to_number(nvl(max(sofar),0)) from v\$recovery_progress 
  where item = 'Last Applied Redo' and timestamp = (select max(timestamp) from 
  v\$recovery_progress where item = 'Last Applied Redo'); 
  exit;"
else
  l_grs_sql="  set pagesize 0 feedback off verify off heading off echo off tab off numwidth 20
  whenever sqlerror exit sql.sqlcode 
  select to_number(nvl(substr(max(comments), 6), 0)) from v\$recovery_progress 
  where item = 'Last Applied Redo' and timestamp = (select max(timestamp) from 
  v\$recovery_progress where item = 'Last Applied Redo'); 
  exit;"
fi
 
sql_exec $1 $2 $3 "$l_grs_sql"
chkerr $? "failed to query last applied scn by media recovery on database $4"
 
l_grs_val=`echo $sql_out | awk '{print $1}'`
if [ "$PHYSRU_DEBUG" = "1" ]; then
  display "DEBUG: get_recovery_scn() - aft ($l_grs_val) bef ($sql_out)"
fi
}
 
###############################################################################
# NAME:        get_resume_state
#
# DESCRIPTION: 
#   Get the stage and task values which identifies the last completed operation.
#   These values are parsed from the restore point name composed in the 
#   checkpoint routine.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     $RPFX
#
# RETURN:
#   l_grs2_stage: stage # from most recent checkpoint restore point
#   l_grs2_task:  task  # from most recent checkpoint restore point
# 
###############################################################################
get_resume_state() {
l_grs2_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select nvl(substr(max(name), length('${RPFX}')+2, length('0000')), '0000') 
from v\$restore_point where time = (select max(time) from v\$restore_point 
where name like '${RPFX}_%'); 
exit;"
sql_exec $1 $2 $3 "$l_grs2_sql"
chkerr $? "failed to query script resume state on datbase $4"
l_grs2_stage=`echo $sql_out | cut -b 1-2`
l_grs2_task=`echo $sql_out | cut -b 3-4`
}
 
###############################################################################
# NAME:        get_resume_version
#
# DESCRIPTION: 
#   Get the script version # from the oldest script-related restore point on
#   the specified database.  This query assumes we adhere to a naming 
#   convention for the initial restore point which is as follows:
# 
#     <restore-point-prefix>_0000_<4-digit version#> (eg: PRU_0000_0001)
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     $RPFX
#
# RETURN:
#   l_grv_val: version string from the oldest checkpoint restore point
# 
###############################################################################
get_resume_version() {
l_grv_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select nvl(substr(max(name), -4), '0000') from v\$restore_point where 
name like '${RPFX}_0000_%';
exit;"
sql_exec $1 $2 $3 "$l_grv_sql"
chkerr $? "failed to query script resume version on database $4"
l_grv_val=$sql_out
}
 
###############################################################################
# NAME:        inthandler
#
# DESCRIPTION: 
#   This routine traps SIGINTs during the script.  The only current use for
#   the handler is to restore the tty back to its original state.
#
# INPUT(S):
#   Arguments:
#     None
#
#   Globals:
#     $orig_stty
#
# RETURN:
#   None
# 
###############################################################################
inthandler()
{
stty $origstty
exit 1
}
 
###############################################################################
# NAME:        is_database_role
#
# DESCRIPTION: 
#   Check if the database_role in v$database for the specified database
#   matches $5.  If it matches, a 1 is returned, and 0 otherwise.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: database role string (eg: PRIMARY, LOGICAL STANDBY, PHYSICAL STANDBY)
#
#   Globals:
#     None
#
# RETURN:
#    0: database role does not match
#    1: database role matches
#
###############################################################################
is_database_role() {
l_idr_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(1) from v\$database where database_role='$5'; 
exit;"
sql_exec $1 $2 $3 "$l_idr_sql"
chkerr $? "failed to verify database role of $4 is $5"
l_idr_val=`echo $sql_out`
return $l_idr_val
}
 
###############################################################################
# NAME:        is_dg_enabled
#
# DESCRIPTION: 
#   Check if the specified database has an enabled DG Broker.  This routine 
#   returns a 1 if the check succeeds, and 0 otherwise.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
# RETURN:
#    0: DG Broker is disabled
#    1: DG Broker is enabled
#
###############################################################################
is_dg_enabled() {
l_ide_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(1) from v\$parameter where name='dg_broker_start' and value='TRUE'; 
exit;"
sql_exec $1 $2 $3 "$l_ide_sql"
chkerr $? "failed to query DG_BROKER_START parameter on database $4"
l_ide_val=`echo $sql_out`
return $l_ide_val
}
 
###############################################################################
# NAME:        is_flashback_enabled
#
# DESCRIPTION: 
#   Check if flashback database is enabled on the specified database.  If
#   flashback is enabled, a 1 is returned, and 0 otherwise.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   0: flashback is not enabled
#   1: flashback is enabled
#
###############################################################################
is_flashback_enabled() {
l_ife_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(1) from v\$database where flashback_on = 'YES' or 
flashback_on = 'RESTORE POINT ONLY'; 
exit;"
sql_exec $1 $2 $3 "$l_ife_sql"
chkerr $? "failed to query flashback-enable state on database $4"
l_ife_val=`echo $sql_out`
return $l_ife_val
}
 
###############################################################################
# NAME:        is_lsp_running
#
# DESCRIPTION: 
#   Check if logical standby apply is running.  If running, the instance id is
#   on which it is running is returned.  If not running, a 0 is returned.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   0: lsp0 is not running on any instance
#   N: the instance id on which the lsp0 is running
#
###############################################################################
is_lsp_running() {
l_ilr_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(1) from v\$logstdby_state where state != 'SQL APPLY NOT ON'; 
exit;"
sql_exec $1 $2 $3 "$l_ilr_sql"
chkerr $? "failed to query logical standby apply run state on database $4"
l_ilr_val=`echo $sql_out`
return $l_ilr_val
}
 
###############################################################################
# NAME:        is_mrp_running
#
# DESCRIPTION: 
#   Check if media recovery is running on any instance in the specified 
#   database.  If running, the instance id on which it is running is returned.
#   If not running, a 0 is returned.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   0: mrp is not running on any instance
#   N: the instance id on which the mrp is running
#
###############################################################################
is_mrp_running() {
l_imr_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select nvl(max(inst_id),0) from gv\$managed_standby where process='MRP0'; 
exit;" 
sql_exec $1 $2 $3 "$l_imr_sql" 
chkerr $? "failed to query media recovery run state on database $4" 
l_imr_val=`echo $sql_out`
return $l_imr_val
}
 
###############################################################################
# NAME:        is_rac_database
#
# DESCRIPTION: 
#   Check if the specified instance is a RAC database.  This routine returns a 1 
#   if the instance is in a RAC, and 0 otherwise.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
# RETURN:
#    0: instance is in a RAC
#    1: instance is not in a RAC
#
###############################################################################
is_rac_database() {
l_ird_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
select count(1) from v\$instance where PARALLEL='YES'; 
exit;"
sql_exec $1 $2 $3 "$l_ird_sql"
chkerr $? "failed to verify if database $4 is a RAC"
l_ird_val=`echo $sql_out`
return $l_ird_val
}
 
###############################################################################
# NAME:        is_open_mode
#
# DESCRIPTION: 
#   Check if the specified database is in an open mode matching $5.  Typically
#   the open_mode in v$database is compared against $5.  The exception to this
#   is 'OPEN MIGRATE' which is displayed in v$instance.  If the open mode
#   matches, a 1 is returned, and 0 otherwise.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: open mode (eg: OPEN, MOUNTED, OPEN MIGRATE)
#
# RETURN:
#    0: open mode does not match
#    1: open mode matches
#
###############################################################################
is_open_mode() {
if [ "$5" = "OPEN MIGRATE" ]; then
  l_iom_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  select count(1) from v\$instance where status = '$5'; 
  exit;"
else
  l_iom_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  select count(1) from v\$database where open_mode='$5'; 
  exit;"
fi
sql_exec $1 $2 $3 "$l_iom_sql"
chkerr $? "failed to verify database $4 is in $5 mode"
l_iom_val=`echo $sql_out`
return $l_iom_val
}
 
###############################################################################
# NAME:        mount_database
#
# DESCRIPTION: 
#   Startup and mount the specified database.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
mount_database()
{
display "mounting database $4"
l_md_sql=`sqlplus -L -s $1/$2@$3 as sysdba << EOF > /dev/null
set pagesize 0 feedback off verify off heading off echo off tab off
whenever sqlerror exit sql.sqlcode
startup mount;
exit;
EOF`
chkerr $? "failed to mount database $4"
is_open_mode $1 $2 $3 $4 "MOUNTED"
if [ "$?" -eq "0" ]; then
  fail "$4 is not in MOUNTED mode"
fi
}
 
###############################################################################
# NAME:        open_database
#
# DESCRIPTION:
#   Opens the specified database.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
open_database()
{
display "opening database $4"
l_od_sql=`sqlplus -s $1/$2@$3 as sysdba << EOF > /dev/null
set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode
alter database open; 
exit; 
EOF`
chkerr $? "failed to open datdabase $4"
}
 
###############################################################################
# NAME:        purge_resume_state
# 
# DESCRIPTION:
#   Deletes all flashback restore points associated with this script on the 
#   specified database.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     $RPFX
#
# RETURN:
#   None
#
###############################################################################
purge_resume_state() {
display "purging script execution state from database $4"
l_prs_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
declare 
  cursor curs is 
    select name from v\$restore_point where name like '${RPFX}_%'; 
begin 
  for r_curs in curs loop 
    execute immediate 'drop restore point ' || r_curs.name; 
  end loop; 
end;
/
exit;"
sql_exec $1 $2 $3 "$l_prs_sql"
chkerr $? "failed to purge script state from database $4"
}
 
###############################################################################
# NAME:        recover_to_logical
#
# DESCRIPTION: 
#   Instantiates a transient logical standby from a physical standby.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
recover_to_logical()
{
display "converting physical standby into transient logical standby"
 
l_rtl_sql="set pagesize 0 feedback off verify off heading off echo off tab off; 
whenever sqlerror exit sql.sqlcode 
alter database recover to logical standby keep identity; 
exit;"
sql_exec $1 $2 $3 "$l_rtl_sql"
chkerr $? "failed to convert database $4 into a transient logical standby"
 
is_database_role $1 $2 $3 $4 "LOGICAL STANDBY"
if [ "$?" -eq "0" ]; then
  fail "failed to convert database $4 into a transient logical standby"
fi
}
 
###############################################################################
# NAME:     minimize_apply_lag
#
# DESCRIPTION:
#   Wait for the 'apply lag' to fall below $5 seconds.  Rather than look at the
#   apply lag in v$dataguard_stats, this procedure will measure the time it 
#   takes the current scn at the primary to be processed by media recovery or
#   or the apply process, depending on the standby type.  If the time to apply 
#   the redo exceeds the specified threshold, this procedure will retry for up 
#   to $9 minutes, after which it will return an error.
#
# INPUT(S): 
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name to primary
#     $4: database unique name of primary
#     $5: tns service name to standby
#     $6: database unique name of standby
#     $7: database version of standby
#     $8: apply lag target in seconds
#     $9: timeout in minutes
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
minimize_apply_lag()
{
# Don't perform the wait if on 11.1 (see 14174798)
if [ "$7" = "11.1" ]; then
  return 0
fi
 
display "waiting for apply lag to fall under $8 seconds"
 
# Determine the database role of the standby
is_database_role $1 $2 $5 $6 "PHYSICAL STANDBY"
if [ "$?" -eq "1" ]; then
  l_mal_dbtype="physical"
else
  is_database_role $1 $2 $5 $6 "LOGICAL STANDBY"
  if [ "$?" -eq "1" ]; then
    l_mal_dbtype="logical"
  else
    fail "database $6 is neither a physical nor logical standby"
  fi
fi
 
# Loop until within apply delay or timeout
l_mal_curtime=0
l_mal_maxtime=1
l_mal_reset=1
l_mal_status=1
while [ "$l_mal_status" -eq "1" ] && [ "$l_mal_curtime" -lt "$l_mal_maxtime" ]
do
  # Reset the timeouts
  if [ "$l_mal_reset" -eq "1" ]; then
    l_mal_curtime=`perl -e 'print int(time)'`
    l_mal_exptime=`expr $9 "*" 60`
    l_mal_engtime=`expr $l_mal_curtime + 60`
    l_mal_maxtime=`expr $l_mal_curtime + $l_mal_exptime`
    l_mal_reset=0
  fi
 
  # Grab the base time from which to measure the apply delay
  l_mal_basetime=`perl -e 'print int(time)'`
 
  # Grab the current scn associated with base time
  get_primary_scn $1 $2 $3 $4
  l_mal_basescn=$l_gps_val
 
  # Switch logs to ensure delivery to the standby
  switch_logs $1 $2 $3 $4
 
  # Loop until the base scn has been applied
  l_mal_pstscn="0"
  l_mal_curscn="0"
  while (sql_lt $1 $2 $5 "$l_mal_curscn" "$l_mal_basescn") && [ "$l_mal_curtime" -lt "$l_mal_maxtime" ]
  do
    # Get the last processed scn on the standby
    if [ "$l_mal_dbtype" = "logical" ]; then
      get_applied_scn $1 $2 $5 $6
      l_mal_curscn=$l_gas_val
    else
      get_recovery_scn $1 $2 $5 $6 $7
      l_mal_curscn=$l_grs_val
    fi
 
    # Grab the time associated with processed scn
    l_mal_curtime=`perl -e 'print int(time)'`
 
    # The base scn has been processed
    if (sql_lt $1 $2 $5 "$l_mal_basescn" "$l_mal_curscn"); then 
 
      # Calculate the apply lag
      l_mal_lag=`expr $l_mal_curtime - $l_mal_basetime`
      display "apply lag measured at ${l_mal_lag} seconds"
 
      # Success if it was applied within the delay
      if [ "$l_mal_lag" -le "$8" ]; then
        l_mal_status=0
      else
        # Reset the timeout and try again since we're making progress
        l_mal_reset=1
      fi
 
      # Break to the main loop to proceed or retry
      break
    fi
 
    # The processed scn has advanced from the last check
    if [ "$l_mal_pstscn" != "$l_mal_curscn" ]; then
      l_mal_pstscn=$l_mal_curscn
      l_mal_engtime=`expr $l_mal_curtime + 60`
    else
      # Check the engine status if the processed scn was stagnant for 60 seconds
      if [ "$l_mal_curtime" -gt "$l_mal_engtime" ]; then
        if [ "$l_mal_dbtype" = "logical" ]; then
          is_lsp_running $1 $2 $5 $6
          if [ "$?" -eq "0" ]; then
            fail "failed to find a running logical standby apply on $6"
          fi
        else
          is_mrp_running $1 $2 $5 $6
          if [ "$?" -eq "0" ]; then
            fail "failed to find a running media recovery process on $6"
          fi 
        fi
 
        # Reset the engine check time
        l_mal_engtime=`expr $l_mal_curtime + 60`
      fi
    fi 
 
    # Sleep and try again
    sleep $STB_VIEW_INIT_INTERVAL
  done
done
 
chkerr $l_mal_status "timed out after $9 minutes of inactivity"
}
 
###############################################################################
# NAME:        prompt 
# 
# DESCRIPTION: 
#   Displays the message supplied in $1 and returns the user input.  The $2
#   argument is a / delimited list of valid answers.
#
# INPUT(S):
#   Arguments:
#     $1: message to display to user
#     $2: valid answers list eg: y/n
#
#   Globals:
#     None
#
# RETURN:
#   l_p_val:  One of the values supplied in $2
#
###############################################################################
prompt()
{
# Create a space delimited list of valid answers
l_p_anslst=`echo $2 | tr '/' ' '`
 
# Create the full message text
l_p_msg="$1 ($2): "
 
while [ "1" ]
do
  # Write to logfile only since prompting writes to the terminal
  suppresst=1
  display_raw "$l_p_msg"
  suppresst=0
 
  l_p_val=""
  read -p "$l_p_msg" selection
  for validans in $l_p_anslst
  do
    if [ "$selection" = "$validans" ]; then
      l_p_val="$selection"
      display_raw ""
      return 0
    fi
  done
 
  display "not a valid option - '$selection'\n"
done
}
 
###############################################################################
# NAME:        set_rolling_upg_params
# 
# DESCRIPTION: 
#   Setup logical standby parameters suitable for physical rolling upgrade on
#   the specified database.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
set_rolling_upgrade_params()
{
display "configuring transient logical standby parameters for rolling upgrade"
l_rup_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
execute sys.dbms_logstdby.apply_set('LOG_AUTO_DELETE', 'FALSE'); 
execute sys.dbms_logstdby.apply_set('MAX_EVENTS_RECORDED', dbms_logstdby.max_events); 
execute sys.dbms_logstdby.apply_set('RECORD_UNSUPPORTED_OPERATIONS', 'TRUE'); 
execute sys.dbms_logstdby.apply_set('MAX_SERVERS', '15'); 
execute sys.dbms_logstdby.apply_set('MAX_SGA', '50'); 
exit;"
sql_exec $1 $2 $3 "$l_rup_sql"
chkerr $? "failed to configure logical standby for rolling upgrade"
}
 
###############################################################################
# NAME:        shutdown_database
#
# DESCRIPTION: 
#   Shutdown the specified database.
# 
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
shutdown_database() 
{
display "shutting down database $4"
l_sd_sql=`sqlplus -s $1/$2@$3 as sysdba << EOF > /dev/null
set pagesize 0 feedback off verify off heading off echo off tab off 
shutdown immediate;
exit;
EOF`
}
 
###############################################################################
# NAME:        sql_exec
#
# DESCRIPTION: 
#   Execute a sql string via the supplied credentials and tns service.  The exit
#   code from sqlplus is returned from this routine.  Note that this return value
#   varies across platforms and should not be relied on beyond simple 
#   success/failure distinction.  The output from the sql script is stored in 
#   the sql_out global variable, and should not be modified in any way.  This
#   routine will also handle displaying the output and entire sql script should
#   a failure occur during execution.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: sql string to execute
#
#   Globals:
#     sql_out:  raw output from sql script 
#
# RETURN:
#   status returned from sqlplus
#
###############################################################################
sql_exec()
{
# write sql string to temporary file
echo "$4" > $LOG_SQL_EXETMP
 
# execute sql
sql_out=`sqlplus -L -s $1/$2@$3 as sysdba @${LOG_SQL_EXETMP}`
 
# save sqlplus return value
l_se_ret=$?
 
# display offending sql code
if [ "$l_se_ret" -ne "0" ] && [ "$LOG_SQL_ERRORS" -eq "1" ]; then
  display_raw "\n### The following error was encountered:"
  display_raw "$sql_out"
  display_raw "\n### The offending sql code in its entirety:"
  display_raw "$4\n"
fi
 
return $l_se_ret
}
 
###############################################################################
# NAME:        start_logical_apply
#
# DESCRIPTION: 
#   Start logical standby apply on the specified database.  Since the START 
#   LOGICAL DDL returns immediately after spawning the LSP0 process, this 
#   routine waits until is_lsp_running can confirm the startup.  If logical 
#   standby is already running, this routine concludes success.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     $LSP_START_INTERVAL $LSP_START_TIMEOUT 
#
# RETURN:
#   None
#
###############################################################################
start_logical_apply()
{
# check if logical needs to be started
is_lsp_running $1 $2 $3 $4
if [ "$?" -eq "0" ]; then
 
  # Start logical standby
  display "starting logical standby on database $4"
  l_sla_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  alter database start logical standby apply immediate; 
  exit;"
  sql_exec $1 $2 $3 "$l_sla_sql"
  chkerr $? "failed to start logical standby on database $4"
 
  # Confirm startup
  l_sla_status=1
  l_sla_curtime=`perl -e 'print int(time)'`
  l_sla_exptime=`expr $LSP_START_TIMEOUT "*" 60`
  l_sla_maxtime=`expr $l_sla_curtime + $l_sla_exptime`
  while [ "$l_sla_curtime" -lt "$l_sla_maxtime" ]
  do
    is_lsp_running $1 $2 $3 $4
    if [ "$?" -eq "1" ]; then
      l_sla_status=0
      break
    fi
 
    sleep $LSP_START_INTERVAL
    l_sla_curtime=`perl -e 'print int(time)'`
  done
  chkerr $l_sla_status "timed out after $LSP_START_TIMEOUT minutes of inactivity"
fi
}
 
###############################################################################
# NAME:        start_logical_build
#
# DESCRIPTION: 
#   Invokes dbms_logstdb.build on the specified database as required for 
#   logical standby instantiation.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
start_logical_build()
{
display "executing dbms_logstdby.build on database $4"
l_slb_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
execute dbms_logstdby.build; 
exit;"
sql_exec $1 $2 $3 "$l_slb_sql"
chkerr $? "failed to complete dbms_logstdby.build"
}
 
###############################################################################
# NAME:        start_media_recovery
#
# DESCRIPTION: 
#   This routine starts media recovery on the specified database.  If media 
#   recovery is already running, this routine concludes success.  The $5
#   argument allows for customized options to media recovery startup.  If not
#   specified it assumes a default.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: recovery options (optional)
#
#   Globals:
#     $MRP_START_INTERVAL $MRP_START_TIMEOUT
#
# RETURN:
#   None
#
###############################################################################
start_media_recovery()
{
# check if media recovery needs to be started
is_mrp_running $1 $2 $3 $4
l_smr_inst=$?
if [ "$l_smr_inst" -eq "0" ]; then
  if [ "$5" ]; then
    l_smr_opt="$5"
  else
    l_smr_opt="using current logfile through next switchover disconnect"
  fi
 
  # start media recovery
  display "starting media recovery on $4"
  l_smr_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  alter database recover managed standby database ${l_smr_opt}; 
  exit;"
  sql_exec $1 $2 $3 "$l_smr_sql"
  chkerr $? "failed to start media recovery"
 
  # confirm the MRP has started
  wait_mrp_active $1 $2 $3 $4
else
  # ensure the MRP is running on the instance associated with the tns service name
  get_instance_id $1 $2 $3 $4
  if [ "$l_smr_inst" -ne "$l_gii_val" ]; then
    display "media recovery on database $4 is unexpectedly running on instance id $l_smr_inst"
    fail "restart media recovery on instance id $l_gii_val or supply a tns name to instance id $l_smr_inst"
  fi
fi
}
 
###############################################################################
# NAME:        stop_logical_apply
#
# DESCRIPTION: 
#   Stops logical standby apply on the specified database.  If apply is not
#   running, this routine concludes success.
#   
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
stop_logical_apply()
{
# check if logical needs to be stopped
is_lsp_running $1 $2 $3 $4
if [ "$?" -eq "1" ]; then
 
  # stop logical standby
  display "stopping logical standby on $4"
  l_sla_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  alter database stop logical standby apply; 
  exit;"
  sql_exec $1 $2 $3 "$l_sla_sql"
  chkerr $? "failed to stop logical standby apply"
fi
}
 
###############################################################################
# NAME:        stop_media_recovery
#
# DESCRIPTION: 
#   Stop media recovery on the specified database.  If media recovery is not 
#   running, this routine concludes success.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
stop_media_recovery()
{
# check if media recovery needs to be stopped
is_mrp_running $1 $2 $3 $4
l_smr_inst=$?
if [ "$l_smr_inst" -gt "0" ]; then
  # ensure the MRP is running on the instance associated with the tns service name
  get_instance_id $1 $2 $3 $4
  if [ "$l_smr_inst" -ne "$l_gii_val" ]; then
    display "media recovery on database $4 is unexpectedly running on instance id $l_smr_inst"
    fail "restart media recovery on instance id $l_gii_val or supply a tns name to instance id $l_smr_inst"
  fi
 
  # stop media recovery
  display "stopping media recovery on $4"
  l_smr2_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  alter database recover managed standby database cancel; 
  exit;"
  sql_exec $1 $2 $3 "$l_smr2_sql"
  chkerr $? "failed to stop media recovery"
fi
}
 
###############################################################################
# NAME:        switch_logs
#
# DESCRIPTION: 
#   Switch logs on the database solely for the purpose of refreshing the 
#   connection status with the standby database.  
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
switch_logs()
{
l_sl_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter system switch logfile;
exit;"
sql_exec $1 $2 $3 "$l_sl_sql"
chkerr $? "failed to switch logfiles on database $4"
}
 
###############################################################################
# NAME:        switch_logical_to_primary
#
# DESCRIPTION: 
#   Switchover the specified logical standby database to the primary role.  If 
#   the database is already a primary, this routine concludes success.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
switch_logical_to_primary()
{
# Assume success if db is already a primary
is_database_role $1 $2 $3 $4 "PRIMARY"
if [ "$?" -eq "1" ]; then
  display "$4 is already a primary"
  return 1
fi
 
# Ensure we're switching from a logical
is_database_role $1 $2 $3 $4 "LOGICAL STANDBY"
if [ "$?" -eq "0" ]; then
  fail "$4 is neither a primary nor logical standby database"
fi
 
# Switchover
display "switching $4 to become the new primary"
l_slp_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter database commit to switchover to primary; 
exit;"
sql_exec $1 $2 $3 "$l_slp_sql"
chkerr $? "failed to switchover to primary"
display "$4 is now the new primary"
return 0
}
 
###############################################################################
# NAME:        switch_physical_to_primary
#
# DESCRIPTION: 
#   Switchover the specified physical standby database to the primary role.  If 
#   the database is already a primary, this routine concludes success.
# 
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
switch_physical_to_primary()
{
# Assume success if db is already a primary
is_database_role $1 $2 $3 $4 "PRIMARY"
if [ "$?" -eq "1" ]; then
  display "$4 is already a primary"
  return 1
fi
 
# Ensure we're switching from a physical
is_database_role $1 $2 $3 $4 "PHYSICAL STANDBY"
if [ "$?" -eq "0" ]; then
  fail "$4 is neither a primary nor physical standby database"
fi
 
# Switchover
display "switching $4 to become the new primary"
l_spp_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter database commit to switchover to primary; 
exit;"
sql_exec $1 $2 $3 "$l_spp_sql"
chkerr $? "failed to switchover to primary"
display "$4 is now the new primary"
return 0
}
 
 
###############################################################################
# NAME:        switch_primary_to_logical
#
# DESCRIPTION: 
#   Switchover the specified primary database to a logical standby role.  If the
#   database is already a logical standby, this routine concludes success.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
switch_primary_to_logical()
{
# Assume success if db is already a logical
is_database_role $1 $2 $3 $4 "LOGICAL STANDBY"
if [ "$?" -eq "1" ]; then
  display "$4 is already a logical standby"
  return 1
fi
 
# Ensure we're switching from a primary
is_database_role $1 $2 $3 $4 "PRIMARY"
if [ "$?" -eq "0" ]; then
  fail "$4 is neither a primary nor logical standby database"
fi
 
# Switchover
display "switching $4 to become a logical standby"
l_spl_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter database commit to switchover to logical standby; 
exit;"
sql_exec $1 $2 $3 "$l_spl_sql"
chkerr $? "failed to switchover to logical standby"
display "$4 is now a logical standby"
return 0
}
 
###############################################################################
# NAME:        switch_primary_to_physical
#
# DESCRIPTION: 
#   Switchover the specified primary database to a physical standby role.  If 
#   the database is already a physical standby, this routine concludes success.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
switch_primary_to_physical()
{
# Assume success if db is already a physical
is_database_role $1 $2 $3 $4 "PHYSICAL STANDBY"
if [ "$?" -eq "1" ]; then
  display "$4 is already a physical standby"
  return 1
fi
 
# Ensure we're switching from a primary
is_database_role $1 $2 $3 $4 "PRIMARY"
if [ "$?" -eq "0" ]; then
  fail "$4 is neither a primary nor physical standby database"
fi
 
# Switchover
display "switching $4 to become a physical standby"
l_spp_sql="set pagesize 0 feedback off verify off heading off echo off tab off 
whenever sqlerror exit sql.sqlcode 
alter database commit to switchover to physical standby with session shutdown; 
exit;"
sql_exec $1 $2 $3 "$l_spp_sql"
chkerr $? "failed to switchover to physical standby"
display "$4 is now a physical standby"
return 0
#
# NOTE: no further queries permitted since the database will no longer mounted 
#
}
 
###############################################################################
# NAME:        wait_mrp_active
#
# DESCRIPTION: 
#   Wait for $MRP_START_TIMEOUT minutes to confirm that the MRP is active.  If
#   we can't detect an active MRP, abort the script with an error.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
wait_mrp_active()
{
  display "confirming media recovery is running"
  l_wma_status=1
  l_wma_curtime=`perl -e 'print int(time)'`
  l_wma_exptime=`expr $MRP_START_TIMEOUT "*" 60`
  l_wma_maxtime=`expr $l_wma_curtime + $l_wma_exptime`
  while [ "$l_wma_curtime" -lt "$l_wma_maxtime" ]
  do
    is_mrp_running $1 $2 $3 $4
    if [ "$?" -gt "0" ]; then
      l_wma_status=0
      break
    fi
 
    sleep $MRP_START_INTERVAL
    l_wma_curtime=`perl -e 'print int(time)'`
  done
  chkerr $l_wma_status "could not detect an active MRP after $MRP_START_TIMEOUT minutes"
}
 
###############################################################################
# NAME:        wait_standby_eor
#
# DESCRIPTION: 
#   Wait for specified standby database to process the end-of-redo and shutdown 
#   apply/recovery.  This routine will wait for up to $STB_EOR_TIMEOUT minutes
#   before timing out.  If the apply/recovery is found to be shutdown, this 
#   routine will make one startup attempt.  
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#     $5: database version of standby
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
wait_standby_eor()
{
display "waiting for standby $4 to process end-of-redo from primary"
 
# Identify the standby type
is_database_role $1 $2 $3 $4 "LOGICAL STANDBY"
if [ "$?" -eq "1" ]; then
  l_wse_islogical=1
else
  is_database_role $1 $2 $3 $4 "PHYSICAL STANDBY"
  if [ "$?" -eq "1" ]; then
    l_wse_islogical=0
  else
    fail "database $4 is neither a physical nor logical standby"
  fi
fi
 
# Wait for 'TO PRIMARY' switchover status to appear -- end-of-redo success
#display "waiting until end-of-redo complete (switchover_status of 'TO PRIMARY')"
l_wse_expdelta=`expr $STB_EOR_TIMEOUT "*" 60`
l_wse_resdelta=`expr $STB_EOR_RESTART_AFTER "*" 60`
l_wse_pstscn=0
l_wse_curscn=1
l_wse_restarted=0
l_wse_status=1
l_wse_curtime=0
l_wse_maxtime=1
l_wse_reset=1
while [ "$l_wse_curtime" -lt "$l_wse_maxtime" ]
do
  # Grab the current time
  l_wse_curtime=`perl -e 'print int(time)'`
 
  # Reset the timeouts
  if [ "$l_wse_reset" -eq "1" ]; then
    l_wse_maxtime=`expr $l_wse_curtime + $l_wse_expdelta`
    l_wse_restime=`expr $l_wse_curtime + $l_wse_resdelta`
    l_wse_reset=0
  fi
 
  # Check for end-of-redo completion
  l_wse_sql="    set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  select count(1) from v\$database where SWITCHOVER_STATUS='TO PRIMARY' or SWITCHOVER_STATUS='SESSIONS ACTIVE'; 
  exit;"
  sql_exec $1 $2 $3 "$l_wse_sql"
  chkerr $? "failed to query switchover status on $4"
  l_wse_val=`echo $sql_out`
 
  # Success
  if [ "$l_wse_val" = "1" ]; then
    l_wse_status=0
    break
  fi
 
  # Get the last processed scn on the standby
  if [ "$l_wse_islogical" -eq "1" ]; then
    get_applied_scn $1 $2 $3 $4
    l_wse_curscn=$l_gas_val
  else
    get_recovery_scn $1 $2 $3 $4 $5
    l_wse_curscn=$l_grs_val
  fi
 
  # Reset all timeouts if last applied scn has changed
  if [ "$l_wse_pstscn" != "$l_wse_curscn" ]; then
    l_wse_pstscn=$l_wse_curscn
    l_wse_reset=1
  else
    # Consider a one-time restart if last applied scn has stagnated for l_wse_resdelta seconds
    if [ "$l_wse_curtime" -gt "$l_wse_restime" ]; then
 
      # Check if apply/recovery is currently running
      if [ "$l_wse_islogical" -eq "1" ]; then
        is_lsp_running $1 $2 $3 $4
      else
        is_mrp_running $1 $2 $3 $4
      fi
      l_wse_running=$?
 
      # Perform a one-time restart if apply/recovery is not running.
      if [ "$l_wse_restarted" -eq "0" ]; then
        if [ "$l_wse_running" -eq "0" ]; then
          display "WARNING: making one attempt to start apply/recovery until end-of-redo"
          if [ "$l_wse_islogical" -eq "1" ]; then
            start_logical_apply $1 $2 $3 $4
          else
            start_media_recovery $1 $2 $3 $4
          fi
          l_wse_restarted=1
          l_wse_reset=1
        fi
      else
        if [ "$l_wse_running" -eq "0" ]; then
          fail "standby $4 failed to process end-of-redo"
        fi
      fi
    fi
  fi
  sleep $STB_EOR_INTERVAL
done
chkerr $l_wse_status "timed out after $l_wse_expdelta seconds of inactivity"
}
 
###############################################################################
# NAME:        wait_logical_dictload
#
# DESCRIPTION: 
#   Wait for the logminer dictionary to load.  This routine will wait for up to
#   $LSP_DICT_LOAD_TIMEOUT minutes before timing out.  In order to timeout,
#   successive checks of the dictionary load progress must remain the same for
#   the entire timeout period.  Any change in progress resets the timeout.
#
# INPUT(S):
#   Arguments:
#     $1: database user
#     $2: user password
#     $3: tns service name
#     $4: database unique name
#
#   Globals:
#     None
#
# RETURN:
#   None
#
###############################################################################
wait_logical_dictload()
{
display "waiting until logminer dictionary has fully loaded"
 
# Wait for dictionary to load
l_wld_lstpct=0
l_wld_retried=0
l_wld_status=1
l_wld_wasloading=0
l_wld_curtime=`perl -e 'print int(time)'`
l_wld_exptime=`expr $LSP_DICT_LOAD_TIMEOUT "*" 60`
l_wld_maxtime=`expr $l_wld_curtime + $l_wld_exptime`
while [ "$l_wld_curtime" -lt "$l_wld_maxtime" ]
do 
  # Lookup current dictionary load completion
  l_wld_sql="  set pagesize 0 feedback off verify off heading off echo off tab off 
  whenever sqlerror exit sql.sqlcode 
  select nvl(translate((select substr(l.status, 12, 4) from 
  v\$logstdby l, v\$logstdby_state s where l.status like 'ORA-16115%' 
  and s.state = 'LOADING DICTIONARY'), '%', ' '), '0') from dual; 
  exit;"
  sql_exec $1 $2 $3 "$l_wld_sql"
  chkerr $? "failed to query dictionary load status"
  l_wld_curpct=`echo $sql_out | awk '{print int(\$0)}'`
 
  # has dictionary loading stopped ?
  if [ "$l_wld_curpct" -eq "0" ]; then
 
    # Upon successful load, we should be in either of these states
    l_wld_sql="    set pagesize 0 feedback off verify off heading off echo off tab off 
    whenever sqlerror exit sql.sqlcode 
    select count(1) from v\$logstdby_state where 
    state = 'APPLYING' or state = 'IDLE'; 
    exit;"
    sql_exec $1 $2 $3 "$l_wld_sql"
    chkerr $? "failed to query logical standby apply status"
    l_wld_loaded=`echo $sql_out`
 
    # Success
    if [ "$l_wld_loaded" -eq "1" ]; then
      l_wld_status=0
      break
    fi
 
    # Give apply one chance to reload the dictionary
    if [ "$l_wld_retried" -eq "0" ]; then
      start_logical_apply $1 $2 $3 $4
      l_wld_retried=1
    fi
  else
    # Set flag indicating we've seen the dictionary loading
    l_wld_wasloading=1
 
    # NOTE: this view hangs at 100%...show 99% instead
    if [ "$l_wld_curpct" -eq "100" ]; then
      l_wld_curpct=99
    fi
 
    # Report if progress was made since last check
    if [ "$l_wld_curpct" -gt "$l_wld_lstpct" ] && [ "$l_wld_curpct" -ne "100" ]; then
      l_wld_strpct=`printf %02d $l_wld_curpct`
      display "dictionary load ${l_wld_strpct}% complete"
 
      # Reset timeout
      l_wld_maxtime=`expr $l_wld_curtime + $l_wld_exptime`
 
      # Retain last retain last percentage
      l_wld_lstpct=$l_wld_curpct
    fi
  fi
 
  sleep $LSP_DICT_LOAD_INTERVAL
  l_wld_curtime=`perl -e 'print int(time)'`
done
 
if [ "$l_wld_status" -eq "0" ]; then
  if [ "$l_wld_wasloading" -eq "1" ]; then
    display "dictionary load is complete"
  else
    display "dictionary already loaded"
  fi
else
  fail "timed out after $LSP_DICT_LOAD_TIMEOUT minutes of inactivity"
fi
}
 
autopru $1 $2 $3 $4 $5 $6
  • oracle_guard_upgrade.1602864478.txt.gz
  • Last modified: 2020/10/16 16:07
  • by andonovj