Home > Oracle Database / Applications Tips > Oracle database administrator > Hot backup and recovery with Perl
Oracle Tips:
EMAIL THIS
 TIPS & NEWSLETTERS TOPICS 

ORACLE DATABASE ADMINISTRATOR

Hot backup and recovery with Perl


FRANK PANTALEO
01.29.2003
Rating: --- (out of 5)


Digg This!    StumbleUpon Toolbar StumbleUpon    Bookmark with Delicious Del.icio.us   


Updated January, 2003.

This script is very useful for DBAs who want to use Perl for doing hot backups. Perl DBI/DBD provides very close integration and control of the state of the database. It also allows real time queries that allow the script to be self-learning about what dbf's are in the database. It has been tested on 8.1.7 and 9i. This script was originally written to invoke SQL*Plus. I have updated it to use zlib and DBI/DBD connections into Oracle to control the backup.

#!/usr/bin/perl
# ------------------------------------------------------------------------
# hotarch -- hotarchive backup for Microsoft copied from ye olde shell script 
# G. Patterson, Nov 2001
#
# 9/2002          Enhanced by Frank Pantaleo (The Rochester Group) to use DBI 9/2002
# 1/2003          add Parallelization of dbf's relative to the tablespace 
# 1/17/2003       Code Cleanup and add more verbose messages
#                 Deal with gzip return codes better and a bad connection in middle of hotbackup
# 
use File::Basename; 
use File::Copy; 
use Parallel::ForkManager; 
use DBI; use Compress::Zlib; 
use File::Slurp; 
$MyPath = dirname($0); 
$return_code = 0;
 
# ------------------------------------------------------------------------
sub LogMsg{
        my @t = localtime(time);
        printf LOG "%04d-%02d-%02d %02d:%02d %sn",
                $t[5]+1900,$t[4]+1,$t[3],$t[2],$t[1],$_[0];
}
 
sub Compress {
    $in_file=$_[0];
    $out_file=$_[1];
    $status=0;
    open(OUTFILE,'>',$out_file) or die "could not open $out_file";
    binmode INFILE;
    binmode OUTFILE;
 
    my $gz = gzopen(*OUTFILE, "wb") or die "Cannot open stdout: $gzerrnon" ;
 
    $input = read_file($in_file);
    $gz->gzwrite($input) ;
    $gz->gzclose ;
 
    close OUTFILE;
    return $gz->gzerror;
}
# ------------------------------------------------------------------------
sub OraDo {
    ($thesql) = $_[0];
    local($sthx);
    $status=0;
    print "OraDo $thesqln";
    if (($dbh) && ($dbh->ping)) {}
    else {
        $dbh = DBI->connect("dbi:Oracle:", '', '',{ PrintError=>0, RaiseError =>0, ora_session_mode => 2}) or  wrapup(6);
    }
    $sthx = $dbh->prepare($thesql) or wrapup(5); 
    if ($DBI::errstr) {
            LogMsg "$thesqlnParse Error $DBI::errstrn";
            $status=-1;
            }
    $sthx->execute or wrapup(5); 
    if ($DBI::errstr) {
            LogMsg "$thesqlnExec Error $DBI::errstrn";
            $status=-1;
            }
    $sthx->finish;
    return $status;
}
# ------------------------------------------------------------------------
sub OraQry {
    ($thesql) = $_[0];
    $status=0;
    print "OraQry $thesqln";
    if ($dbh->ping) {}
    else {
        $dbh = DBI->connect("dbi:Oracle:", '', '',{ PrintError=>0, RaiseError =>0,ora_session_mode => 2}) or  wrapup(6);
    }
    $sth1 = $dbh->prepare($thesql) or wrapup(5);
    if ($DBI::errstr) {
            LogMsg "$thesqlnParse Error $DBI::errstrn";
            $status=-1;
            }
    $sth1->execute or wrapup{5};
    if ($DBI::errstr) {
            LogMsg "$thesqlnExec Error $DBI::errstrn";
            $status=-1;
            }
    return $status;
}
sub OraQry2 {
    ($thesql) = $_[0];
    $status=0;
    print "OraQry2 $thesqln";
    if ($dbh->ping) {}
    else {
        $dbh = DBI->connect("dbi:Oracle:", '', '',{ PrintError=>0, RaiseError =>0, ora_session_mode => 2}) or wrapup(6); 
    }
    $sth2 = $dbh->prepare($thesql) or wrapup(5);
    if ($DBI::errstr) {
        LogMsg "$thesqlnParse Error $DBI::errstrn";
        $status=-1;
        }
    $sth2->execute or wrapup{5};
    if ($DBI::errstr) {
        LogMsg "$thesqlnExec Error $DBI::errstrn";
        $status=-1;
        }
    return $status;
}
 
