#
# dgcreate_o.pl
#
# Copyright (c) 2004, 2009, Oracle and/or its affiliates.All rights reserved.
#
# NAME
# dgcreate_10_2.pl
#
# DESCRIPTION
# Data Guard physical/logical post-DBClone standby creation
# for 10.2 OMS
#
# NOTES
# This file is used by the 10.2 OMS for DG create standby jobs.
# It is streamed to the Agent from the OMS side, and cannot be
# used alone, as it calls some routines in db_clone_10_2.pl. Since
# it does not include this file, it is bundled with db_clone_10_2.pl
# during the streaming process. Therefore, it cannot be compiled
# by itself from the command line.
#
# MODIFIED (MM/DD/YY)
# ngade 02/17/09 - Code slap 10.2.0.5.0 -> 11.2 round 2
# ngade 08/03/08 - Code Slap 10.2.0.5GC -> 11.2SA
# sjconnol 10/20/08 - Support for no-broker creation
# rimmidi 05/04/08 - Code slap from 11GC to 10.2.0.5
# rimmidi 06/11/07 - Password File Modification
# sjconnol 03/21/07 - Backport sjconnol_bug5699797a from main
# sjconnol 02/13/07 -
# sjconnol 10/20/07 - Bug 6516731
# ngade 11/03/06 - RMAN backup support
# sjconnol 11/02/06 - Bug 5624530
# ngade 09/19/06 - fix nls bug 5250491
# sjconnol 07/11/06 - Bug 5352565
# sjconnol 05/02/06 - Bug 5162803
# sjconnol 09/28/05 - Bug 4636417, 4652338
# sjconnol 09/14/05 - Bug 4607258
# sxzhu 09/06/05 - ASM spfile support
# sjconnol 08/29/05 - Bug 4562587
# sjconnol 07/31/05 - Bug 4520686
# sjconnol 07/05/05 - Bug 4474555
# gallison 06/28/05 - Fix bug 4342419
# sjconnol 06/17/05 - Bug 4439779: revert to dgutil.pl
# sjconnol 06/03/05 - Bug 4407481: include dgutil_core.pl
# sjconnol 05/23/05 - Bug 4380906
# sjconnol 03/15/05 - Bug 4225517
# sjconnol 01/26/05 - Bug 4058815: support for ctrl file on raw
# sjconnol 11/21/04 - Fix tempfile creation for OMF
# sjconnol 11/12/04 - Stream rman.pl from OMS
# sjconnol 09/29/04 - Cleanup old controlfile
# sjconnol 09/08/04 - Incorporate all code from dgcreate.pl
# sjconnol 08/10/04 - OMF support
# sjconnol 08/02/04 - Creation
#
require "flush.pl";
require "$ENV{EMDROOT}/sysman/admin/scripts/db/dg/dgutil.pl";
use strict;
#use warnings;
use Encode;
use vars qw/ $DGstbyLDA $DGprmyLDA
$DGInstance $DGOracleHome
$DGdevMode $DGsiteID $DGresName $DGdisplayName
$DGdbObjID $DGautoDelete $DGuseBroker $DGcreateSnapshot
$DGisLogical $DGdbVer $DGIfile $DGIfileForMV $DGIfiletmp1
$DGIfiletmp2 $DGis10gOrHigher $DGis102OrHigher $DGis11gOrHigher $DELIMITER $DGstbyUser $DGstbyPW $DGParam1
$DGstbyTNS $brokerConnDesc $DGpitSCN @OFFLINE_FILES $NT $S $MV $TEMP
$target_home $target_sid $db_10_or_higher @SQLCMDS
$rman_script $DGuseDuplicate $DGuseRMANBackup $DGConvertToLogical
$db_username $rcvcat_username $rcvcat_connect_string $dbid
$db_role $rman_command_id $is_repos_database $rin $rout
$DGRecover
/;
#$ENV{EMAGENT_PERL_TRACE_LEVEL} = 0; #DEBUG level.
$SIG{__DIE__} = \&handleError;
$SIG{__WARN__} = \&handleCreateWarning;
## Handle DBI sql warnings
sub handleCreateWarning{
debug("dgutil.handleCreateWarning()");
if(defined($DBI::errstr)){
## Special handling for ORA-00283
debug("SQL Warning: $DBI::errstr");
if($DBI::errstr =~ /ORA-00283/){
debug("dgutil.handleCreateWarning(): Re-running recovery");
$DGRecover = 1;
}
}
else{
debug("$_[0]");
}
}
sub DGcreateStandby_o{
debug("dgcreate.DGcreateStandby_o: *** START ***");
my($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $logsizes, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname, $controlfile, $toControlFiles, $maxinfo, $isOMF, $isASM, $spfileName, $conndesc, $use_rman_backup, $use_duplicate) = @_;
## environment setup
DGcreateAdmin($instance, $ohome, $lfile, $initfile, $islogical, $conndesc, $dgopts, $use_duplicate, $use_rman_backup);
DGsetStbyCreds();
## Remove old SPFILE
DGrmInitFiles();
## For 9i and non-OMF 10g, run the old procedure after installing
## 2nd controlfile
if(!$DGis10gOrHigher || (($isOMF ne "Y") && ($isASM ne "Y"))){
## Must substitute 2nd controlfile, created after db backup
DGshutdownStby();
## Bug 4058815: Always restore ctrl file to ultimate location
debug("dgcreate.DGcreateStandby_o: Changing init file control_files param to $toControlFiles");
# Add ultimate ctrl file name to real init file
# (Bug 4652338: handle multiple controlfiles)
commentInitFileEntry("control_files", $DGIfile);
addControlFileToInitFile("$toControlFiles", $DGIfile);
# Restore ctrl file to ultimate location
DGstartupStbyNomount();
if($DGuseDuplicate eq "N"){
restoreBackupControlFile("${controlfile}tmp");
unlink("${controlfile}tmp");
unlink("${controlfile}");
}
DGshutdownStby();
DGstartupStby();
DGcreateStandby($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname, $isOMF, $isASM);
}
## For 10g OMF, run the new procedure
else{
## For 10g, accomodate OMF by harvesting names from original ctrl file,
## and then using these names for the rename.
debug("dgcreate.DGcreateStandby_o: Getting names from original ctrl file");
my($ids, $datafiles) = getRestoredDatafiles();
$toDatafiles = $datafiles;
## Duplicate: no datafiles restored, so add dummy entries
my $realCtlFile = "${controlfile}tmp";
if($DGuseDuplicate eq "Y"){
debug("dgcreate.DGcreateStandby_o: Duplicate: no datafiles listed");
## Use the 1st datafile as the dummy entry; just need to list a file
$toDatafiles = DGgetDatafile();
$realCtlFile = DGgetCtlfile();
}
## Must substitute 2nd controlfile, created after db backup. Needs
## to be converted to OMF. Create dummy file to harvest OMF name,
## update init file, then copy the real ctrl file to OMF loc.
## Temporarily modify the db_name param in the init file; use of
## primary db_name will cause ctrl file creation to fail if done
## on same host
DGshutdownStby();
# Remove control_file entry from real init file
# (so create controlfile uses OMF)
commentInitFileEntry("control_files", $DGIfile);
# Create temp init file with original db_name
copyFile("$DGIfile", "${DGIfile}tmp");
# Modify orig init file with temporary db_name
modifyInitFile("db_name", "temp", $DGIfile);
# Bug 4607258; remove pwfile entry from temporary file
commentInitFileEntry("remote_login_passwordfile", $DGIfile);
# Create the dummy controlfile using init file with temp db_name
createControlFile_10_2("temp", $toDatafiles, $logGroups, $toLogfiles, $logsizes, $maxinfo, $DGIfile, "N", "Y", "N");
# Add OMF ctrl file name to real init file
addControlFileNamesInInitFile("${DGIfile}tmp", "N");
# Substitute real (containing new OMF ctl file name) init file for orig
DGshutdownStby();
copyFile("${DGIfile}tmp", "$DGIfile");
removeFile("${DGIfile}tmp");
# Restart db, restore real controlfile to OMF location
DGstartupStbyNomount();
restoreBackupControlFile("$realCtlFile");
unlink("$realCtlFile");
unlink("${controlfile}");
# Restart db with correct OMF ctrl file
DGshutdownStby();
DGstartupStby();
if($DGisLogical){
DGcreateLogical10iOMF($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname, $isASM, $spfileName);
}
else{
DGcreatePhysicalOMF($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $isASM, $spfileName);
}
## Add the resource and enable
if($DGis102OrHigher){
DGaddResource102($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
}
else{
DGaddResource($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
}
DGdisconnect();
}
debug("dgcreate.DGcreateStandby_o: *** END ***");
}
sub DGcreatePhysicalOMF{
debug("dgcreate.DGcreatePhysicalOMF: *** START ***");
my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $isASM, $spfileName) = @_;
##init.ora files for physical
DGputIfile();
## bounce standby
DGbounceStby();
## Rename files
DGrenameFiles($fromDatafiles, $toDatafiles);
## Add new OMF logfiles
addLogfilesOmf();
#DGmigrateOnlineLogs();
#DGmigrateStandbyLogs();
## Harvest the recovery SCN
DGgetRecoverySCN();
## Recover the standby
if(! $DGis102OrHigher){
DGrecoverStby($tempNames);
}
## Move the real init.ora file in place
DGmvIfile();
## Create tempfiles (call dbclone routine)
if(! $DGis102OrHigher){
if($isASM eq "Y"){
addTempfilesOmf($tempNames, $tablespaces, $sizes, $autoextends);
}
else{
addTempfilesOmf("",$tablespaces, $sizes, $autoextends);
}
}
## Create the SPFILE
if($isASM eq "Y"){
$DGstbyLDA->disconnect;
copyFile("$DGIfile", "${DGIfile}.saved");
&createSpfile($DGIfile, $spfileName);
}
else{
DGcreateSpfile();
unlink($DGIfile);
}
## bounce standby
DGbounceStby();
## Online offlined datafiles
DGonlineDatafiles();
## Setup autodeletion policy
## (Non-essential operation; do not process return val)
DGsetAutoDelete();
debug("dgcreate.DGcreatePhysicalOMF: *** END ***");
}
## 10i Logical creation
sub DGcreateLogical10iOMF{
debug("dgcreate.DGcreateLogical10iOMF: *** START ***");
my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname, $isASM, $spfileName) = @_;
## Modify init.ora files for physical
DGputIfile($newdbname);
## bounce standby, startup mount
DGbounceStby();
## Rename files
DGrenameFiles($fromDatafiles, $toDatafiles);
## Add new OMF logfiles
addLogfilesOmf();
#DGmigrateOnlineLogs();
#DGmigrateStandbyLogs();
## Harvest the recovery SCN
DGgetRecoverySCN();
## Recover the standby, open resetlogs
DGrecoverStby();
## Create tempfiles (call dbclone routine)
if($isASM eq "Y"){
addTempfilesOmf($tempNames, $tablespaces, $sizes, $autoextends);
}
else{
addTempfilesOmf("",$tablespaces, $sizes, $autoextends);
}
## bounce, startup mount
DGbounceStby();
## DB ID/name change steps
if(!$DGdevMode){
## Turn off broker before running nid
my($sql) = "alter system set dg_broker_start=false scope=memory";
printDebug("dgcreate.nid: $sql");
my($dbcur) = $DGstbyLDA->prepare($sql);
$dbcur->execute;
## Disconnect before running nid
$DGstbyLDA->disconnect;
## Run NID to change DB ID/name; 10i NID shuts down database
DGnid($newdbname);
## Database is now shutdown; create new password file
DGorapwd();
## Move the real init.ora file in place
DGmvIfile();
## Restart
DGstartupStby();
}
## Create the SPFILE
if($isASM eq "Y"){
$DGstbyLDA->disconnect;
copyFile("$DGIfile", "${DGIfile}.saved");
&createSpfile($DGIfile, $spfileName);
}
else{
DGcreateSpfile();
unlink($DGIfile);
}
## Bounce
DGbounceStby();
## Open resetlogs (only if NID was run)
if(!$DGdevMode){
DGopenStby();
}
debug("dgcreate.DGcreateLogical10iOMF: *** START ***");
}
sub DGsetPrmyCreds{
my ($username, $password, $tns) = @_;
my $mode = 2; # SYSDBA
if(!$tns){
EMD_PERL_DEBUG("dgcreate.DGsetPrmyCreds: not using connect desc");
$DGprmyLDA = DBI->connect('dbi:Oracle:', "$username", "$password",
{ora_session_mode => $mode,
PrintError => 0,
RaiseError => 1});
}
else{
EMD_PERL_DEBUG("dgcreate.DGsetPrmyCreds: using connect desc");
$DGprmyLDA = DBI->connect('dbi:Oracle:', "$username@".$tns , "$password",
{ora_session_mode => $mode,
PrintError => 0,
RaiseError => 1});
}
}
sub DGsetStbyCredsNoConn{
($DGstbyUser, $DGstbyPW, $DGstbyTNS) = @_;
}
sub DGsetStbyCreds{
if(@_){
EMD_PERL_DEBUG("dgcreate.DGsetStbyCreds: connect info passed");
($DGstbyUser, $DGstbyPW, $DGstbyTNS) = @_;
}
my $mode = 2; # SYSDBA
## Bug 5699797: Always use full connect desc for Win
if($DGstbyTNS && $NT){
EMD_PERL_DEBUG("dgcreate.DGsetStbyCreds: using connect desc");
$DGstbyLDA = DBI->connect('dbi:Oracle:', "$DGstbyUser@".$DGstbyTNS , "$DGstbyPW",
{ora_session_mode => $mode,
PrintError => 0,
RaiseError => 1});
}
else{
EMD_PERL_DEBUG("dgcreate.DGsetStbyCreds: not using connect desc");
$DGstbyLDA = DBI->connect('dbi:Oracle:', "$DGstbyUser", "$DGstbyPW",
{ora_session_mode => $mode,
PrintError => 0,
RaiseError => 1});
}
}
sub DGdisconnect{
debug("dgcreate.DGdisconnect");
$DGprmyLDA->disconnect;
$DGstbyLDA->disconnect;
}
sub DGcreateStandby{
debug("dgcreate.DGcreateStandby");
my($instance, $ohome, $lfile, $initfile, $islogical, $fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $dgopts, $newdbname, $isOMF, $isASM) = @_;
if($DGisLogical){
if($DGis10gOrHigher){
DGcreateLogical10i($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
}
else{
DGcreateLogical9i($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
}
}
else{
DGcreatePhysical($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends);
}
## Add the resource and enable
if($DGis102OrHigher){
DGaddResource102($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
}
else{
DGaddResource($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname);
}
DGdisconnect();
unlink($DGIfile);
}
sub DGcreatePhysical{
debug("dgcreate.DGcreatePhysical");
my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends) = @_;
##init.ora files for physical
DGputIfile();
## bounce standby
DGbounceStby();
## Rename files
DGrenameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups);
## Harvest the recovery SCN
DGgetRecoverySCN();
## Recover the standby
if(! $DGis102OrHigher){
DGrecoverStby($tempNames);
}
## Move the real init.ora file in place
DGmvIfile();
## Create the SPFILE
DGcreateSpfile();
## Create tempfiles
if(! $DGis102OrHigher){
DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends);
}
## bounce standby
DGbounceStby();
## Online offlined datafiles
DGonlineDatafiles();
## Setup autodeletion policy
## (Non-essential operation; do not process return val)
DGsetAutoDelete();
}
## 9i Logical creation
sub DGcreateLogical9i{
debug("dgcreate.DGcreateLogical9i");
my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_;
## Modify init.ora files for physical
DGputIfile($newdbname);
## bounce standby, startup mount
DGbounceStby();
## Rename files
DGrenameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups);
## Harvest the recovery SCN
DGgetRecoverySCN();
## Recover the standby, open resetlogs
DGrecoverStby();
## Create tempfiles
DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends);
## Start logical apply (9i only)
DGstartLogical();
## bounce, startup mount
DGbounceStby();
## DB ID/name change steps
if(!$DGdevMode){
## Run NID to change DB ID/name
DGnid($newdbname);
## Move the real init.ora file in place
DGmvIfile();
}
## Create the SPFILE
DGcreateSpfile();
## shutdown
DGshutdownStby();
if(!$DGdevMode){
## Create new orapwd file
DGorapwd();
}
## startup
DGstartupStby();
## Open resetlogs (only if NID was run)
if(!$DGdevMode){
DGopenStby();
}
}
## 10i Logical creation
sub DGcreateLogical10i{
debug("dgcreate.DGcreateLogical9i");
my($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_;
## Modify init.ora files for physical
DGputIfile($newdbname);
## bounce standby, startup mount
DGbounceStby();
## Rename files
DGrenameFiles($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups);
## Harvest the recovery SCN
DGgetRecoverySCN();
## Recover the standby, open resetlogs
DGrecoverStby();
## Create tempfiles
DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends);
## bounce, startup mount
DGbounceStby();
## DB ID/name change steps
if(!$DGdevMode){
## Turn off broker before running nid
my($sql) = "alter system set dg_broker_start=false scope=memory";
printDebug("dgcreate.nid: $sql");
my($dbcur) = $DGstbyLDA->prepare($sql);
$dbcur->execute;
## Disconnect before running nid
$DGstbyLDA->disconnect;
## Run NID to change DB ID/name; 10i NID shuts down database
DGnid($newdbname);
## Database is now shutdown; create new password file
DGorapwd();
## Move the real init.ora file in place
DGmvIfile();
## Restart
DGstartupStby();
}
## Create the SPFILE
DGcreateSpfile();
## Bounce
DGbounceStby();
## Open resetlogs (only if NID was run)
if(!$DGdevMode){
DGopenStby();
}
## Setup autodeletion policy
## (Non-essential operation; do not process return val)
DGsetAutoDelete();
}
sub DGgetStbyDest{
my($stbydest);
if($DGis10gOrHigher){
my $recarea = get_init_param($DGstbyLDA, "db_recovery_file_dest");
## If the recovery area is in use, no need to return
## a value; recovery will automatically use it
if(defined($recarea) && ($recarea =~ /\S/)){
debug("dgcreate.DGgetStbyDest: using recovery area: $recarea");
return undef;
}
## Otherwise, get the value of standby archive dest
my $sql = "select destination from v\$archive_dest where dest_name='STANDBY_ARCHIVE_DEST'";
my $dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
$stbydest = $row[0];
}
else{
$stbydest = get_init_param($DGstbyLDA, "standby_archive_dest");
}
debug("dgcreate.DGgetStbyDest: standby_archive_dest: $stbydest");
return $stbydest;
}
sub DGrecoverStby102{
my($tempfiles) = @_;
my($sql, $dbcur, $dbcur2);
debug("dgcreate.DGrecoverStby102: tempfiles = $tempfiles");
## For physical, only do recovery if there are tempfiles
if(defined($tempfiles) && ($tempfiles eq "$DELIMITER")){
debug("dgcreate.DGrecoverStby102: no recovery to be done, returning");
return;
}
if($DGuseDuplicate eq "N"){
## 10.2: tempfile entries are retained in stby controlfile;
## run drop cmd for as many entries as are known
$sql = "alter system set standby_file_management='MANUAL'";
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur2 = $DGstbyLDA->prepare($sql);
$dbcur2->execute;
$sql = "SELECT FILE#, NAME from v\$tempfile";
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
my @row;
while(@row = $dbcur->fetchrow_array()){
## Bug 4520686: For OMF, TEMPFILE DROP will attempt to remove
## the underlying OMF file, so always rename the file to a temporary
## name before removing it.
my($tempname) = "${TEMP}${S}temp$$";
$sql = "ALTER DATABASE RENAME FILE '$row[1]' to '$tempname'";
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur2 = $DGstbyLDA->prepare($sql);
$dbcur2->execute;
$sql = "ALTER DATABASE TEMPFILE $row[0] DROP";
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur2 = $DGstbyLDA->prepare($sql);
$dbcur2->execute;
}
$sql = "alter system set standby_file_management='AUTO'";
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur2 = $DGstbyLDA->prepare($sql);
$dbcur2->execute;
}
## Harvest the recovery SCN
DGgetRecoverySCN102();
$sql = "ALTER DATABASE RECOVER MANAGED STANDBY DATABASE UNTIL CHANGE $DGpitSCN";
## Ignore potential ORA-283 error; just retry
$DGstbyLDA->{RaiseError} = 0;
$DGstbyLDA->{PrintError} = 1;
## $DGRecover will be reset by signal handler on ORA-283
$DGRecover = 1;
while($DGRecover){
$DGRecover = 0;
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
## Reactivate error handling
$DGstbyLDA->{RaiseError} = 1;
$DGstbyLDA->{PrintError} = 0;
if(! ($DGConvertToLogical || $DGisLogical)){
$sql = "ALTER DATABASE OPEN READ ONLY";
printDebug("dgcreate.DGrecoverStby102: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
}
sub DGrecoverStby{
my($tempfiles) = @_;
my($sql, $dbcur, $dbcur2);
debug("dgcreate.DGrecoverStby: tempfiles = $tempfiles");
## For physical, only do recovery if there are tempfiles
if(defined($tempfiles) && ($tempfiles eq "$DELIMITER")){
debug("dgcreate.DGrecoverStby: no recovery to be done, returning");
return;
}
my $fromclause = "";
my $stbydest = DGgetStbyDest();
## If using recovery area (which will be true for OMF/ASM), no
## need to specify FROM clause
if(defined($stbydest)){
$fromclause = "FROM '$stbydest'";
}
$sql = "ALTER DATABASE RECOVER AUTOMATIC $fromclause STANDBY DATABASE UNTIL CHANGE $DGpitSCN";
## Backup controlfile used only for 9i logical
if($DGisLogical && !$DGis10gOrHigher){
$sql = "ALTER DATABASE RECOVER AUTOMATIC FROM '$stbydest' DATABASE UNTIL CHANGE $DGpitSCN USING BACKUP CONTROLFILE";
}
## Only disable exception handling for physical
if(!$DGisLogical){
## Want to ignore all ORA-00278, ORA-00279, ORA-00289, ORA-00280,
## ORA-00310 , ORA-00334 errors during this type of recovery, as they
## are normal
## Turn off all exception handling, turn on logging of errors;
## if there's real error (not one of the ones above), we'll
## catch it in the open
$DGstbyLDA->{RaiseError} = 0;
$DGstbyLDA->{PrintError} = 1;
}
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
if($DGisLogical){
## Bug 4636417: Always set logical to Max Perf (do for all db versions)
$sql = "alter database set standby database to maximize performance";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
## 10i logical: ACTIVATE (not open), bounce, open
if($DGis10gOrHigher){
$sql = "alter database activate standby database";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
DGbounceStby();
$sql = "ALTER DATABASE OPEN";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
## 9i logical: Turn on guard prior to open
else{
$sql = "ALTER DATABASE GUARD ALL";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
$sql = "ALTER DATABASE OPEN RESETLOGS";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
}
## Physical
else{
$sql = "ALTER DATABASE RECOVER CANCEL";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
## Turn exception handling back on
$DGstbyLDA->{RaiseError} = 1;
$DGstbyLDA->{PrintError} = 0;
$sql = "ALTER DATABASE OPEN READ ONLY";
printDebug("dgcreate.DGrecoverStby: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
debug("dgcreate.DGrecoverStby: recovery complete to $DGpitSCN");
}
sub DGgetDatafile{
my($sql, $dbcur);
$sql = "SELECT NAME FROM V\$DATAFILE";
$dbcur = $DGstbyLDA->prepare($sql);
printDebug("dgcreate.DGgetDatafile: $sql");
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
my $file = $row[0];
debug("dgcreate.DGgetDatafile: $file");
return $file;
}
sub DGgetCtlfile{
my($sql, $dbcur);
$sql = "SELECT NAME FROM V\$CONTROLFILE";
$dbcur = $DGstbyLDA->prepare($sql);
printDebug("dgcreate.DGgetCtlfile: $sql");
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
my $file = $row[0];
debug("dgcreate.DGgetCtlfile: $file");
return $file;
}
sub DGgetRecoverySCN102{
my($sql, $dbcur);
$sql = "SELECT CHECKPOINT_CHANGE# FROM V\$DATABASE";
$dbcur = $DGprmyLDA->prepare($sql);
printDebug("dgcreate.DGgetRecoverySCN102: $sql");
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
$DGpitSCN = $row[0];
debug("dgcreate.DGgetRecoverySCN: pitSCN: $DGpitSCN");
}
sub DGgetRecoverySCN{
my($sql, $dbcur);
## 10i Logical: Recovery SCN is found on the standby, not the primary
if($DGisLogical && $DGis10gOrHigher){
$sql = "SELECT DI2LR_SCN FROM X\$KCCDI2";
$dbcur = $DGstbyLDA->prepare($sql);
}
## Otherwise, recovery SCN found on primary
else{
if($DGisLogical){
$sql = "SELECT MAX(FIRST_CHANGE#) FROM V\$ARCHIVED_LOG WHERE DICTIONARY_BEGIN = 'YES'";
}
else{
$sql = "SELECT MAX(NEXT_CHANGE#) FROM V\$ARCHIVED_LOG";
#$sql = "SELECT CHECKPOINT_CHANGE# FROM V\$DATABASE";
}
$dbcur = $DGprmyLDA->prepare($sql);
}
printDebug("dgcreate.DGgetRecoverySCN: $sql");
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
$DGpitSCN = $row[0];
debug("dgcreate.DGgetRecoverySCN: pitSCN: $DGpitSCN");
}
sub DGgetPrmyID{
my $sql = "SELECT DBID from v\$database";
printDebug("dgcreate.getPrmyID: $sql");
my $dbcur = $DGprmyLDA->prepare($sql);
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
my($dbID) = $row[0];
debug("dgcreate.getPrmyID: db ID: $dbID");
return($dbID);
}
sub DGstartLogical(){
debug("dgcreate.DGstartLogical: *** START ***");
my($dbID) = DGgetPrmyID();
my $sql = "Begin DBMS_LOGSTDBY.PRIMARY_DBID($dbID); End;";
printDebug("dgcreate.DGstartLogical: $sql");
my $dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
if($DGis10gOrHigher){
$sql = "ALTER DATABASE START LOGICAL STANDBY APPLY";
}
else{
$sql = "ALTER DATABASE START LOGICAL STANDBY APPLY INITIAL $DGpitSCN";
}
printDebug("dgcreate.DGstartLogical: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
$sql = "ALTER DATABASE STOP LOGICAL STANDBY APPLY";
printDebug("dgcreate.DGstartLogical: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
debug("dgcreate.startupLogical: *** END ***");
}
sub DGonlineDatafiles{
my($file, $dbcur);
foreach $file (@OFFLINE_FILES){
my($sql) = "ALTER DATABASE DATAFILE '$file' ONLINE";
printDebug("dgcreate.DGonlineDatafiles: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
}
sub DGrenameFiles{
my ($fromDatafiles, $toDatafiles, $fromLogfiles, $toLogfiles, $logGroups) = @_;
my(@fromDatafiles) = split /$DELIMITER/, $fromDatafiles;
my(@toDatafiles) = split /$DELIMITER/, $toDatafiles;
my(@fromLogfiles, @toLogfiles, @logGroups);
if($fromLogfiles){
@fromLogfiles = split /$DELIMITER/, $fromLogfiles;
}
if($toLogfiles){
@toLogfiles = split /$DELIMITER/, $toLogfiles;
}
if($logGroups){
@logGroups = split /$DELIMITER/, $logGroups;
}
my($from, $dbcur);
debug("dgcreate.DGrenameFiles: *** START ***");
my($count) = 0;
## rename datafiles
if($DGuseDuplicate eq 'Y') {
debug("dgcreate.DGrenameFiles: Use Duplicate - Skipping rename datafiles");
return;
}
foreach $from (@fromDatafiles){
my($to) = $toDatafiles[$count];
$count++;
## Skip rename if files are the same name
## (Case insensitive match for NT)
if(($from eq $to) || ($NT && (lc($from) eq lc($to)))){
debug("dgcreate.DGrenameFiles: Skipping rename for same name: $from $to");
next;
}
my $sql = "ALTER DATABASE RENAME FILE '$from' to '$to'";
printDebug("dgcreate.DGrenameFiles: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
## Bug 3736070: If the file is offline on the primary, take it offline
## on the standby so recovery doesn't try to recover it
$sql = "SELECT STATUS FROM V\$DATAFILE WHERE NAME = '$from'";
printDebug("dgcreate.DGrenameFiles: $sql");
$dbcur = $DGprmyLDA->prepare($sql);
$dbcur->execute;
my @row = $dbcur->fetchrow_array();
if($row[0] =~ /OFFLINE|RECOVER/i){
printDebug("dgcreate.DGrenameFiles: datafile $from is offline on primary; taking $to offline on standby");
push(@OFFLINE_FILES, $to);
$sql = "ALTER DATABASE DATAFILE '$to' OFFLINE DROP";
printDebug("dgcreate.DGrenameFiles: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
}
$count = 0;
foreach $from (@fromLogfiles){
my($to) = $toLogfiles[$count];
$count++;
## Skip rename if files are the same name
## (Case insensitive match for NT)
if(($from eq $to) || ($NT && (lc($from) eq lc($to)))){
debug("dgcreate.DGrenameFiles: Skipping rename for same name: $from $to");
next;
}
my $sql = "ALTER DATABASE RENAME FILE '$from' to '$to'";
printDebug("dgcreate.DGrenameFiles: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
debug("dgcreate.DGrenameFiles: Data/log file rename complete");
my($group, %donegroups);
foreach $group (@logGroups){
if(defined($donegroups{$group})){
printDebug("dgcreate.DGrenameFiles: already processed group $group");
next;
}
$donegroups{$group} = 1;
my $sql = "ALTER DATABASE CLEAR LOGFILE GROUP $group";
printDebug("dgcreate.DGrenameFiles: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
debug("dgcreate.DGrenameFiles: Log groups created");
}
## Shutdown the standby
sub DGshutdownStby
{
debug("dgcreate.DGshutdownStby: *** START ***");
## disconnect
$DGstbyLDA->disconnect;
push(@SQLCMDS, "shutdown immediate");
#executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS);
executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, '');
debug("dgcreate.DGshutdownStby: *** END ***");
undef @SQLCMDS;
}
## Shutdown the standby abort
sub DGshutdownStbyAbort
{
debug("dgcreate.DGshutdownStbyAbort: *** START ***");
## disconnect
$DGstbyLDA->disconnect;
push(@SQLCMDS, "shutdown abort");
#executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS);
executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, '');
debug("dgcreate.DGshutdownStbyAbort: *** END ***");
undef @SQLCMDS;
}
## Open resetlogs (logical only)
sub DGopenStby
{
debug("dgcreate.DGopenStby: *** START ***");
push(@SQLCMDS, "ALTER DATABASE OPEN RESETLOGS");
#executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS);
executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, '');
debug("dgcreate.DGopenStby: *** END ***");
undef @SQLCMDS;
}
## Startup the standby
## mount for logical
## nomount for physical
sub DGstartupStby
{
debug("dgcreate.DGstartupStby: *** START ***");
if($DGis10gOrHigher || $DGisLogical){
push(@SQLCMDS, "startup mount");
}
else{
push(@SQLCMDS, "startup nomount");
push(@SQLCMDS, "ALTER DATABASE MOUNT STANDBY DATABASE");
}
#executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, $DGstbyTNS);
executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, '');
## Reconnect to standby
DGsetStbyCreds();
debug("dgcreate.DGstartupStby: *** END ***");
undef @SQLCMDS;
}
## Startup the standby nomount
sub DGstartupStbyNomount{
debug("dgcreate.DGstartupStbyNomount: *** START ***");
push(@SQLCMDS, "startup nomount");
executeSQLPlusSYSDBA($DGstbyUser, $DGstbyPW, '');
## Reconnect to standby
DGsetStbyCreds();
debug("dgcreate.DGstartupStbyNomount: *** END ***");
undef @SQLCMDS;
}
sub DGbounceStby{
DGshutdownStby();
DGstartupStby();
}
sub DGcreateSpfile(){
debug("dgcreate.DGcreateSpfile: *** START ***");
my $sql = "CREATE SPFILE FROM PFILE='$DGIfile'";
printDebug("dgcreate.DGcreateSpfile: $sql");
my $dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
debug("dgcreate.DGcreateSpfile: *** END ***");
}
sub DGaddTempfiles{
debug("dgcreate.DGaddTempfiles: *** START ***");
my ($fileNames, $tablespaces, $sizes, $autoextends) = @_;
if($fileNames eq "$DELIMITER"){
debug("dgcreate.DGaddTempfiles: NO TEMPFILES TO ADD, returning");
return;
}
my @tempTablespaces = split /$DELIMITER/, $tablespaces;
my @tempFileNames = split /$DELIMITER/, $fileNames;
my @tempSizes = split /$DELIMITER/, $sizes;
my @tempAutoextends = split /$DELIMITER/, $autoextends;
my $tempFileNames;
my $index = 0;
my($sql, $dbcur);
foreach $tempFileNames (@tempFileNames)
{
$sql = "ALTER TABLESPACE $tempTablespaces[$index] ADD TEMPFILE '$tempFileNames' SIZE $tempSizes[$index] REUSE $tempAutoextends[$index]";
printDebug("dgcreate.DGaddTempfiles: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
$index++;
}
debug("dgcreate.DGaddTempfiles: *** END ***");
}
sub get_dbdisplayname
{
my ($lda, $object_id) = @_;
my($indoc);
my($outdoc);
my($pos);
$indoc = "";
$outdoc = get_dg_document($lda, $indoc);
$pos = -1;
my $retstr;
## First one is value_type=
$retstr = get_dg_token($outdoc, "value", $pos);
## Second one is value=
$retstr = get_dg_token($outdoc, "value", $pos);
## Need to remove %CREATING% (this does not get translated).
$pos = -1;
if (($pos = index($retstr, "%CREATING%", $pos)) = 0)
{
$retstr = substr($retstr, 10, length($retstr));
}
debug("dgcreate.get_dbdisplayname: $retstr");
return $retstr;
}
sub getNewIDs {
my $indoc="";
my $outdoc = get_dg_document($DGprmyLDA, $indoc);
my $pos = -1;
## First one is object_id=
$DGdbObjID = get_dg_token($outdoc, "object_id", $pos);
$indoc="";
$outdoc = get_dg_document($DGprmyLDA, $indoc);
## First one is object_id=
$pos = -1;
$DGsiteID = get_dg_token($outdoc, "object_id", $pos);
debug("dgcreate.getNewIDs: DGdbObjID: $DGdbObjID, DGsiteID: $DGsiteID");
}
sub _DGremoveResource{
my $indoc = "";
debug("dgcreate._DGremoveRes: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate._DGremoveRes: Database $DGresName removed");
}
sub _DGaddResource{
my $state = $_[0];
my $indoc = "";
debug("dgcreate._DGaddResource: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate._DGaddResource: Database $DGresName re-added");
}
sub _DGfixName{
my $name = $_[0];
my $indoc = "";
debug("dgcreate._DGfixName: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate._DGfixName: Display name set to $name for DB object");
}
sub _DGenable{
my $indoc = "";
debug("dgcreate._DGenable: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate._DGenable: Resource $DGresName enabled");
}
sub _DGdisable{
my $indoc = "";
debug("dgcreate._DGdisable: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate._DGdisable: $DGsiteID Disabled");
}
sub DGaddResource102{
my($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_;
my $apply_ready = "PHYSICAL-APPLY-READY";
my $state = "PHYSICAL";
## Start out in physical state
my $initstate = "PHYSICAL";
if($DGConvertToLogical || $DGisLogical){
$state = "LOGICAL";
}
my($indoc);
## For 10i, first remove the site (and database), then re-add it
## so that the property harvest happens
## Save the original displayname (NLS bug: 4342419)
$DGdisplayName = get_dbdisplayname($DGprmyLDA, $DGdbObjID);
debug("dgcreate.DGaddResource102: saving original displayname=$DGdisplayName");
## Remove the resource
_DGremoveResource();
## Add it back
_DGaddResource($initstate);
## Always get new site id, database id from database after you add a resource.
&getNewIDs;
## set LogShipping OFF.
$indoc = "";
debug("dgcreate.DGaddResource102: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate.DGaddResource102: LogShipping = OFF");
## Turn off all exception handling on the primary, turn on logging of errors;
## we don't want to error the whole job because of an enable problem
$DGprmyLDA->{RaiseError} = 0;
$DGprmyLDA->{PrintError} = 1;
## Give broker time to start on the standby
waitForDG($DGstbyLDA);
## Enable
_DGenable();
## Fix display name (add CREATING tag so it appears to be still creating in UI)
_DGfixName('%CREATING%'.$DGdisplayName);
## set GoOnline to PHYSICAL-APPLY-READY
$indoc = "";
debug("dgcreate.DGaddResource102: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate.DGaddResource102: GoOnline = $apply_ready.");
## set LogShipping ON.
$indoc = "";
debug("dgcreate.DGaddResource102: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate.DGaddResource102: LogShipping = ON");
## Disable
_DGdisable();
## Do recovery
DGrecoverStby102($tempNames);
## 10.2+ logical: convert from physical
if($DGConvertToLogical){
## Build dictionary on primary
my $sql = "begin DBMS_LOGSTDBY.BUILD; end;";
printDebug("dgcreate.DGaddResource102: $sql");
my $dbcur = $DGprmyLDA->prepare($sql);
$dbcur->execute;
## Recovery to current primary point already done, so this recovery
## will be garaunteed to go to the latest dict build done above
$sql = "ALTER DATABASE RECOVER TO LOGICAL STANDBY $newdbname";
printDebug("dgcreate.DGaddResource102: $sql");
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
## Bounce
DGbounceStby();
## Open resetlogs
DGopenStby();
}
## Add temp files (not necessary for duplicate)
if($DGuseDuplicate eq "N"){
if(($isOMF ne "Y") && ($isASM ne "Y")) {
DGaddTempfiles($tempNames, $tablespaces, $sizes, $autoextends);
}
else{
if($isASM eq "Y"){
addTempfilesOmf($tempNames, $tablespaces, $sizes, $autoextends);
}
else{
addTempfilesOmf("",$tablespaces, $sizes, $autoextends);
}
}
}
## Bounce
DGbounceStby();
## Open logical
if($DGConvertToLogical){
my $sql = "ALTER DATABASE OPEN";
printDebug("dgcreate.DGrecoverStby: $sql");
my $dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
## Remove the resource
_DGremoveResource();
## Add it back
_DGaddResource($state);
## Always get new site id, database id from database after you add a resource.
&getNewIDs;
## Give broker time to start on the standby
waitForDG($DGstbyLDA);
## Enable
_DGenable();
## Fix up the display name
_DGfixName($DGdisplayName);
## Convert to snapshot if specified
if($DGcreateSnapshot){
debug("dgcreate.DGaddResource102: Converting to snapshot");
$indoc = "";
debug("dgcreate.DGaddResource102: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate.DGaddResource102: Conversion to snapshot complete");
}
## Disable broker of no-broker option selected
if(!$DGuseBroker){
debug("dgcreate.DGaddResource102: Disabling broker");
$indoc = "";
debug("dgcreate.DGaddResource102: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate.DGaddResource102: Broker disabled");
my($sql) = "alter system set dg_broker_start=false";
printDebug("dgcreate.DGrecoverStby102: $sql");
my $dbcur = $DGprmyLDA->prepare($sql);
$dbcur->execute;
$dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
}
sub DGaddResource{
my($isOMF, $isASM, $tempNames, $tablespaces, $sizes, $autoextends, $newdbname) = @_;
my $apply_ready = "PHYSICAL-APPLY-READY";
my $state = "PHYSICAL";
## Start out in physical state
my $initstate = "PHYSICAL";
if($DGis10gOrHigher && ($DGConvertToLogical || $DGisLogical)){
$state = "LOGICAL";
}
else{
$state = "PHYSICAL-APPLY-ON";
if($DGisLogical){
$state = "LOGICAL-APPLY-ON";
}
}
my($indoc);
## For 10i, first remove the site (and database), then re-add it
## so that the property harvest happens
if($DGis10gOrHigher){
## Save the original displayname (NLS bug: 4342419)
$DGdisplayName = get_dbdisplayname($DGprmyLDA, $DGdbObjID);
debug("dgcreate.DGaddResource: saving original displayname=$DGdisplayName");
## Remove the resource
_DGremoveResource();
## Add it back
_DGaddResource($initstate);
## Always get new site id, database id from database after you add a resource.
&getNewIDs;
}
## For 9i, add the resource
else{
$DGresName = encode("UTF-8", $DGresName);
debug("converted to utf8: $DGresName");
$indoc = "";
debug("dgcreate.DGaddResource: $indoc");
DGprocessStatus(get_dg_document($DGprmyLDA, $indoc));
debug("dgcreate.DGaddResource: Resource $DGresName added");
}
## Turn off all exception handling on the primary, turn on logging of errors;
## we don't want to error the whole job because of an enable problem
$DGprmyLDA->{RaiseError} = 0;
$DGprmyLDA->{PrintError} = 1;
## Give broker time to start on the standby
waitForDG($DGstbyLDA);
## Enable
_DGenable();
## For 10i, fix up the display name
if($DGis10gOrHigher){
## Fix display name
_DGfixName($DGdisplayName);
}
}
sub DGprocessStatus{
my($status) = @_;
my @dbres_status_tokens = split(/[><]+/, $status);
# Token 0 is "", token 1 is "RESULT ", token 2 is "MESSAGE "
my($dbres_status) = $dbres_status_tokens[3];
my @temp_tokens = split(/\s+/, $dbres_status);
$dbres_status = $temp_tokens[0];
EMD_PERL_DEBUG("dbres_status=$dbres_status.");
if ($dbres_status eq "SUCCESS")
{
debug("dgcreate.DGprocessStatus: DG operation successful");
return;
}
else
{
# Parse out the error from the big status.
my $dbres_status_token;
my $token_count = 0;
my($dbres_status_text);
foreach $dbres_status_token (@dbres_status_tokens)
{
# EMD_PERL_DEBUG("dbres_status_token=$dbres_status_token.");
if ($dbres_status_token eq "ERROR_TEXT ")
{
$dbres_status_text = $dbres_status_tokens[$token_count + 1];
last;
}
$token_count++;
}
if ($dbres_status eq "WARNING"){
debug("dgcreate.DGprocessStatus: DG operation produced warning: $dbres_status_text");
}
elsif ($dbres_status eq "FAILURE"){
die("$dbres_status_text");
}
}
}
##########################################################
## Setup for standby creation
##########################################################
sub DGcreateAdmin{
debug("dgcreate.DGcreateAdmin: @_");
## globals
my($inst, $ohome, $listenerfile, $initfile, $clonetype, $conndesc, $dgopts, $use_duplicate, $use_rman_backup) = @_;
$DGInstance = $inst;
$DGOracleHome = $ohome;
## Used for broker add site connect string (initial connect desc)
$brokerConnDesc = $conndesc;
## ensure global clonePurpose is set in db_clone_10_2
setClonePurpose($clonetype);
## Harvest DG options
## 1) dev mode
## 2) site ID
## 3) DB resource name
my(@dgopts) = split(/$DELIMITER/, $dgopts);
# SJC 9/18/03: Disable development mode
#$DGdevMode = $dgopts[0];
$DGdevMode = 0;
$DGsiteID = $dgopts[1];
$DGresName = $dgopts[2];
## displayName is 10i only
$DGdisplayName = $dgopts[3];
## dbObjID is 10i only; distinct from siteID
$DGdbObjID = $dgopts[4];
## autoDelete is 10i only; determines if rec area uses auto deletion
$DGautoDelete = $dgopts[5];
debug("dgcreate.DGcreateAdmin: Auto delete = $DGautoDelete");
## Create logical by converting phys (10.2 and up)
$DGConvertToLogical = $dgopts[6];
if($DGConvertToLogical){
debug("dgcreate.DGcreateAdmin: Logical Standby 10.2+");
}
## Disable broker after creation (10.2 and up)
$DGuseBroker = $dgopts[7];
## Convert to snapshot after creation (11.1 and up)
$DGcreateSnapshot = $dgopts[8];
## Use RMAN duplicate (11g feature)
$DGuseDuplicate = $use_duplicate;
## Use RMAN existing backup (11g feature)
$DGuseRMANBackup = $use_rman_backup;
if($DGdevMode){
debug("dgcreate.DGcreateAdmin: Development mode");
}
elsif(-e "/tmp/dgNoNewID"){
debug("dgcreate.DGcreateAdmin: No NID mode");
$DGdevMode = 1;
}
$DGisLogical = 0;
if($clonetype =~ /LOGICAL_STANDBY/){
debug("dgcreate.DGcreateAdmin: Logical Standby");
$DGisLogical = 1;
}
else{
debug("dgcreate.DGcreateAdmin: Physical Standby");
}
debug("dgcreate.DGcreateAdmin: site ID: $DGsiteID");
debug("dgcreate.DGcreateAdmin: resource name: $DGresName");
## Figure out DB Version (primary and standby will be same)
my $dbver = get_dbversion($DGprmyLDA);
if($dbver =~ /^1[1-9]/){
debug("dgcreate.DGcreateAdmin: Database version: 11gR1 (or higher)");
$DGis11gOrHigher = 1;
$DGis102OrHigher = 1;
$DGis10gOrHigher = 1;
$DGdbVer = "11.1";
}
elsif($dbver =~ /^102/){
debug("dgcreate.DGcreateAdmin: Database version: 10gR2");
$DGis11gOrHigher = 0;
$DGis10gOrHigher = 1;
$DGis102OrHigher = 1;
$DGdbVer = "10.2";
}
elsif($dbver =~ /^(101|100)/){
debug("dgcreate.DGcreateAdmin: Database version: 10gR1");
$DGis11gOrHigher = 0;
$DGis102OrHigher = 0;
$DGis10gOrHigher = 1;
$DGdbVer = "10.1";
}
else{
debug("dgcreate.DGcreateAdmin: Database version: 9iR2");
$DGis11gOrHigher = 0;
$DGis102OrHigher = 0;
$DGis10gOrHigher = 0;
$DGdbVer = "9.2";
}
## Environmentals
## These should already have been reset from the in-job Perl
debug("ORACLE_HOME = $ENV{ORACLE_HOME}");
debug("PATH = $ENV{PATH}");
## Since the listener.ora file may not be the default one,
## set TNS_ADMIN to whatever directory the file is in
if(defined($listenerfile) && $listenerfile){
my($idx) = rindex($listenerfile,"$S");
if($idx == -1){
die("ERROR_TNS_ADMIN:$listenerfile");
return;
}
my($tns) = substr($listenerfile,0,$idx);
$ENV{TNS_ADMIN} = "$tns";
debug("TNS_ADMIN = $ENV{TNS_ADMIN}");
}
if(defined($initfile) && $initfile){
$DGIfile = $initfile;
$DGIfileForMV = substr($DGIfile, rindex($DGIfile,"$S") + 1);
## Beware that the value of $DGIfiletmp will be inferred as
## "${Ifile}tmp" by the client, so any changes here must be performed
## in the client code, too.
$DGIfiletmp1 = "${DGIfile}tmp1";
$DGIfiletmp2 = "${DGIfile}tmp2";
}
}
##########################################################
## Move the "real" init.ora file to the correct name
##########################################################
sub DGmvIfile{
local($SIG{'CHLD'}) = 'DEFAULT';
my($file) = $_[0];
## default: move temp file #1 to permanent slot
if(!defined($file)){
$file = $DGIfiletmp1;
}
debug("dgcreate.DGmvIfile: $_[0]");
my($cmd) = "$MV $file $DGIfile";
if($NT){
# For NT, first delete the destination file.
my($delcmd) = "del $DGIfile";
debug("\t$delcmd");
&DGrunCmd($delcmd);
$cmd = "$MV $file $DGIfileForMV";
}
debug("$cmd");
if(&DGrunCmd($cmd)){
return(1);
}
}
##########################################################
## Install a new init.ora file
##########################################################
sub DGputIfile{
debug("dgcreate.DGputIfile");
my($db_name) = @_;
my($line);
## Create main init.ora file for standby ($DGIfiletmp1). It will actually be
## created under the temporary file name, initially.
open(IFILETMP1,">$DGIfiletmp1") || die("ERROR_FILE_OPEN:$DGIfiletmp1");
## Create temp init.ora ($DGIfiletmp2) file w/o db_file_name_convert,
## log_file_name_convert, or standby_file_management. This is
## so standby can start w/o these params set in order to do the
## file renaming. Also, strip log_archive_format parameter, so
## that if a manual recovery is done by the create process, a
## user specified format doesn't get in the way (log_archive_format
## will be preserved in the real spfile).
open(IFILETMP2,">$DGIfiletmp2") || die("ERROR_FILE_OPEN:$DGIfiletmp2");
## Open the file copied over by DB Clone for reading, from which
## the above two files will be created.
open(IFILE,"$DGIfile") || die("ERROR_FILE_OPEN:$DGIfile");
while($line = ){
chomp($line);
## For Logical, substitute the new db_name, so it will match
## the name set by the NID utility
if((defined($db_name)) && $line =~ /^\s*(db_name)/i){
debug("\tadding new db_name=$db_name for logical stby");
print IFILETMP1 "db_name=$db_name\n";
## Keep old db_name in tempfile
print IFILETMP2 "$line\n";
next;
}
## remove lock_name_space from main file; don't need it after db name change
elsif((defined($db_name)) && $line =~ /^\s*lock_name_space/i){
print IFILETMP2 "$line\n";
next;
}
## remove db/log_file_name_convert from temp file
if($line =~ /^\s*(db_file_name_convert|log_file_name_convert|standby_file_management)/i){
print IFILETMP1 "$line\n";
next;
}
## for 11g or higher audit_trail default is set to DB.
## In order to open the database in read only mode, we need to set this parameter to NONE/FALSE.
if($line =~ /^\s*(audit_trail)/i){
print IFILETMP1 "audit_trail=NONE\n";
next;
}
print IFILETMP1 "$line\n";
print IFILETMP2 "$line\n";
}
## Bug 5162803: Set log_file_name_convert to empty string in the temp
## init file that will be used during the file rename. This is necessary
## to avoid ORA-19527 during CLEAR LOGFILE if the "keep primary names"
## option was used to create the standby.
## Bug 5624530: Empty strings cause ORA-09134 on Windows; use a dummy string
print IFILETMP2 "log_file_name_convert=\'a\'\,\'a\'\n";
close(IFILE);
close(IFILETMP1);
close(IFILETMP2);
## Now that we're done producing the two init.ora files, move
## the temp one into place.
## Return with no output if error; mvIfile will output an error messages
if(&DGmvIfile("$DGIfiletmp2")){
return;
}
}
##########################################################
## Create a remote login passwordfile
##########################################################
sub DGorapwd{
local($SIG{'CHLD'}) = 'DEFAULT';
debug("dgcreate.DGorapwd");
my($password);
my($pwfile) = "${DGOracleHome}${S}dbs${S}orapw${DGInstance}";
my($orapwexec) = "${DGOracleHome}${S}bin${S}orapwd";
if($NT){
$pwfile = "${DGOracleHome}${S}database${S}PWD${DGInstance}.ora";
$orapwexec = "${DGOracleHome}${S}bin${S}orapwd.exe";
}
my($pwcmd);
if($DGis102OrHigher){
$password = $DGParam1;
$pwcmd = "$orapwexec file=$pwfile password=$password entries=10 -ob";
}
else{
$password = $DGstbyPW;
$pwcmd = "$orapwexec file=$pwfile password=$password entries=10";
}
## If the executable is there, and the file isn't, create it
if((-e "$orapwexec") && !(-e "$pwfile")){
if(&DGrunCmd($pwcmd)){
exit(1);
}
debug("Created file $pwfile");
}
## else, if file is there, remove it and recreate
elsif(-e "$pwfile"){
debug("Removing file $pwfile");
if(unlink($pwfile)){
if(&DGrunCmd($pwcmd)){
exit(1);
}
debug("Created file $pwfile");
}
## If remove fails, that's an error
else{
die("ERROR_RM_FILE:$pwfile");
}
}
##
## else, there's a problem (probably no orapwd exec)
else{
die("ERROR_EXEC:orapwd");
}
}
##########################################################
## Run the NID utility to change the database name. (Called
## only to change dbname for a logical standby.)
##########################################################
sub DGnid{
local($SIG{'CHLD'}) = 'DEFAULT';
debug("dgcreate.DGnid: $_[0]");
my($dbname) = @_;
my($nidexec) = "${DGOracleHome}${S}bin${S}nid";
if($NT){
$nidexec = "${DGOracleHome}${S}bin${S}nid.exe";
}
## If a connect alias isn't specified, NID will connect
## to whatever ORACLE_SID is set to
$ENV{ORACLE_SID} = "$DGInstance";
my($cmd) = "echo Y | $nidexec TARGET=/ DBNAME=$dbname";
debug("$cmd");
## If the executable is there, and the file isn't, create it
if(-e "$nidexec"){
if(&DGrunCmd($cmd)){
exit(1);
}
}
else{
die("ERROR_EXEC:nid");
}
}
##########################################################
## Remove the SPFILE file for an instance
##
## Since this is run at create time, the globals $DGInstance and
## $DGOracleHome will be set when it's called.
##########################################################
sub DGrmInitFiles{
debug("dgcreate.DGrmInitFiles");
my($file) = "${DGOracleHome}${S}dbs${S}spfile${DGInstance}.ora";
if($NT){
$file = "${DGOracleHome}${S}database${S}spfile${DGInstance}.ora";
}
debug("\tlooking for $file");
if(-e "$file"){
debug("\tremoving file for for ${DGInstance}: $file");
unlink($file)
|| die("ERROR_RM_FILE:$file");
}
}
##########################################################
## Setup the arch log file auto deletion policy using RMAN
## $DGautoDelete global set in createAdmin
##########################################################
sub DGsetAutoDelete{
local($SIG{__WARN__}) = 'DEFAULT';
debug("dgcreate.DGsetAutoDelete");
## Logical auto delete feature (10.2 only); always set this
## (Actually not necessary since server will do by default, but leave it)
if($DGisLogical && ($DGis102OrHigher)){
my $sql = "Begin DBMS_LOGSTDBY.APPLY_SET('LOG_AUTO_DELETE','TRUE'); End;";
printDebug("dgcreate.DGsetAutoDelete: $sql");
my $dbcur = $DGstbyLDA->prepare($sql);
$dbcur->execute;
}
## Until setting-up rec area deletion policy makes sense for logical,
## skip it. (Not done at all pre-10g.)
if($DGisLogical || $DGConvertToLogical || !$DGis10gOrHigher || !$DGautoDelete){
return;
}
## Set these globals to avoid compile warnings in rman_o.pl
$db_username = "";
$rcvcat_username = "";
$rcvcat_connect_string = "";
$dbid = 0;
$db_role = "";
$rman_command_id = "";
$is_repos_database = "";
$rin = "";
$rout = "";
$target_home = $DGOracleHome;
$target_sid = $DGInstance;
$db_10_or_higher = "YES";
$rman_script = "CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;";
## Don't want fatal error here to fail the job; trap any errors
my($status) = eval{br_rman("NO", "");};
if($@){
debug("dgcreate.DGsetAutoDelete: RMAN script failed: $@");
}
debug("dgcreate.DGsetAutoDelete: RMAN returned $status");
}
sub DGrunCmd{
local($SIG{'CHLD'}) = 'DEFAULT';
my($cmd) = $_[0];
my(@res) = `$cmd 2>&1`;
debug("@res ");
if($?){
my($err) = $!;
## If there is no system error, just put out the output
if($err eq undef || ($err eq "")){
$err = "@res";
## To make multiline errors fit on one line (so it
## makes it back to the client) replace all \n with tabs.
## The client will in turn replace the tabs with newlines
## before presenting the error to the user.
$err =~ s/\n/\t/g;
}
chomp($err);
## Send back only the OS components of error to client
## to minimize translation issues
if($cmd !~ /password/i){
printDebug("${cmd}: $err");
}
return(1);
}
return(0);
}
# Migrate online logs to ASM with hot migration approach
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# migrateOnlineLogsHot()
sub DGmigrateOnlineLogs
{
EMD_PERL_DEBUG("dgcreate_o.DGMigrateOnlineLogs(): *** START ***");
my $sql_string = "";
$sql_string .= "set echo off\n";
$sql_string .= "set serveroutput on;\n";
$sql_string .= "declare\n";
$sql_string .= " cursor logfile_cur is\n";
$sql_string .= " select l.thread#, l.group#, l.bytes\n";
$sql_string .= " from v\$log l;\n";
$sql_string .= " type numTab_t is table of number index by binary_integer;\n";
$sql_string .= " grouplist numTab_t;\n";
$sql_string .= " threadlist numTab_t;\n";
$sql_string .= " byteslist numTab_t;\n";
$sql_string .= " procedure migrateorl ( thread# IN number, group# IN number, bytes IN number) is\n";
$sql_string .= " retry number := 5;\n";
$sql_string .= " stmt varchar2(1024);\n";
$sql_string .= " als varchar2(1024) := 'alter system archive log current';\n";
$sql_string .= " chk varchar2(1024) := 'alter system checkpoint global';\n";
$sql_string .= " begin\n";
$sql_string .= " stmt := 'alter database add logfile thread '||thread#||' size '||bytes;\n";
$sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n";
$sql_string .= " execute immediate stmt;\n";
$sql_string .= " stmt := 'alter database drop logfile group '||group#;\n";
$sql_string .= " for I in 1 .. retry loop\n";
$sql_string .= " begin\n";
$sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n";
$sql_string .= " execute immediate stmt;\n";
$sql_string .= " exit;\n";
$sql_string .= " exception\n";
$sql_string .= " when others then\n";
$sql_string .= " if I > retry then\n";
$sql_string .= " raise;\n";
$sql_string .= " end if;\n";
$sql_string .= " dbms_output.put_line('Execute stmt: ' || als);\n";
$sql_string .= " execute immediate als;\n";
$sql_string .= " dbms_output.put_line('Execute stmt: ' || chk);\n";
$sql_string .= " execute immediate chk;\n";
$sql_string .= " end;\n";
$sql_string .= " end loop;\n";
$sql_string .= " end;\n";
$sql_string .= "begin\n";
$sql_string .= " open logfile_cur;\n";
$sql_string .= " fetch logfile_cur bulk collect into threadlist, grouplist, byteslist;\n";
$sql_string .= " close logfile_cur;\n";
$sql_string .= " for I in 1 .. threadlist.count loop\n";
$sql_string .= " migrateorl (threadlist(I), grouplist(I), byteslist(I) );\n";
$sql_string .= " end loop;\n";
$sql_string .= "end;\n";
$sql_string .= "/\n";
$sql_string .= "EXIT;\n\n";
(my $fh, my $filename) = &runSqlOnDestination($sql_string);
close $fh;
if($NT)
{
&removeFile($filename);
}
EMD_PERL_DEBUG("dgcreate_o.DGMigrateOnlineLogs(): *** END ***");
}
# Migrate online logs to ASM for standby database with hot migration approach
# Call &set_env($oracleHome, $oracleSid) before calling this method.
# migrateOnlineLogsStandbyHot()
sub DGmigrateStandbyLogs
{
EMD_PERL_DEBUG("dgcreate_o.DGmigrateStandbyLogs(): *** START ***");
my $sql_string = "";
$sql_string .= "set echo off\n";
$sql_string .= "set serveroutput on;\n";
$sql_string .= "declare\n";
$sql_string .= " cursor logfile_cur is\n";
$sql_string .= " select l.thread#, l.group#, l.bytes\n";
$sql_string .= " from v\$standby_log l;\n";
$sql_string .= " type numTab_t is table of number index by binary_integer;\n";
$sql_string .= " grouplist numTab_t;\n";
$sql_string .= " threadlist numTab_t;\n";
$sql_string .= " byteslist numTab_t;\n";
$sql_string .= " procedure migratesrl ( thread# IN number, group# IN number, bytes IN number) is\n";
$sql_string .= " retry number := 5;\n";
$sql_string .= " stmt varchar2(1024);\n";
$sql_string .= " begin\n";
$sql_string .= " if thread# = 0 then\n";
$sql_string .= " stmt := 'alter database add standby logfile size '||bytes;\n";
$sql_string .= " else\n";
$sql_string .= " stmt := 'alter database add standby logfile thread '||thread#||' size '||bytes;\n";
$sql_string .= " end if;\n";
$sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n";
$sql_string .= " execute immediate stmt;\n";
$sql_string .= " stmt := 'alter database drop standby logfile group '||group#;\n";
$sql_string .= " dbms_output.put_line('Execute stmt: ' || stmt);\n";
$sql_string .= " execute immediate stmt;\n";
$sql_string .= " dbms_backup_restore.sleep(30);\n";
$sql_string .= " end;\n";
$sql_string .= "begin\n";
$sql_string .= " open logfile_cur;\n";
$sql_string .= " fetch logfile_cur bulk collect into threadlist, grouplist, byteslist;\n";
$sql_string .= " close logfile_cur;\n";
$sql_string .= " for I in 1 .. threadlist.count loop\n";
$sql_string .= " migratesrl (threadlist(I), grouplist(I), byteslist(I) );\n";
$sql_string .= " end loop;\n";
$sql_string .= "end;\n";
$sql_string .= "/\n";
$sql_string .= "EXIT;\n\n";
(my $fh, my $filename) = &runSqlOnDestination($sql_string);
close $fh;
if($NT)
{
&removeFile($filename);
}
EMD_PERL_DEBUG("dgcreate_o.DGmigrateStandbyLogs(): *** END ***");
}