# ------------------------------------------------------------------------
sub stop_backup{
#
# just in case previous hotarch died an untidy death
#
            local($SQL)=<ping)) {
                        $dbh->disconnect;
            }
            if ($myrtn < $return_code) {
                        $myrtn = $return_code;
            }
        if (($return_code == 0) && ($myrtn == 0)) {
                LogMsg "SUCCESS: $MyName completed";
        }
        else {
                LogMsg "FAILURE: $MyName errors in logfile";
        }
            close LOG;
        unlink $lockfile;
        unlink $stopfile;
            exit($myrtn);
}
# ------------------------------------------------------------------------
 
sub get_log_info{
            $SQL = 'select log_mode from v_$database';
            $rtn=OraQry($SQL);
            ($arch_mode) = $sth1->fetchrow_array;          
            $sth1->finish;
            $rtn=OraQry('select archiver from v_$instance');
            ($archiver) = $sth1->fetchrow_array;
            $sth1->finish;
            $rtn=OraQry('select value from v_$parameter where name = ' . "'" . 'log_archive_dest' . "'");
            ($arcdir) = $sth1->fetchrow_array;
            $sth1->finish;
            $rtn=OraQry('select max(recid) from v_$archived_log where ' . " archived = 'YES'");
            ($log_cur_seq) = $sth1->fetchrow_array;
            $sth1->finish;
            $rtn=OraQry('select sequence# from v_$log where ' . "archived = 'NO'");
            ($next_archive) =$sth1->fetchrow_array;
            $sth1->finish;
}
 
# ------------------------------------------------------------------------
 
sub get_oraparm{
# retrieve parameter values from v$parameter
            $SQL = 'select name from v_$database';
            $rtn=&OraQry($SQL);
            ($orasid) = $sth1->fetchrow_array;
            $sth1->finish;
}
 
# ------------------------------------------------------------------------
 
sub backup_mode{
# alter tablespace begin/end backup
            my ($tsname,$mode) = $_[0], $_[1];
            if($tsname) {
                        $SQL="alter tablespace $_[0] $_[1] backup";
                        $rtn=OraDo($SQL);
                        LogMsg "tablespace $_[0] changed $_[1]";
            }
            else {
            return -1;
            }          
}
 
# ------------------------------------------------------------------------
 
sub backup_init{
            $BKUPDIR= $ENV{BKUPDIR} or die "Environment missing BKUPDIR can not run!";
            get_oraparm();
            get_log_info();
            @start = localtime(time);
            $start_str = sprintf "%04d-%02d-%02d %02d:%02d:%02d",$start[5]+1900,
                                    $start[4]+1,$start[3],$start[2],$start[1],$start[0];
            $logfile = sprintf "%s/%s_%s_%02d%02d%02d.log",$BKUPDIR,$orasid,$MyName,
                                    $start[5]%100,$start[4]+1,$start[3];
            open(LOG,">$logfile") || die "error opening $logfilen";
            LogMsg "$MyName started at $start_str ORACLE_SID: 
                        $orasid ORACLE_HOME: $ENV{ORACLE_HOME} BLOCK_SIZE: 
                        $db_block_size START_SEQNO: $log_cur_seq Archive Mode: $arch_mode";
            $lockfile = "$BKUPDIR/hotarch.$orasid.lock";
            $stopfile = "$BKUPDIR/hotarch.$orasid.stop";
            if ((-f $lockfile) && (-M $lockfile < 1)) {
                        LogMsg "$MyName is already running";
                        wrapup(5);
                        }          
            open (LOCK,">$lockfile") || die "Cannot write to $lockfile";
            print LOCK "$MyName started $start_strn";
            close (LOCK) || die "$!";
            #
            if ($arch_mode =~ "ARCHIVELOG") {}
            else {
                        LogMsg "Error! No Archive Mode";
                        wrapup(5);
                        }
            if ($archiver =~ "STARTED") {}
            else {
                        LogMsg "Error! Archive disabled";
                    wrapup(5);
                        }
            if ($arcdir) {}
            else {
                        LogMsg "Error! No archive log destination";
                        wrapup(5);
                        }
            stop_backup();
}
 
# ------------------------------------------------------------------------
 
sub backup_control_file{
# create a backup copy of the control file
            my $backup_ctrl = "$BKUPDIR" . '/' . uc($orasid) . "_CTRL.FILE";
            unlink "$backup_ctrl";
            $SQL = "alter database backup controlfile to '$backup_ctrl'";
            $rtn=OraDo($SQL);
}
 
# ------------------------------------------------------------------------
sub backup_dbfs {
 
            my %ts_array;
            $pm = new Parallel::ForkManager(40);
            $pm->run_on_start( sub { my ($pid, $ident) = @_; print "** $ident just got in the pool with PID $pidn"; });
            $pm->run_on_finish( 
                        sub { my ($pid, $exit_code, $ident) = @_; 
                                    print "** $ident just got out of the pool with PID $pid and exit code: $exit_coden"; 
                                    if ($exit_code != 0) {
#
#       If there was a problem with a compress of a dbf
#       its all over but the crying. Get out now.
#
                                                $return_code=1;
                                                problem('$exit_code Compression failure on dbf');
                                    }
                        });
 
        $rtn=OraQry2("select distinct(tablespace_name) from dba_data_files where status ='AVAILABLE'");
        while (($ts_name) = $sth2->fetchrow_array) {
                $ts_array{$ts_name}= "$ts_name";
        }
        $sth2->finish;
 
        while (($ts_name,$value) = each %ts_array)      {
                        backup_mode $ts_name,"BEGIN";
                        $SQL= "select file_name from dba_data_files where status = 'AVAILABLE' and tablespace_name = '$ts_name' order by tablespace_name, file_name";
                        $rtn=OraQry($SQL);
                        while( ($file_name) = $sth1->fetchrow_array ) {
                                    LogMsg "compressing $ts_name, $file_name";
                                    ($base_name)=basename($file_name);
                                    print "Queing compress of $file_namen";
                                    my $pid = $pm->start and next;            
 
                                    $in_file=$file_name;
                                    $out_file=$BKUPDIR . '/' .  $base_name . '.gz';
                                    open(OUTFILE,'>',$out_file) or die "could not open $out_file";
                                    binmode INFILE;
                                    binmode OUTFILE;
 
                                    my $gz = gzopen(*OUTFILE, "wb") or die "Cannot open stdout: $gzerrnon" ;
                                    $input = read_file($in_file);
                                    $gz->gzwrite($input) or die "Error compressing file $in_filen" ;
                                    $gz->gzclose ;
 
                                    close OUTFILE;
 
                                    $rtn = $gz->gzerror;
 
                                    if ($rtn == Z_OK or $rtn == Z_STREAM_END) {
                                                $pm->finish(0);
                                    }
                                    else {
                                                $pm->finish($rtn);
                                    }
 
                        }
                        $pm->wait_all_children;
                        backup_mode $ts_name,"END";
            }
}
# ------------------------------------------------------------------------
sub switch_log{
# switch_log files ... then wait for the archiver to catch up
            $SQL = "alter system switch logfile";
            $rtn=OraDo($SQL);
            $SQL = "alter system switch logfile";
            $rtn=OraDo($SQL);
            do {
                        OraQry('select max(recid) from v_$archived_log where ' . " archived = 'YES'");
                        ($log_nxt_seq) = $sth1->fetchrow_array;
                        $sth1->finish;
                        sleep(60);
            } until $log_nxt_seq > $log_cur_seq;
}
 
# ------------------------------------------------------------------------
 
sub copy_log_files{
            my $i;
            my $next_log = $next_archive;
        $SQL='select name from v$archived_log where ' . " ARCHIVED = 'YES'";
        &OraQry($SQL);
        $df_cnt = 0;
        while( ($file_name) = $sth1->fetchrow_array ) {
                        ($base_name)=basename($file_name);
                if (-r "$arcdir/$base_name") {
                        $df_cnt++;
                        LogMsg "compressing & moving $file_name";
                                    $rtn=Compress($arcdir . '/' . $base_name,$BKUPDIR . '/' . $base_name . '.gz');
                        if ($rtn == Z_OK or $rtn = Z_STREAM_END  ) {
                                                unlink "$arcdir/$base_name";
                        }
                                    else {
                                                LogMsg "error compressing $file_name";
                                                $return_code=1;
                                                }
                        }
                        else {
                                    LogMsg "warning: archive log $file_name not here";
                        }
        }
        $sth1->finish;
}
 
# ------------------------------------------------------------------------
 
# Main ... start here:
            $MyName = basename $0,".pl";
            $dbh = DBI->connect("dbi:Oracle:", '', '',{ PrintError=>0, RaiseError =>0, ora_session_mode => 2}) or  die "Login error to db";
            backup_init();
            backup_dbfs();
            stop_backup();
            backup_control_file();
            copy ($ENV{ORACLE_HOME} . '/dbs/init' . $orasid . '.ora',$BKUPDIR);
            if ($!) {
                        LogMsg "Error copy " . $ORACLE_HOME . '/dbs/init' . $orasid . '.ora' . " $!. Return code was $? ";
            }
            switch_log();
            copy_log_files();
            wrapup(0);
--- end ---

For More Information

  • Feedback: E-mail the editor with your thoughts about this tip.
  • More tips: Hundreds of free Oracle tips and scripts.
  • Tip contest: Have an Oracle tip to offer your fellow DBAs and developers? The best tips submitted will receive a cool prize -- submit your tip today!
  • Ask the Experts: Our SQL, database design, Oracle, SQL Server, DB2, metadata, and data warehousing gurus are waiting to answer your toughest questions.
  • Forums: Ask your technical Oracle questions--or help out your peers by answering them--in our active forums.
  • Best Web Links: Oracle tips, tutorials, and scripts from around the Web.

Rate this Tip
To rate tips, you must be a member of SearchOracle.com.
Register now to start rating these tips. Log in if you are already a member.




Digg This!    StumbleUpon Toolbar StumbleUpon    Bookmark with Delicious Del.icio.us   



RELATED CONTENT
Hot backups
Hot backups of Oracle E-Business Suite
Questions about user-managed hot backups
Taking a hot backup on Linux
Performing a hot backup with RMAN
Questions about backup and recovery
Advantages of using RMAN in a 24x7 environment
Step-by-step backup procedures
Try this simple hot backup script
Oracle backup and recovery scripts, part III: Backup and recovery under Windows NT
Oracle backup and recovery scripts, part II: Hot backup under Unix

Oracle database administrator
Understanding SQL string functions
What is the difference between a database engineer, architect and administrator?
Import on one table from dump file
Error during RMAN backup
Can I drop a column in SYS schema?
STATSPACK tool: transaction vs. execution measurement
Should I port from Microsoft Access?
How can I find statistics on total memory usage and database connections?
Installing multiple Oracle homes
Modifying SYS password in a RAC environment

RELATED RESOURCES
2020software.com, trial software downloads for accounting software, ERP software, CRM software and business software systems
Search Bitpipe.com for the latest white papers and business webcasts
Whatis.com, the online computer dictionary

DISCLAIMER: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.



Oracle Development Solutions - SQL, J2EE, XML, SOA
HomeNewsTopicsTipsAsk the ExpertsMultimediaWhite PapersProductsBlogs
About Us  |  Contact Us  |  For Advertisers  |  For Business Partners  |  Site Index  |  RSS
SEARCH 
TechTarget provides technology professionals with the information they need to perform their jobs - from developing strategy, to making cost-effective purchase decisions and managing their organizations' technology projects - with its network of technology-specific websites, events and online magazines.

TechTarget Corporate Web Site  |  Media Kits  |  Site Map




All Rights Reserved, Copyright 2003 - 2009, TechTarget | Read our Privacy Policy
  TechTarget - The IT Media ROI Experts