Properties

Name: DBMS_RCVCAT 
Status: VALID 
Last modify: 05/12/2002 
Created: 05/12/2002 

Procedures

Name Overload
  BEGINCKPT 
  BSSTATUSRECALC 
  CANCELCKPT 
  CHANGEARCHIVEDLOG 
  CHANGEBACKUPPIECE 
  CHANGEBACKUPSET 
  CHANGECONTROLFILECOPY 
  CHANGEDATAFILECOPY 
  CHANGEPROXYCOPY 
  CHECKARCHIVEDLOG 
  CHECKBACKUPCORRUPTION 
  CHECKBACKUPDATAFILE 
  CHECKBACKUPPIECE 
  CHECKBACKUPREDOLOG 
  CHECKBACKUPSET 
  CHECKBACKUPSPFILE 
  CHECKCOPYCORRUPTION 
  CHECKDATAFILE 
  CHECKDATAFILECOPY 
  CHECKDELETEDOBJECT 
  CHECKLOGHISTORY 
  CHECKOFFLINERANGE 
  CHECKONLINEREDOLOG 
  CHECKPROXYDATAFILE 
  CHECKTABLESPACE 
  CHECKTHREAD 
  COMMITCHANGES 
  CREATESCRIPT 
  DELETECONFIG 
  DELETESCRIPT 
  ENDARCHIVEDLOGRESYNC 
  ENDBACKUPCORRUPTIONRESYNC 
  ENDBACKUPDATAFILERESYNC 
  ENDBACKUPPIECERESYNC 
  ENDBACKUPREDOLOGRESYNC 
  ENDBACKUPSETRESYNC 
  ENDBACKUPSPFILERESYNC 
  ENDCKPT 
  ENDCONFIGRESYNC 
  ENDCOPYCORRUPTIONRESYNC 
  ENDDATAFILECOPYRESYNC 
  ENDDATAFILERESYNC 
  ENDDELETEDOBJECTRESYNC 
  ENDINCARNATIONRESYNC 
  ENDLOGHISTORYRESYNC 
  ENDOFFLINERANGERESYNC 
  ENDONLINEREDOLOGRESYNC 
  ENDPROXYRESYNC 
  ENDTABLESPACERESYNC 
  ENDTHREADRESYNC 
  GETCONFIG 
  GETSCRIPT 
  LOCKFORCKPT 
  PUTLINE 
  REGISTERDATABASE 
  RENORMALIZE 
  REPLACESCRIPT 
  RESETCONFIG 
  RESETDATABASE 
  RESETDATABASE 
  SANITYCHECK 
  SETCLONENAME 
  SETCONFIG 
  SETDATABASE 
  SETDATABASE 
  SETDATABASE 
  SETDATAFILESIZE 
  SETDEBUGOFF 
  SETDEBUGON 
  UNREGISTERDATABASE 
  UPDATEBACKUPSETREC 
Total: 71 procedure(s)

Functions

Name Overload Type Length Precision
  BEGINARCHIVEDLOGRESYNC  NUMBER  22   
  BEGINBACKUPCORRUPTIONRESYNC  NUMBER  22   
  BEGINBACKUPDATAFILERESYNC  NUMBER  22   
  BEGINBACKUPPIECERESYNC  NUMBER  22   
  BEGINBACKUPREDOLOGRESYNC  NUMBER  22   
  BEGINBACKUPSETRESYNC  NUMBER  22   
  BEGINBACKUPSPFILERESYNC  NUMBER  22   
  BEGINCONFIGRESYNC  NUMBER  22   
  BEGINCOPYCORRUPTIONRESYNC  NUMBER  22   
  BEGINDATAFILECOPYRESYNC  NUMBER  22   
  BEGINDATAFILERESYNC  PL/SQL BOOLEAN     
  BEGINDELETEDOBJECTRESYNC  NUMBER  22   
  BEGININCARNATIONRESYNC  NUMBER  22   
  BEGINLOGHISTORYRESYNC  NUMBER  22   
  BEGINOFFLINERANGERESYNC  NUMBER  22   
  BEGINONLINEREDOLOGRESYNC  PL/SQL BOOLEAN     
  BEGINPROXYRESYNC  NUMBER  22   
  BEGINTABLESPACERESYNC  PL/SQL BOOLEAN     
  BEGINTHREADRESYNC  PL/SQL BOOLEAN     
  CHECKINCARNATION  NUMBER  22   
  CKPTNEEDED  NUMBER  22   
  GETCATALOGVERSION  VARCHAR2     
  GETCLONENAME  VARCHAR2     
  GETDBID  NUMBER  22   
  GETLINE  VARCHAR2     
  GETPACKAGEVERSION  VARCHAR2     
Total: 26 function(s)

Used by

Owner Name Type
RMAN  DBMS_RCVCAT  PACKAGE BODY 
Total: 1 used by reference(s)

DDL


  CREATE OR REPLACE PACKAGE "RMAN"."DBMS_RCVCAT" is
-- resync types
RESYNC_FULL    CONSTANT number := 1;
RESYNC_PARTIAL CONSTANT number := 2;
RESYNC_NONE    CONSTANT number := 3;
CONFIGRESYNC_NO    CONSTANT number := 0;
CONFIGRESYNC_TORC  CONSTANT number := 1;
CONFIGRESYNC_TOCF  CONSTANT number := 2;
-- controlfile types
CF_CURRENT     CONSTANT number := 1;
CF_BACKUP      CONSTANT number := 2;
CF_CREATED     CONSTANT number := 3;
CF_STANDBY     CONSTANT number := 4;
CF_NOMOUNT     CONSTANT number := 5;
this_db_key     number := NULL;
this_dbinc_key  number := NULL;
/*-----------------------*
* Debugging functions   *
*-----------------------*/

PROCEDURE setDebugOn;
PROCEDURE setDebugOff;
/*-----------------------*
* Database Registration *
*-----------------------*/

PROCEDURE registerDatabase(
   db_id      IN number
  ,db_name    IN varchar2
  ,reset_scn  IN number
  ,reset_time IN date
);
-- registerDatabase registers a new target database in the recovery catalog.
-- All target databases must be registered exactly once.  The database
-- is inserted into the db table, and also one row is inserted into the dbinc
-- table as the root and the current incarnation of this database.
-- Note that it is legal to register a database that has undergone a previous
-- resetlogs, but recovery to a time prior to the resetlogs is not supported.
-- registerDatabase is atomic; all changes to recovery catalog are committed
-- or rolled back if an exception is signalled
-- Input parameters:
--   db_id
--     the value of kccfhdbi from the controlfile of the target database
--   db_name
--     the name of the database
--   reset_scn
--     the resetlogs SCN of this database
--   reset_time
--     the resetlogs time
-- Exceptions:
--   DUPLICATE_DATABASE
--     a database with the same db_id has already been registered. This may
--     happen if the database was created by copying datafiles from an
--     existing datafile
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before calling registerDatabase
--   BACKUP_IN_PROGRESS (internal)
--     a backup conversation is in progress. The current backup conversation
--     must be ended or canceled before calling registerDatabase
procedure resetDatabase(
  db_id      IN number
,db_name    IN varchar2
,reset_scn  IN number
,reset_time IN date
,parent_reset_scn  IN number
,parent_reset_time IN date
);
procedure resetDatabase(
  dbinc_key  IN number
,db_name    IN varchar2
);
-- There are two forms of resetDatabase procedure.
-- The first form registers a new incarnation of an registered database
-- and makes it the current incarnation. It must always be called after
-- the target database is opened with resetlogs option.
-- The second form makes an old incarnation the current incarnation.
-- registerDatabase is atomic; all changes to recovery catalog are committed
-- or rolled back if an exception is signalled
-- Input parameters:
--   dbinc_key
--     the primary key of an existing database incarnation in the recovery
--     catalog. Only used by the second form.
--   db_id
--     the value of kccfhdbi from the controlfile of the target database
--   db_name
--     the name of the database
--   reset_scn
--     the resetlogs SCN of this database
--   reset_time
--     the resetlogs time
-- Exceptions:
--   DB_ID_IS_NULL (internal)
--     a null db_id was given
--   DATABASE_NOT_FOUND
--     No database with the given db_id was found in the recovery catalog
--     Use registerDatabase procedure instead
--   RESET_SCN_TOO_LOW
--   RESET_TIME_TOO_LOW
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before calling resetDatabase
--   BACKUP_IN_PROGRESS (internal)
--     a backup conversation is in progress. The current backup conversation
--     must be ended or canceled before calling resetDatabase
procedure unregisterDatabase(
  db_key     IN number
,db_id      IN number
);
-- unRegisterDatabase removes a registered database from the recovery catalog
/*--------------------------*
* Set Database Incarnation *
*--------------------------*/

procedure setDatabase(
  db_name    IN varchar2
,reset_scn  IN number
,reset_time IN date
,db_id      IN number
);
procedure setDatabase(dbinc_key number);
procedure setDatabase;
-- setDatabase selects which target database subsequent dbms_rcvcat
-- procedures operate on. Note that only the current incarnation can be
-- selected. If the target database or its current incarnation is not
-- registered then setDatabase will fail.
-- setDatabase sets the package state variables to point to the selected
-- database and its current incarnation.
-- The settings will be valid until the end of the session unless setDatabase
-- is called again
-- Input parameters:
--   db_id
--     the value of kccfhdbi from the controlfile of the target database
--     if not set and succesfully connected to database it will set to
--     current dbid of target database
--   db_name
--     the name of the database
--   reset_scn
--     the resetlogs SCN of this database
--   reset_time
--     the resetlogs time
-- Exceptions:
--   DATABASE_NOT_FOUND
--     No database with the given db_id was found in the recovery catalog
--     The database must be registered using registerDatabase first
--   DATABASE_INCARNATION_NOT_FOUND
--     No database incarnation matches the given arguments
--     The database incarnation must be registered using resetDatabase first
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before calling setDatabase
/*-----------------------------*
* Recovery Catalog Checkpoint *
*-----------------------------*/

function ckptNeeded(
  ckp_scn         IN number
,ckp_cf_seq      IN number
,cf_version      IN date
,cf_type         IN number
,high_df_recid   IN number
,high_orl_recid  IN number
,high_cdf_recid  IN number
,high_al_recid   IN number
,high_bp_recid   IN number
,high_do_recid   IN number
,high_offr_recid IN number
,high_pc_recid   IN number DEFAULT NULL -- for compatibility
,high_conf_recid IN number DEFAULT NULL -- for compatibility
,rltime          IN DATE   DEFAULT NULL -- for compatibility
,high_ts_recid   IN number DEFAULT NULL -- for compatibility
,high_bs_recid   IN number DEFAULT NULL -- for compatibility
) return number;
PROCEDURE lockForCkpt;
procedure beginCkpt(
  ckp_scn       IN number
,ckp_cf_seq    IN number
,cf_version    IN date
,ckp_time      IN date
,ckp_type      IN varchar2
,ckp_db_status IN varchar2
,high_df_recid IN number
,cf_type       IN varchar2 DEFAULT 'CURRENT'   -- for compatibility reasons
);
-- ckptNeeded determines whether recovery catalog is current enough
-- for name translation. It is used by RMAN to check whether an implicit
-- resync is needed before compiling a new backup, copy, restore or recover
-- command.
-- beginCkpt begins a new recovery catalog checkpoint. It records the
-- checkpoint scn and controlfile sequence number of the backup control file
-- used for the checkpoint. The ckp_scn must greater than ckp_scn of the
-- previous checkpoint or if the ckp_scns are equal then the ckp_cf_seq
-- must be greater ckp_cf_seq of the previous checkpoint. If also the
-- ckp_sf_seqs are equal, then the recovery catalog has already been resynced
-- with this backup controlfile. Otherwise the backup controlfile is too old.
--
-- Since the current control file doesn't have ckp_scn partial resyncs
-- check only that ckp_cf_seq advances.
--
-- if the cf_create_time has not changed since the previous checkpoint,
-- we assume that the controlfile has not been recreated. If it has changed
-- the controlfile must have been recreated and all recids in the controlfiles
-- have effectively been reset. Therefore beginCkpt resets the highwater marks
-- for all recids to 0 to force this resync will read all entries from
-- the newly create controlfile.
--
-- It locks the database incarnation row in the dbinc table to prevent
-- multiple simultaneous checkpoint from happening. The endCkpt procedure must
-- be called in the end to commit the recovery catalog checkpoint transaction.
-- Alternatively cancelCkpt can be called to rollback the transaction if
-- something goes wrong.
--
-- Input Parameters:
--   ckp_scn
--     controlfile checkpoint scn
--   ckp_cf_seq
--     controlfile sequence number
--   cf_create_time
--     control file creation timestamp
--   ckp_time
--     controlfile checkpoint timestamp
--   ckp_type
--     'FULL' full resync from a snapshot control file
--     'JOB'  partial resync from current control file
--   high_*_recid
--     High recid of coresponding controlfile records
--   cf_type
--    controlfile type CURRENT, STANDBY, BACKUP, CLONE etc
-- Exceptions:
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before beginning a new one
--   DATABASE_INCARNATION_NOT_SET
--     the database incarnation is not set. It must be set by calling
--     setDatabase before calling beginCkpt
--   INVALID_CKP_SCN
--     the backup controlfile ckp_scn is less than ckp_scn of the previous
--     checkpoint. This should not happen. However, it is conceivable that this
--     could happen if the user restores the database from an offline backup
--     and doesn't open the database with resetlogs.
--   INVALID_CKP_CF_SEQ
--     the backup controlfile ckp_cf_seq is less than ckp_cf_seq of the
--     previous checkpoint. This should not happen. See the explanation of the
--     the previous exception.
--   CKP_ALREADY_RECORDED
--     the backup control file ckp_scn and ckp_cf_seq are equal to the previous
--     checkpoint. Nothing was changed in the control file between this
--     this checkpoint and the previous one, so the there is no reason to
--     resync
--   CHECKPOINT_TYPE_INVALID
--     the ckp_type is not valid ('ONLINE' or 'OFFLINE')
procedure endCkpt;
-- endCkpt must be called after all information is successfully resynced.
-- Note that endCkpt cannot verify that everything was successfully resynced,
-- the client must not call endCkpt if it encountered unrecoverable errors
-- during the checkpoint.
-- It will commit the checkpoint transaction and clear the package state
-- variables to indicate that no checkpoint is in progress
-- Exceptions:
--   CHECKPOINT_NOT_IN_PROGRESS (internal)
--     there is no checkpoint in progress to be ended. Maybe cancelCkpt was
--     called before this.
procedure cancelCkpt;
-- cancelCkpt must be called if unrecoverable error is encountered during
-- the checkpoint. It will rollback the checkpoint transaction and clear
-- the package state variables to indicate that no checkpoint is in progress
/*-------------------*
* Resync            *
*-------------------*/

-- Resyncing all non-circular-reuse type records (tablespace, datafile and
-- redo thread) from the controlfile use the following protocol. The
-- controlfile maintains a high_recid for each record type. The high_recid
-- is incremented every time a record of that type is created, updated or
-- deleted. The client gets the high_recid for the record type
-- from the controlfile and passes it to beginResync. The beginResync
-- compares the given high_recid with the high_recid stored in the recovery
-- catalog. If the high_recids are equal then nothing has changed in the
-- controlfile since the previous resync and beginResync returns false.
-- If the given high_recid is greater than the one stored in the recovery
-- catalog then beginResync will return true and the client will query
-- all records from the controlfile and call Check procedure for each
-- record.
-- Resyncing all circular-reuse type records from the controlfile use another
-- protocol. Since the circular-reuse type records are never updated
-- (significantly) after they have been created, only records created after
-- the previous resync need to be resynced. The client calls the beginResync
-- procedure which returns the high_recid stored in the recovery catalog.
-- The client compared the returned high_recid with the high_recid in the
-- controlfile. If the high_recids are equal then no new record have created
-- since the previous resync. If the high_recid in controlfile is greater
-- than the one returned by beginResync, then the client will query all
-- records with recid greater than the returned high_recid from the controlfile
-- and call Check procedure for each record.
-- The parameters for all Check procedures match the columns in the
-- corresponding V$ views on the controlfile fixed tables.
/*-------------------*
* Tablespace Resync *
*-------------------*/

function beginTableSpaceResync(
  high_ts_recid IN NUMBER,
  force         IN BOOLEAN DEFAULT FALSE
) return boolean;
-- beginTableSpaceResync should be called to start the process of resyncing
-- information about tablespaces.
-- Returns:
--  TRUE --> tablespace resync started
--  FALSE --> tablespace resync not started (since there is nothing to resync)
-- Exceptions:
--   CHECKPOINT_NOT_IN_PROGRESS
procedure checkTableSpace(
  ts_name                     IN varchar2
,ts#                         IN number
,create_scn                  IN number
,create_time                 IN date
,rbs_count                   IN number   DEFAULT NULL
,included_in_database_backup IN varchar2 DEFAULT NULL
);
-- checkTableSpace inserts or updates information about the given tablespace.
-- It must be called once for each tablespace currently listed in
-- the controlfile in ascending ts# order. If checkTableSpace notices that a
-- tablespace is dropped, either because its ts# is not passed to it or
-- because a new tablespace with same ts# but higher create_scn is passed,
-- it marks the tablespace dropped in the recovery catalog.
--
-- Input Parameters:
--   ts_name
--     The name of the tablespace
--   ts#
--     The ktsn number of the tablespace
--   create_scn
--     The lowest cretion SCN among all datafiles in the tablespace
--   create_time
--     Timestamp for create_scn
--   rbs_count
--   included_in_database_backup
--     Flag telling if the tablespace is included in full backups.
--     'YES'or NULL  - included in whole database backup
--     'NO'          - not included in whole database backup
-- Exceptions:
--   TS_RESYNC_NOT_STARTED
--   BAD_TS_ORDER
--     tablespaces were passed in wrong order
--   INVALID_CREATE_SCN
--     create_scn is higher than ckp_scn or create scn is lower than the
--     create scn of the previous incarnation
--   INVALID_CREATE_TIME
--   INVALID_TS_NAME
procedure endTableSpaceResync;
-- endTableSpaceResync must be called after the last call to checkTablespace.
-- Exceptions:
--    TS_RESYNC_NOT_STARTED
/*-----------------*
* Datafile Resync *
*-----------------*/

function beginDataFileResync(
  high_df_recid IN number
) return boolean;
-- beginDataFileResync must be called to start the process of resyncing
-- information about datafiles. The tablespace information must be resync'd
-- before doing a datafile resync.
-- Exceptions:
--    TS_RESYNC_NOT_COMPLETE
procedure checkDataFile(file#        IN NUMBER,
                        fname        IN VARCHAR2,
                        create_scn   IN NUMBER,
                        create_time  IN DATE,
                        blocks       IN NUMBER,
                        block_size   IN NUMBER,
                        ts#          IN NUMBER,
                        stop_scn     IN NUMBER,
                        read_only    IN NUMBER,
                        stop_time    IN DATE     DEFAULT NULL,
                        rfile#       IN NUMBER   DEFAULT NULL,
                        aux_fname    IN VARCHAR2 DEFAULT NULL);
-- checkDataFile inserts or updates information about the given datafile.
-- It must be called once for each datafile currently listed in
-- the controlfile in ascending file# order. checkDataFile will also mark
-- datafiles which are not listed in the controlfile anymore as dropped
-- in rcvcat. Note that datafiles may disappear from controlfile if it is
-- recreated.
--
-- Input Parameters:
--   file#
--     datafile number
--   fname
--     the current name of the datafile
--   create_scn
--     the creation SCN
--   create_time
--     the creation timestamp
--   fsize
--     the current size of the datafile in blocks
--   ts#
--     the ktsn of the tablespace to which this datafile belongs
--   aux_name
--     file name of auxname
--     'NONE' - auxname is not configured
--     NULL   - auxname is unknown
-- Exceptions:
--    DF_RESYNC_NOT_STARTED
procedure endDataFileResync;
-- endDataFileResync must be called after all datafiles have been passed in.
-- Exceptions:
--    DF_RESYNC_NOT_STARTED
procedure setDatafileSize(
  file#       IN number
,create_scn  IN number
,blocks      IN number
);
/*---------------------*
* Redo Thread resync  *
*---------------------*/

function beginThreadResync(
  high_rt_recid IN number
) return boolean;
-- beginThreadResync begins the redo thread resync.
procedure checkThread(
  thread#        IN number
,last_sequence# IN number
,enable_scn     IN number
,enable_time    IN date
,disable_scn    IN number
,disable_time   IN date
,status         IN varchar2
);
-- checkThread inserts or updates information about the given redo thread.
-- It must called once for each redo thread currently listed in the
-- control file in ascending thread# order.
-- Input Parameters:
--   thread#
--     thread number
--   last_sequence#
--     last log sequence number allocated for this thread
--   enable_scn
--     SCN of the last thread enable or disable
--   create_time
--     timestamp of the last thread enable or disable
--   status
--     'E' -> enabled, 'D' -> disabled
-- Exceptions:
--    RT_RESYNC_NOT_STARTED
--    BAD_RT_ORDER
procedure endThreadResync;
-- endThreadResync must be called after all redo threads have been checked
/*------------------------*
* Online Redo Log resync *
*------------------------*/

function beginOnlineRedoLogResync(
  high_orl_recid IN number
) return boolean;
-- beginOnlineRedoLogResync begins the redo log resync.
procedure checkOnlineRedoLog(
  thread#        IN number
,group#         IN number
,fname          IN varchar2
);
-- checkOnlineRedoLog inserts or updates information about the given redo log
-- It must called once for each redo log currently listed in the
-- control file in ascending fname order.
procedure endOnlineRedoLogResync;
-- endOnlineRedoLogResync must be called after all logs have been checked
/*----------------------------------*
* RMAN Configration records resync *
*----------------------------------*/

function beginConfigResync(
  high_conf_recid IN number
) return number;
-- begiConfigResync begins resync of RMAN Configration records.
procedure endConfigResync;
-- endConfigResync must be called after all RMAN Configurations records
-- have been checked
procedure getConfig(
   conf#          OUT    number
  ,name           IN OUT varchar2
  ,value          IN OUT varchar2
  ,first          IN     boolean);
-- this getConfig is called during resync from RC to CF. This is duplicate
-- of function from dbms_rcvman PL/SQL package
/*-----------------------------*
* Redo Log History resync     *
*-----------------------------*/

function beginLogHistoryResync return number;
-- beginLogHistoryResync begins the log history resync. It returns the
-- highest recid of the log history entries recorded in the recovery catalog.
-- The purpose of the returned recid is to avoid processing log history
-- entries that are already in rcvcat.
procedure checkLogHistory(
  rlh_recid   IN number
,rlh_stamp   IN number
,thread#     IN number
,sequence#   IN number
,low_scn     IN number
,low_time    IN date
,next_scn    IN number
);
-- checkLogHistory inserts or checks the information about given log history
-- entry. If entry doesn't exist in rcvcat then it is inserted. If the entry
-- already exists then it is compared with the given entry. If there
-- is a mismatch an internal error is signalled (the entries shouldn't change)
-- checkLogHistory is called once for every redo log history entry  in the
-- controlfile with a recid greater than or equal to the one returned by
-- beginLogHistoryResync. The idea is to check the last entry insert during the
-- previous checkpoint.
-- Input Parameters:
--   rlh_recid
--     The controlfile recid of the redo log history entry
--   rlh_stamp
--     The controlfile stamp of the redo log history entry
--   thread#
--     Thread number.
--   sequence#
--     Log sequence number
--   low_scn
--     The low SCN of this log.
--   low_time
--     Timestamp associated with lowScn.
--   next_scn
--     The last SCN of this log.  All redo at this SCN or higher
--     is in a subsequent log sequence number.
procedure endLogHistoryResync;
-- endLogHistoryResync will complete redo log history resync and update the
-- high_rlh_recid column in dbinc table.
/*-------------------------*
* Archived Log resync     *
*-------------------------*/

function beginArchivedLogResync return number;
-- beginArchivedLogResync begins archive log resync
procedure checkArchivedLog(
  al_recid    IN number
,al_stamp    IN number
,thread#     IN number
,sequence#   IN number
,reset_scn   IN number
,reset_time  IN date
,low_scn     IN number
,low_time    IN date
,next_scn    IN number
,next_time   IN date
,blocks      IN number
,block_size  IN number
,fname       IN varchar2
,archived    IN varchar2
,completion_time IN date
,status      IN varchar2
,is_standby  IN varchar2 DEFAULT NULL
,dictionary_begin IN varchar2 DEFAULT NULL
,dictionary_end IN varchar2 DEFAULT NULL
);
-- checkArchivedLog inserts the archived log entry in rcvcat. If the entry is
-- already in rcvcat it will check that it matches the given entry.
-- Input Parameters:
--   al_recid
--     The controlfile recid of the archived log entry
--   al_stamp
--     The controlfile stamp of the archived log entry
--   thread#
--     Thread number.
--   sequence#
--     Log sequence number
--   reset_scn
--     The resetlogs SCN.
--   reset_time
--     Timestamp associated with resetScn.
--   low_scn
--     The low SCN of this log.
--   low_time
--     Timestamp associated with lowScn.
--   next_scn
--     The last SCN of this log.  All redo at this SCN or higher
--     is in a subsequent log sequence number.
--   next_time
--     Timestamep associated with nextScn.
--   blocks
--     Number of blocks in this archivelog.
--   block_size
--     Blocksize of the log.
--   fname
--     Filename of the archived log. NULL -> log was archived
--   status
--     'A' -> log is available, 'U' -> log is unavailable
--   is_standby
--     'Y' -> this is a standby archive log,
--     'N' -> this is a primary archive log
procedure endArchivedLogResync;
-- endArchivedLogResync completes archived log resync and updates the
-- high_al_recid column in dbinc table for the next resync.
/*-------------------------*
* Offline range resync    *
*-------------------------*/

function beginOfflineRangeResync return number;
-- beginOfflineRangeResync begins the offline range resync. It returns
-- the highest offline range recid that was recorded by the previous resync.
procedure checkOfflineRange(
  offr_recid     IN number
,offr_stamp     IN number
,file#          IN number
,create_scn     IN number
,offline_scn    IN number
,online_scn     IN number
,online_time    IN date
,cf_create_time IN date
);
-- checkOfflineRange inserts a offline range record in the rcvcat.
-- Input Parameters:
--   offr_recid
--     recid of the offline range record in control file
--   offr_stamp
--     stamp of the offline range record in control file
procedure endOfflineRangeResync;
-- endOfflineRangeResync completes the offline range resync.
/*-------------------------*
* Backup Set resync       *
*-------------------------*/

PROCEDURE updateBackupSetRec(bs_key IN NUMBER,
                             act_corrupt IN BOOLEAN DEFAULT TRUE);
-- act_corrupt - take rc_corruption entries into account while
--               deleting backupsets. These are dummy records hanging for
--               BMR 'corruption list' option.
function beginBackupSetResync return number;
-- beginBackupSetResync begins the backup set resync. It returns the highest
-- backup set recid that was recorded by the previous resync.
procedure checkBackupSet(
  bs_recid        IN number
,bs_stamp        IN number
,set_stamp       IN number
,set_count       IN number
,bck_type        IN varchar2
,incr_level      IN number DEFAULT NULL
,pieces          IN number
,start_time      IN date
,completion_time IN date
,controlfile_included
                  IN VARCHAR2 DEFAULT NULL
,input_file_scan_only
                  IN VARCHAR2 DEFAULT NULL
,keep_options    IN number   DEFAULT NULL
,keep_until      IN date     DEFAULT NULL
);
-- checkBackupSet inserts a backup set record in the rcvcat. If the record is
-- already in rcvcat the completion_time will be checked.
-- Input Parameters:
--   bck_type
--     Type of backup:  'D' = full datafile, 'I' = incremental datafile,
--                      'L' = archivelog
--   incr_level
--     Level of the incremental backup
--   bs_recid
--     recid of the backup set record in control file
--   bs_stamp
--     stamp of the backup set record in control file
--   completion_time
--     time when the backup completed
--   ctrlf_ckp_scn
--     If the backup set contains a control file, this the checkpoint SCN
--     in the control file backup. Null otherwise
--   controlfile_included
--     Indicates if this backup set has a controlfile in it and its type
--     'YES' or 'SBY'.  If no controlfile in the backup set then
--     it is 'NO'.
--   keep_until
---    Keep until some time.
--   keep_options
---    Keep options (0 means no_keep).
procedure endBackupSetResync;
-- endBackupSetResync completes the backup set resync.
/*-------------------------*
* Backup piece resync     *
*-------------------------*/

function beginBackupPieceResync return number;
procedure checkBackupPiece(
  bp_recid    IN number
,bp_stamp    IN number
,set_stamp   IN number
,set_count   IN number
,piece#      IN number
,tag         IN varchar2
,device_type IN varchar2
,handle      IN varchar2
,comments    IN varchar2
,media       IN varchar2
,concur      IN varchar2
,start_time  IN date
,completion_time IN date
,status      IN varchar2
,copy#       IN number default 1
,media_pool  IN number default 0
);
-- Input Parameters:
--
--   bp_recid
--     recid of backup piece record in control file
--   bp_stamp
--     stamp of backup piece record in control file
--   piece#
--     The ordinal number of this piece within the set.
--   tag
--     User-specified tag.  May be null.
--   device_type
--     Sequential device type on which this backup set resides.  'DISK'
--     specified if on disk rather than sequential media.
--   handle
--     The media handle on which this backup piece is stored.
--   comments
--     The comment associated with the backup piece.
--   media
--     The media id.  If multiple backup pieces reside on the same physical
--     piece of media, then they have the same mediaId.  Note this is not
--     the same as a handle, as the handle is specific to a single piece.
--   concur
--     TRUE if multiple job steps can access the physical media concurrently,
--     FALSE if not.
procedure endBackupPieceResync;
/*-------------------------*
* Backup Datafile resync  *
*-------------------------*/

function beginBackupDataFileResync return number;
procedure checkBackupDataFile(
  bdf_recid       IN number
,bdf_stamp       IN number
,set_stamp       IN number
,set_count       IN number
,file#           IN number
,create_scn      IN number
,create_time     IN date
,reset_scn       IN number
,reset_time      IN date
,incr_level      IN number
,incr_scn        IN number
,ckp_scn         IN number
,ckp_time        IN date
,abs_fuzzy_scn   IN number
,datafile_blocks IN number
,blocks          IN number
,block_size      IN number
,min_offr_recid  IN number
,completion_time IN date
,controlfile_type
                  IN varchar2 DEFAULT NULL
,cfile_abck_year IN number   DEFAULT NULL
,cfile_abck_mon_day
                  IN number   DEFAULT NULL
,cfile_abck_seq  IN number   DEFAULT NULL
);
-- Input Parameters:
--   recid
--     This is the recid of the v$backup_datafile record for this backup.
--   stamp
--     The timestamp associated with the recid in the controlfile.
--   file#
--     Datafile number
--   create_scn
--     The creation SCN of the datafile.
--   reset_scn
--     The resetlogs SCN of the datafile.  Note that different datafiles
--     in a backup set may have different resetScn's.  This occurs when
--     taking a backup of a file that was off-line clean or read-only when
--     a resetlogs was done.
--   reset_time
--   incr_scn
--   ckp_scn
--     The SCN to which the datafile's checkpoint will be advanced if this
--     backup is restored ('F') or applied ('I').
--   ckp_time
--     The timestamp associated with ckp_scn.
--   abs_fuzzy_scn
--     Absolute fuzzy SCN.
--     If zero then the file has no fuzziness at all.
--   blocks
--     Number of blocks written to the backup set for this datafile, or
--     the number of blocks in the datafile copy.
--   block_size
--     The blocksize of the datafile or datafile copy.  All files within
--     a backup set have the same blocksize, but this need not be enforced
--     by this package.
--   controlfile_type
--     If this a controlfile, it indicates its type ('B' or 'S'), null
--     otherwise
procedure endBackupDataFileResync;
/*-------------------------*
* Backup SPFILE resync  *
*-------------------------*/

function beginBackupSpFileResync return number;
procedure checkBackupSpFile(
  bsf_recid         IN number
,bsf_stamp         IN number
,set_stamp         IN number
,set_count         IN number
,modification_time IN date
,bytes             IN number
);
-- Input Parameters:
--   bsf_recid
--     This is the recid of the v$backup_spfile record for this backup.
--   bsf_stamp
--     The timestamp associated with the recid in the controlfile.
--   set_stamp
--     The timestamp associated with the parent backup set.
--   set_count
--     The set_count of the parent backup set.
--   modification_time
--     The modification time of this SPFILE.
--   bytes
--     Number of bytes written to the backup set for this SPFILE.
procedure endBackupSpFileResync;
/*-------------------------*
* Backup Redo Log resync  *
*-------------------------*/

function beginBackupRedoLogResync return number;
procedure checkBackupRedoLog(
  brl_recid   IN number
,brl_stamp   IN number
,set_stamp   IN number
,set_count   IN number
,thread#     IN number
,sequence#   IN number
,reset_scn   IN number
,reset_time  IN date
,low_scn     IN number
,low_time    IN date
,next_scn    IN number
,next_time   IN date
,blocks      IN number
,block_size  IN number
);
-- Input Parameters:
--   recid
--     The controlfile recid of the archivelog entry.  Null if for a backup
--     set.
--   stamp
--     Timestamp associated with copyRecid.
--   thread#
--     Thread number.
--   sequence#
--     Log sequence number
--   reset_scn
--     The resetlogs SCN.
--   reset_time
--     Timestamp associated with resetScn.
--   low_scn
--     The low SCN of this log.
--   low_time
--     Timestamp associated with lowScn.
--   next_scn
--     The last SCN of this log.  All redo at this SCN or higher
--     is in a subsequent log sequence number.
--   next_time
--     Timestamep associated with nextScn.
--   blocks
--     Number of blocks in this archivelog.
--   block_size
--     Blocksize of the log.
procedure endBackupRedoLogResync;
/*----------------------------*
* Copy Datafile resync       *
*----------------------------*/

function beginDataFileCopyResync return number;
procedure checkDataFileCopy(
  cdf_recid       IN number
,cdf_stamp       IN number
,fname           IN varchar2
,tag             IN varchar2
,file#           IN number
,create_scn      IN number
,create_time     IN date
,reset_scn       IN number
,reset_time      IN date
,incr_level      IN number
,ckp_scn         IN number
,ckp_time        IN date
,onl_fuzzy       IN varchar2
,bck_fuzzy       IN varchar2
,abs_fuzzy_scn   IN number
,rcv_fuzzy_scn   IN number
,rcv_fuzzy_time  IN date
,blocks          IN number
,block_size      IN number
,min_offr_recid  IN number
,completion_time IN date
,status          IN varchar2
,controlfile_type
                  IN varchar2 DEFAULT NULL
,keep_options    IN number   DEFAULT NULL
,keep_until      IN date     DEFAULT NULL
,scanned         IN varchar2 DEFAULT 'NO'
);
-- Input Parameters:
--   recid
--     If this is a datafile copy, this is the recid of the controlfile
--     record for this copy.
--   stamp
--     The timestamp associated with the recid in the controlfile.
--   fname
--     The filename of the copy file.
--   tag
--     Optional tag for a datafile copy
--   file#
--     Datafile number
--   create_scn
--     The creation SCN of the datafile.
--   reset_scn
--     The resetlogs SCN of the datafile.  Note that different datafiles
--     in a backup set may have different resetScn's.  This occurs when
--     taking a backup of a file that was off-line clean or read-only when
--     a resetlogs was done.
--   reset_time
--   ckp_scn
--     The SCN in the file's header.
--   ckp_time
--     The timestamp associated with ckp_scn.
--   abs_fuzzy_scn
--     Absolute fuzzy SCN.
--     If 0, then the file is not fuzzy at all.
--   blocks
--     File size.
--   block_size
--     Number of bytes in each logical block.
--  controlfile_type
--     The type of controlfile copy this is 'B' or 'S'.
--     null if not a controlfile
--   keep_until
---    Keep until some time.
--   keep_options
---    Keep options (0 means no_keep).
procedure endDataFileCopyResync;
/*----------------------------*
* Corrupt Block resync       *
*----------------------------*/

function beginBackupCorruptionResync return number;
procedure checkBackupCorruption(
  bcb_recid       IN number
,bcb_stamp       IN number
,set_stamp       IN number
,set_count       IN number
,piece#          IN number
,file#           IN number
,block#          IN number
,blocks          IN number
,corrupt_scn     IN number
,marked_corrupt  IN varchar2
,corruption_type IN varchar2 default NULL
);
procedure endBackupCorruptionResync;
function beginCopyCorruptionResync return number;
procedure checkCopyCorruption(
  ccb_recid       IN number
,ccb_stamp       IN number
,cdf_recid       IN number
,cdf_stamp       IN number
,file#           IN number
,block#          IN number
,blocks          IN number
,corrupt_scn     IN number
,marked_corrupt  IN varchar2
,corruption_type IN varchar2 default NULL
);
procedure endCopyCorruptionResync;
-- addCorrupt is called to add information about a block corruption in a
-- datafile copy or backup datafile.
--
-- Input Parameters:
--   bdf_key
--     Primary key of the bdf record which was returned by addBackupDatafile.
--   block#
--     The block number of the corrupt block.
--   corrupt_scn
--     SCN of the redo that corrupted the block for logically corrupt blocks.
--     Zero if the block is media corrupt.
--   marked_corrupt
--     'T' if we could not read the block from disk and had to reformat
--     the block as corrupt for storing in the backup set or copy.  'F'
--     otherwise.
--   piece#
--     The backup piece where the corrupt block was written.  Null if
--     this is for a datafile copy.
/*----------------------------*
* Deleted Object resync      *
*----------------------------*/

function beginDeletedObjectResync return number;
procedure checkDeletedObject(
  do_recid        IN number
,do_stamp        IN number
,object_type     IN varchar2
,object_recid    IN number
,object_stamp    IN number
,object_data     IN number DEFAULT NULL
);
procedure endDeletedObjectResync;
/*-------------------*
* Proxy Copy resync *
*-------------------*/

function beginProxyResync return number;
procedure checkProxyDataFile(
  xdf_recid       IN number
,xdf_stamp       IN number
,tag             IN varchar2
,file#           IN number
,create_scn      IN number
,create_time     IN date
,reset_scn       IN number
,reset_time      IN date
,incr_level      IN number
,ckp_scn         IN number
,ckp_time        IN date
,onl_fuzzy       IN varchar2
,bck_fuzzy       IN varchar2
,abs_fuzzy_scn   IN number
,rcv_fuzzy_scn   IN number
,rcv_fuzzy_time  IN date
,blocks          IN number
,block_size      IN number
,min_offr_recid  IN number
,device_type     IN varchar2
,handle          IN varchar2
,comments        IN varchar2
,media           IN varchar2
,media_pool      IN number
,start_time      IN date
,completion_time IN date
,status          IN varchar2
,controlfile_type
                  IN varchar2 DEFAULT NULL
,keep_options    IN number   DEFAULT NULL
,keep_until      IN date     DEFAULT NULL
);
-- Input Parameters:
--   recid
--     If this is a datafile copy, this is the recid of the controlfile
--     record for this copy.
--   stamp
--     The timestamp associated with the recid in the controlfile.
--   fname
--     The filename of the copy file.
--   tag
--     Optional tag for a datafile copy
--   file#
--     Datafile number
--   create_scn
--     The creation SCN of the datafile.
--   reset_scn
--     The resetlogs SCN of the datafile.  Note that different datafiles
--     in a backup set may have different resetScn's.  This occurs when
--     taking a backup of a file that was off-line clean or read-only when
--     a resetlogs was done.
--   reset_time
--   ckp_scn
--     The SCN in the file's header.
--   ckp_time
--     The timestamp associated with ckp_scn.
--   abs_fuzzy_scn
--     Absolute fuzzy SCN.
--     If 0, then the file is not fuzzy at all.
--   blocks
--     File size.
--   block_size
--     Number of bytes in each logical block.
--   handle
--     The media handle on which this proxy backup is stored.
--   comments
--     The comment associated with the proxy backup.
--   media
--     The media id.  If multiple proxy copies reside on the same physical
--     piece of media, then they have the same mediaId.  Note this is not
--     the same as a handle, as the handle is specific to a single piece.
--   start_time
--     The time that the media management layer was told it could start
--     copying the file.
--   completion_time
--     The time that the media management layer reported that it was done
--     copying the file.
--   status
--     A or D - available or deleted
--  controlfile_type
--     The type of controlfile copy this is 'B' or 'S'.
--     null if not a controlfile
--   keep_until
---    Keep until some time.
--   keep_options
---    Keep options (NULL/0 means no_keep).
procedure endProxyResync;
/*-------------------------*
* Incarnation resync     *
*-------------------------*/

function beginIncarnationResync return number;
-- beginIncarnationResync begins the database incarnation tree resync. It
-- returns the controlfile timestamp seen by previous resync.
function checkIncarnation(reset_scn         IN NUMBER,
                          reset_time        IN DATE,
                          prior_reset_scn   IN NUMBER DEFAULT NULL,
                          prior_reset_time  IN DATE DEFAULT NULL,
                          db_name           IN VARCHAR2 DEFAULT 'UNKNOWN')
                         return number;
-- checkIncarnation checks if reset_scn, reset_time incarnation is present
-- in dbinc table. Ifn't inserts this entry into dbinc table. Returns the
-- corresponding dbinc_key entry.
procedure endIncarnationResync(high_kccdivts IN NUMBER);
-- endIncarnationResync records this controlfile timestamp.
/*----------------------------*
* Change Procedures          *
*----------------------------*/

procedure changeDataFileCopy(
  cdf_recid    IN number
,cdf_stamp    IN number
,status       IN varchar2
,keep_options IN number DEFAULT NULL
,keep_until   IN date   DEFAULT NULL
);
-- changeDatafileCopy is called to change the availability, delete a
-- datafile copy or to change keep optsion of a datafile copy
-- in recovery catalog
procedure changeControlfileCopy(
  cdf_recid    IN number
,cdf_stamp    IN number
,status       IN varchar2
,keep_options IN number DEFAULT NULL
,keep_until   IN date   DEFAULT NULL
);
-- changeControlfileCopy is called to change the availability or delete a
-- controlfile copy in recovery catalog
procedure changeArchivedLog(
  al_recid IN number
,al_stamp IN number
,status   IN varchar2
);
-- changeArchivedLog is called to change the availability or delete a
-- datafile copy in recovery catalog
procedure changeBackupSet(
  recid        IN number
,stamp        IN number
,keep_options IN number
,keep_until   IN date
);
-- changeDatafileCopy is called to change the availability, delete a
-- datafile copy or to change keep optsion of a datafile copy
-- in recovery catalog
procedure changeBackupPiece(
  bp_recid IN number
,bp_stamp IN number
,status   IN varchar2
);
-- changeBackupPiece is called to change the availability of or delete a backup
-- piece
procedure changeProxyCopy(
  pc_recid     IN number
,pc_stamp     IN number
,status       IN varchar2
,keep_options IN number  DEFAULT NULL
,keep_until   IN date    DEFAULT NULL
);
-- changeProxyCopy is called to change the availability of or delete a
-- proxy copy
/*----------------------------*
* Stored Script Procedures   *
*----------------------------*/

procedure createScript(name IN varchar2);
procedure replaceScript(name IN varchar2);
-- These procedures create or replace a script with the given name.  Each
-- database registered in the catalog has its own namespace for scripts.
-- The putLine function must be called to pass the lines of the script
-- to the catalog.
procedure putLine(line IN varchar2);
-- This procedure passes 1 line of the script to the catalog.  The client
-- must call this until all lines have been passed in.  After the last line,
-- call putLine(NULL) to indicate that all lines have been passed in.
-- The maximum line length is 1024 bytes.
procedure deleteScript(name IN varchar2);
-- Deletes a script.
procedure getScript(name IN varchar2);
-- This procedure gets a script from the recovery catalog.  The client must
-- call getLine to fetch the lines of the script.
function getLine return varchar2;
-- This function returns 1 line of the stored script.  When all lines have
-- been passed back, it returns NULL.
procedure commitChanges;
-- Just does a commit;
/*---------------------------------------*
* Procedures for clone database support *
*---------------------------------------*/

procedure setCloneName(file#            IN number
                      ,creation_change# IN number
                      ,clone_fname      IN varchar2);
FUNCTION  getCloneName(file#            IN number
                      ,creation_change# IN number) RETURN VARCHAR2;
/*-----------------------------------*
* Procedures for RMAN configuration *
*-----------------------------------*/

PROCEDURE setConfig(conf#            IN NUMBER
                   ,name             IN VARCHAR2
                   ,value            IN VARCHAR2);
PROCEDURE resetConfig;
PROCEDURE deleteConfig(conf#         IN NUMBER);
/*----------------------------*
* Version info               *
*----------------------------*/

function getPackageVersion return varchar2;
function getCatalogVersion return varchar2;
/*-------------------------*
* Catalog upgrade support *
*-------------------------*/

/*
* NAME
*   bsStstusRecalc
* DESCRIPTION
*   Recompute the backupset status for all backupset whose current
*   status is a specified value.  This is intended to be used when
*   new values are introduced for the bs.status column.
*/

PROCEDURE bsStatusRecalc(status IN varchar2);
/*-------------------*
* Utility functions *
*-------------------*/

-- reNormalize is the interface to the recovery catalog used to apply a
-- one-time fixup on Windows-NT by re-applying the normalization algorithm,
-- which has been improved for 8.1.6.
-- The first call to reNormalize passes a null value for newname, to
-- initialize the procedure.  Each call returns an oldname which needs to be
-- normalized.  The caller normalizes the name and passes it back to
-- reNormalize, in newname, on the next call.  reNormalize will then update
-- the previous row and return the next one.  oldname is returned to the caller
-- as null when there are no more names.
PROCEDURE reNormalize(newname IN varchar2, oldname OUT varchar2);
-- sanityCheck is a procedure that performs internal consistency checks, and
-- cleanup, of the recovery catalog.  It is the last thing that is done during
-- resync.
PROCEDURE sanityCheck;
-- This function gets the sfile_key of the file in the catalog
-- if the catalog does not have the file then the routine fails if createfile
-- if 0, if createfile is 1 then a new record is created
-- It returns the key of the record created.
-- If the file already exists in the catalog, the current key is
-- returned and the newfile flag is set, so that it can be updated.
-- A special case is when this_fname is null, in this case the routine
-- returns the current db_key, this is needed for a posterior select
-- of all the stored files in the catalog for the current database (db_key)
-- in this case createfile and newfile are ignored.
--FUNCTION getsfile (this_fname IN  VARCHAR2,
--                   createfile IN  NUMBER,
--                   newfile    OUT NUMBER) RETURN NUMBER;
-- Obtains the DBID of the database from the catalog database.
FUNCTION getDbid RETURN NUMBER;
end dbms_rcvcat;

CREATE OR REPLACE PACKAGE BODY "RMAN"."DBMS_RCVCAT" IS
-- prvtrvct.sql
--
--  Copyright (c) Oracle Corporation 1995, 2000. ALL Rights Reserved.
--
--    NAME
--      prvtrvct.sql
--
--    DESCRIPTION
--
--    NOTES
--
--  MODIFIED   (MM/DD/YY)
--  banand      02/01/02 - fix 2210440
--  molagapp    01/31/01 - change default value for cftype in beginCkpt
--  molagapp    11/29/01 - update package version 9.2.0
--  molagapp    11/13/01 - bug 2107554
--  fsanchez    11/10/01 - bug-2071872
--  molagapp    10/29/01 - fix standby/primary switch full resync
--  molagapp    05/27/01 - bug 1530744
--  molagapp    10/08/01 - fix default value for scanned
--  sdizdar     09/08/01 - SPFILE backup: add functions for resync
--  swerthei    08/03/01 - close cursors in cancelCkpt
--  banand      07/24/01 - fix 1856783
--  banand      07/02/01 - fix 758489
--  fsanchez    05/23/01 - dbnewid
--  sdizdar     06/26/01 - bug-1852923: fix beginConfigResync()
--  swerthei    05/08/01 - code cleanup
--  swerthei    04/04/01 - add v$datafile_copy.scanned
--  fsanchez    04/11/01 - remove_get_put
--  swerthei    04/03/01 - add new setDatabase calls for debugging
--  swerthei    03/05/01 - ckp table cleanup
--  fsanchez    02/15/01 - bug-1538834
--  fsanchez    01/25/01 - bug-1586048
--  molagapp    11/30/00 - bug 1518515
--  sdizdar     11/12/00 - bug-1496982: 8.2 -> 9.0
--  molagapp    10/26/00 - bug-1332121
--  molagapp    10/24/00 - bug-1478785
--  molagapp    10/16/00 - bug-1467871
--  sdizdar     09/21/00 - fix 8.x compatibility
--                         - modify ckptNeeded
--  fsanchez    05/30/00 - cfile-autobackup
--  sdizdar     09/11/00 - tablespace resync fix:
--                         - fix ckptNeeded (add recid of tablespace records)
--                         - improved beginTableSpaceResyn
--  molagapp    08/28/00 - fix 8.2 upgrade
--  dbeusee     06/28/00 - rman82_maint_syntax_unification
--  dbeusee     07/20/00 - rman82_debug_enhancements
--  fsanchez    07/31/00 - backup_flat_files
--  sdizdar     06/28/00 - Configure auxfilename & exclude tablespace:
--                         - setDatabase calls dbms_rcvman.setDatabase
--                         - add getCloneName,
--                         - updated checkDatafile and checkTablespace
--  fsanchez    05/30/00 - cfile-autobackup
--  sdizdar     05/12/00 - RMAN retention policy (keep):
--                         - keep backup support
--  banand      05/19/00 - use value in findConfig_c cursor
--  swerthei    06/06/00 - add archived log logminer dictionary columns
--  dbeusee     04/13/00 - rman82_cf_status_unification
--  molagapp    05/18/00 - rfile# and recovered changes
--  fsanchez    03/22/00 - instantiate_standby
--  sdizdar     04/17/00 - RMAN configuration:
--                       - add setConfig, resetConfig, deleteConfig & getConfig
--  swerthei    04/04/00 - allow orphaned blocks in bcb
--  molagapp    02/25/00 - bug 1186598: remove compatible, undo deleteAL change
--  sdizdar     02/18/00 - bug-977412: added commit to
--                         deleteScript, createScript and replaceScript
--  swerthei    09/14/99 - add dbms_rcvcat.reNormalize
--  gpongrac    08/16/99 - add 8.1.6 to versionlist
--  gpongrac    08/04/99 - really delete records now
--  gpongrac    07/14/99 - add stop_time to df table
--  gpongrac    07/06/99 - change rcver to 8.1.6
--  gpongrac    12/18/98 - clean up checkBackupPiece
--  dbeusee     09/13/98 - bug-728666
--  rlu         05/03/99 - 621515_restore_main
--  gpongrac    04/08/99 - change REM to --
--  rlu         11/25/98 - bug_621515
--  swerthei    10/22/98 - change proxy messages
--  fsanchez    10/07/98 - bug-607271
--  swerthei    06/19/98 - prepare for wrapping to recover.bsq
--  swerthei    06/17/98 - make compatible with 8.0
--  swerthei    06/01/98 - add media_pool
--  swerthei    06/01/98 - add changeProxyCopy
--  swerthei    05/18/98 - resync proxy copy records
--  dbeusee     04/20/98 - rpt_redundancy_enh
--  gpongrac    05/06/98 - add bsStatusRecalc
--  dbeusee     04/06/98 - xcheck enh
--  fsanchez    03/29/98 - Duplexed backup sets
--  gpongrac    01/27/98 - change getCatalogVersion
--  gpongrac    01/16/98 - bug 612344:  deal with null fname in checkDatafile
--  fsanchez    01/04/98 - Allow setDatabase to receive dbid without dbname
--  gpongrac    09/02/97 - add setDatafile Size
--  tpystyne    09/12/97 - bug 480172, fix name translation
--  gpongrac    08/12/97 - deal with clone_name becoming the real filename
--  gpongrac    07/01/97 - fix typo
--  gpongrac    06/30/97 - keep offline clean and read-only scn in df table
--  gpongrac    06/30/97 - record current offline range in kccor
--  gpongrac    06/26/97 - uppercase all keywords and reformat
--  gpongrac    04/08/97 - deal with 0 creation scn in checkofflinerange
--  gpongrac    04/03/97 - consider offline ranges in ckptneeded
--  gpongrac    03/31/97 - change to use version_time instead of cf_create_t
--  gpongrac    03/31/97 - add cf_create_time to offr
--  tpystyne    03/20/97 - update catalog version to 8.00.03
--  tpystyne    02/27/97 - add ckptneeded
--  gpongrac    02/20/97 - add compltion_time to bdf
--  swerthei    01/14/97 - add setclonename
--  ### comments from 1996 removed
type version_list_type is table of varchar2(8) index by binary_integer;
version_list version_list_type;
version_max_index binary_integer;
version_counter binary_integer := 1;
/*-----------*
* Constants *
*-----------*/

MAXNUMVAL CONSTANT      NUMBER := 2**32-1;
catalog_version CONSTANT VARCHAR2(12) := '09.02.00';
/*-------------------------*
* Package State Variables *
*-------------------------*/

-- Package State Variables:
--   debug
--     Controls whether deb() sends debug info back to RMAN.
--   this_db_key
--     This is the primary key of the db record for the target database we
--     are dealing with.  It is set as a result of calling registerDatabase,
--     resetDatabase or setDatabase.
--   this_dbinc_key
--     This is the primary key of the dbinc for the incarnation of the target
--     database we are dealing with.  It is set as a result of calling
--     registerDatabase, resetDatabase or setDatabase.
--   this_ckp_key
--     This is the primary key of the current recovery catalog checkpoint
--     or 0 for partial checkpoint.
--     It is set when beginCkpt is called, and cleared to null when endCkpt
--     is called.
--   last_*
--     Used to ensure that record are passed to check* procedures
--     in ascending order.
--
--   see Cursor Row Variables section for more state variables
debug           BOOLEAN := FALSE;
this_ckp_key    NUMBER := NULL;
this_ckp_scn    NUMBER := NULL;
this_ckp_time   DATE   := NULL;
last_ts#        NUMBER;
last_file#      NUMBER;
last_thread#    NUMBER;
last_fname      dfatt.fname%type;
last_ts_recid   NUMBER;
last_df_recid   NUMBER;
last_rt_recid   NUMBER;
last_orl_recid  NUMBER;
last_conf_recid NUMBER;
last_rlh_recid  NUMBER;
last_al_recid   NUMBER;
last_offr_recid NUMBER;
last_bs_recid   NUMBER;
last_bp_recid   NUMBER;
last_bdf_recid  NUMBER;
last_bsf_recid  NUMBER;
last_brl_recid  NUMBER;
last_cdf_recid  NUMBER;
last_bcb_recid  NUMBER;
last_ccb_recid  NUMBER;
last_do_recid   NUMBER;
last_xdf_recid  NUMBER := NULL;
last_xal_recid  NUMBER := NULL;
scr_key         NUMBER := NULL;
scr_line        NUMBER;
kccdivts        NUMBER;
last_kccdivts   NUMBER;
type bskeys is table of number index by binary_integer;
cntbs           number := 0;
insertedbs      bskeys;
--
-- NOTE :: this_cf_type usage
--
-- We should never track the high water mark of resync records when the
-- controlfile type is STANDBY/BACKUP. This variable is essentially used to
-- satisfy this requirement. This is because a full resync will happen when
-- switched from standby to primary database.
--
-- This variable MUST be used in all circular record beginresync/endresync
-- code to return 0 as recid and not to update high water mark.
--
-- For STANDBY controlfile, always PARTIAL resync of ALL records will happen.
-- More importantly, as we aren't tracking high water mark when
-- we resync from STANDBY/BACKUP, ALL circular records (from 0 recid) will be
-- resynced every time.
--
-- Hopefully, these issues should be resolved in standby OMF support 10i
-- project.
--
this_cf_type    VARCHAR2(7) := NULL;
reNorm_state binary_integer;
RENORM_DFATT CONSTANT binary_integer := 1;
RENORM_ORL   CONSTANT binary_integer := 2;
RENORM_AL    CONSTANT binary_integer := 3;
RENORM_BP    CONSTANT binary_integer := 4;
RENORM_CCF   CONSTANT binary_integer := 5;
RENORM_CDF   CONSTANT binary_integer := 6;
/*---------*
* Cursors *
*---------*/

-- Package Cursors:
--   tsQ
--     Used to resync the list of tablespaces.
--   dfQ
--     Used to resync the list of datafiles.
--   rtQ
--     Used to resync the list of threads.
--   orlQ
--     Used to resync the list of online redo logs.
--   scrlQ
--     Used to fetch lines from a stored script.
-- select all current tablespaces in this database incarnation
cursor tsQ IS
  SELECT ts.ts_name, ts.ts#, ts.create_scn, ts.create_time, tsatt.rbs_count,
         ts.included_in_database_backup
  FROM ts, tsatt
  WHERE ts.dbinc_key  = tsatt.dbinc_key
    AND ts.ts#        = tsatt.ts#
    AND ts.create_scn = tsatt.create_scn
    AND ts.dbinc_key =  this_dbinc_key
    AND ts.drop_scn       IS NULL       -- skip ones we know were dropped
    AND tsatt.end_ckp_key IS NULL
  ORDER BY ts.ts#;                      -- client passes rows to checkTs in
                                        -- ascending ts# order.  We can detect
                                        -- new or dropped tablespaces this way.
-- select all datafiles in this database incarnation
cursor dfQ IS
  SELECT df.file#, df.create_scn, df.create_time, df.ts#,
         dfatt.fname, dfatt.blocks, df.clone_fname, df.stop_scn, df.read_only
  FROM df, dfatt
  WHERE df.dbinc_key  = dfatt.dbinc_key -- join dfatt to df
    AND df.file#      = dfatt.file#
    AND df.create_scn = dfatt.create_scn
    AND df.dbinc_key  = this_dbinc_key  -- our dbinc please
    AND df.drop_scn       IS NULL       -- df not dropped
    AND dfatt.end_ckp_key IS NULL       -- the current dfatt record
  ORDER BY df.file#;                    -- client passes rows to checkDf in
                                        -- ascending file# order. We can detect
                                        -- new datafiles this way
-- select all redo threads in this database incarnation
cursor rtQ IS
  SELECT rt.thread#, rt.sequence#, rt.enable_scn,
         rt.enable_time, rt.status
  FROM rt
  WHERE rt.dbinc_key = this_dbinc_key
  ORDER BY rt.thread#;
-- select all online redo logs in this database incarnation
cursor orlQ IS
  SELECT orl.thread#, orl.group#, orl.fname
  FROM orl
  WHERE orl.dbinc_key = this_dbinc_key
  ORDER BY nlssort(orl.fname, 'NLS_SORT=binary');   -- bug 2107554
-- select all available pieces that match the device_type and handle
cursor bpq(device_type VARCHAR2, handle VARCHAR2,
           bp_recid VARCHAR2, bp_stamp VARCHAR2) IS
  SELECT bp_key
  FROM bp
  WHERE db_key = this_db_key
  AND   device_type = bpq.device_type
  AND   handle = bpq.handle
  AND   handle_hashkey = substr(bpq.device_type,1,10) ||
                         substr(bpq.handle,1,10) ||
                         substr(bpq.handle,-10)
  AND   status = 'A' -- ### why not all pieces w/ same name???
  AND   bp_stamp <= bpq.bp_stamp
  AND   NOT (bp_recid = bpq.bp_recid AND
             bp_stamp = bpq.bp_stamp);
-- select all lines from a stored script
cursor scrlQ(key NUMBER) IS
  SELECT text
  FROM scrl
  WHERE scr_key = key
  ORDER BY linenum;
-- Get all the recovery catalog versions
cursor rcverQ IS
  SELECT version
  FROM rcver
  ORDER BY version;
cursor reNorm_dfatt_c IS
  SELECT fname
  FROM dfatt
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;
cursor reNorm_orl_c IS
  SELECT fname
  FROM orl
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;
cursor reNorm_al_c IS
  SELECT fname
  FROM al
  where dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;
cursor reNorm_bp_c IS
  SELECT handle
  FROM bp
  WHERE device_type = 'DISK' and db_key = this_db_key
  FOR UPDATE;
cursor reNorm_ccf_c IS
  SELECT fname
  FROM ccf
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;
cursor reNorm_cdf_c IS
  SELECT fname
  FROM cdf
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;
/*----------------------*
* Cursor Row Variables *
*----------------------*/

-- Cursor Row Variables:
--  tsRec
--    Holds 1 row from tsQ.  tsRec.ts# is null when not doing a tablespace
--    resync.  tsRec.ts# is set to MAXNUMVAL when tsQ reaches end-of-fetch.
--  dfRec
--    Holds 1 row from dfQ.  dfRec.file# is null when not doing a datafile
--    resync.  dfRec.file# is set to MAXNUMVAL when dfQ reaches end-of-fetch.
--  rtRec
--    Holds 1 row from rtQ.  rtRec.thread# is null when not doing a datafile
--    resync.  rtRec.thread# is set to MAXNUMVAL when rtQ reaches end-of-fetch.
--  orlRec
--    Holds 1 row from orlQ.  orlRec.fname is null when not doing a datafile
--    resync.  orlRec.fname is set to char(255) when orlQ reaches end-of-fetch.
tsRec           tsQ%rowtype;
dfRec           dfQ%rowtype;
rtRec           rtQ%rowtype;
orlRec          orlQ%rowtype;
/*---------------*
* Private Types *
*---------------*/

/*-------------------*
* Private functions *
*-------------------*/

PROCEDURE setDebugOn IS
BEGIN
   debug := TRUE;
END;
PROCEDURE setDebugOff IS
BEGIN
   debug := FALSE;
END;
PROCEDURE deb(line IN varchar2) IS
BEGIN
   if debug then
      dbms_output.put_line('DBGRCVCAT: '||line);
   end if;
END deb;
-- Ensure that the resync call can go ahead - i.e. beginckpt and setdatabase
-- have been called.
PROCEDURE checkResync IS
BEGIN
  IF (this_ckp_key IS NULL) THEN
    raise_application_error(-20031, 'Resync not started');
  END IF;
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
END checkResync;
-- Note: this is a copy of a function in recover.txt
function date2stamp(dt IN date) return number is
  stamp number;
begin
  stamp := (((((to_number(to_char(dt, 'YYYY'))-1988)*12
         +     (to_number(to_char(dt, 'MM'))-1))*31
         +     (to_number(to_char(dt, 'DD'))-1))*24
         +     (to_number(to_char(dt, 'HH24'))))*60
         +     (to_number(to_char(dt, 'MI'))))*60
         +     (to_number(to_char(dt, 'SS')));
  return stamp;
end;
-- Note: this is a copy of a function in recover.txt
function stamp2date(stamp IN number) return date IS
x number;
dt varchar2(19);
begin
   x := stamp;
   dt := to_char(mod(x,60), 'FM09'); -- seconds
   x := floor(x/60);
   dt := to_char(mod(x,60), 'FM09') || ':' || dt; -- minutes
   x := floor(x/60);
   dt := to_char(mod(x,24), 'FM09') || ':' || dt; -- hours
   x := floor(x/24);
   dt := to_char(mod(x,31)+1, 'FM09') || ' ' || dt; -- days
   x := floor(x/31);
   dt := to_char(mod(x,12)+1, 'FM09') || '/' || dt; -- months
   dt := to_char(floor(x/12)+1988)   || '/' || dt;
   return to_date(dt, 'YYYY/MM/DD HH24:MI:SS');
end;
/*-------------------*
* Register Database *
*-------------------*/

PROCEDURE registerDatabase(db_id      IN NUMBER
                          ,db_name    IN VARCHAR2
                          ,reset_scn  IN NUMBER
                          ,reset_time IN DATE
                          ) IS
local dbinc%rowtype;                    -- local variables
BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible recovery catalog');
  END;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030 , 'Resync in progress');
  END IF;
  this_db_key := NULL;
  this_dbinc_key := NULL;
  BEGIN
    INSERT INTO db(db_key, db_id) VALUES(rman_seq.nextval, db_id);
  EXCEPTION
    WHEN dup_val_on_index THEN
      raise_application_error(-20002, 'Database already registered');
  END;
  SELECT rman_seq.currval INTO local.db_key FROM dual;
  INSERT INTO dbinc
    (dbinc_key, db_key, db_name, reset_scn, reset_time)
  VALUES
    (rman_seq.nextval, local.db_key, db_name, reset_scn,reset_time);
  SELECT rman_seq.currval INTO local.dbinc_key FROM dual;
  UPDATE db SET curr_dbinc_key = local.dbinc_key
  WHERE db.db_key = local.db_key;
  commit;
-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;
END registerDatabase;
-- register a new database incarnation and make it the current incarnation
-- after opening the database with resetlogs option
PROCEDURE resetDatabase(db_id      IN NUMBER
                       ,db_name    IN VARCHAR2
                       ,reset_scn  IN NUMBER
                       ,reset_time IN DATE
                       ,parent_reset_scn  IN NUMBER
                       ,parent_reset_time IN DATE
                       ) IS
local dbinc%rowtype;                    -- local variables
BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible with recovery catalog');
  END;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (db_id IS NULL) THEN
    raise_application_error(-20007, 'db_id is null');
  END IF;
  this_db_key := NULL;
  this_dbinc_key := NULL;
  BEGIN
    SELECT db_key, curr_dbinc_key INTO local.db_key, local.dbinc_key
    FROM db
    WHERE db.db_id = resetDatabase.db_id;               -- should return 1 row
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20001, 'Database not found');
  END;
  -- find the parent of the new incarnation
  BEGIN
    SELECT dbinc_key INTO local.parent_dbinc_key
    FROM dbinc
    WHERE dbinc.db_key = local.db_key
    AND   dbinc.reset_scn = resetDatabase.parent_reset_scn
    AND   dbinc.reset_time = resetDatabase.parent_reset_time;
  EXCEPTION
    WHEN no_data_found THEN
      local.parent_dbinc_key := NULL;
  END;
  -- insert the new incarnation
  BEGIN
    INSERT INTO dbinc
      (dbinc_key, db_key, db_name, reset_scn, reset_time, parent_dbinc_key)
    VALUES
      (rman_seq.nextval, local.db_key, db_name, reset_scn,
       reset_time, local.parent_dbinc_key);
  EXCEPTION
    WHEN dup_val_on_index THEN
      raise_application_error(-20009, 'Db incarnation already registered');
  END;
  SELECT rman_seq.currval INTO local.dbinc_key FROM dual;
  -- and make it the current incarnation of the database
  UPDATE db SET curr_dbinc_key = local.dbinc_key
  WHERE db.db_key = local.db_key;
  commit;
-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;
END resetDatabase;
-- make an existing database incarnation the current incarnation
PROCEDURE resetDatabase(
  dbinc_key  IN NUMBER
,db_name    IN VARCHAR2
) IS
local dbinc%rowtype;                    -- local variables
BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible with recovery catalog');
  END;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (dbinc_key IS NULL) THEN
    raise_application_error(-20008, 'Database incarnation key is missing');
  END IF;
  this_db_key := NULL;
  this_dbinc_key := NULL;
  BEGIN
    SELECT db_key, db_name
    INTO local.db_key, local.db_name
    FROM dbinc
    WHERE dbinc.dbinc_key = resetDatabase.dbinc_key;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20010, 'Database incarnation not found');
  END;
  IF (db_name <> local.db_name OR db_name IS NULL) THEN
    raise_application_error(-20004, 'Database name does not match');
  END IF;
  -- and make it the current incarnation of the database
  UPDATE db SET curr_dbinc_key = resetDatabase.dbinc_key
  WHERE db.db_key = local.db_key;
  commit;
-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;
END resetDatabase;
PROCEDURE unRegisterDatabase(
  db_key     IN NUMBER
,db_id      IN NUMBER
) IS
tmp NUMBER;
BEGIN
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  -- check if the database exists in rcvcat
  BEGIN
    SELECT 0 INTO tmp FROM db
    WHERE db.db_key = unRegisterDatabase.db_key
    AND   db.db_id =  unRegisterDatabase.db_id;
  EXCEPTION
  WHEN no_data_found THEN
    raise_application_error(-20001, 'Database not found');
  END;
  DELETE FROM db WHERE db.db_key = unRegisterDatabase.db_key;
  commit;
-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;
END unRegisterDatabase;
/*--------------*
* Set Database *
*--------------*/

-- This procedure tells the package what target database we are working with.
PROCEDURE setDatabase(db_name    IN VARCHAR2
                     ,reset_scn  IN NUMBER
                     ,reset_time IN DATE
                     ,db_id      IN NUMBER) IS
   local dbinc%rowtype;                    -- local variables
   current_inc VARCHAR2(3);
   dbnm         dbinc.db_name%TYPE;
   rid          char(18);
BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible with recovery catalog');
  END;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  this_db_key := NULL;                  -- clear in case exception raised
  this_dbinc_key := NULL;
  -- If the target database is mounted, then we have the db_id (kccfhdbi).
  -- This can be used to find the row in the db table corresponding
  -- to the target database, and it will indicate which incarnation
  -- is currently considered the current one.
  IF (db_id IS NOT NULL) THEN
    BEGIN
      SELECT db_key, curr_dbinc_key INTO local.db_key, local.dbinc_key
      FROM db
      WHERE db.db_id = setDatabase.db_id;               -- should return 1 row
    EXCEPTION
      WHEN no_data_found THEN
        raise_application_error(-20001, 'Database not found');
    END;
    -- Validate SCN only only if the target database is indeed mounted
    IF (db_name is NOT NULL) THEN
       -- Now validate that the resetlogs SCN we were passed matches that
       -- of the current incarnation of this database.  If not, then
       -- a reset database should be done, or the wrong controlfile is
       -- mounted.
       BEGIN
         SELECT decode(dbinc.dbinc_key, db.curr_dbinc_key, 'YES', 'NO'),
                dbinc.db_name, dbinc.rowid
         INTO   current_inc, dbnm, rid
         FROM db, dbinc
         WHERE db.db_key = dbinc.db_key
         AND   db.db_id = setDatabase.db_id
--         AND   dbinc.db_name = setDatabase.db_name
         AND   dbinc.reset_scn = setDatabase.reset_scn
         AND   dbinc.reset_time = setDatabase.reset_time;
       EXCEPTION
         WHEN no_data_found THEN
           raise_application_error(-20003, 'Database incarnation not found');
       END;
       IF (current_inc = 'NO') THEN
         raise_application_error(-20011, 'Database incarnation not current');
       END IF;
       IF (dbnm != setDatabase.db_name) THEN
          UPDATE dbinc
             SET dbinc.db_name = setDatabase.db_name
           WHERE rowid = rid;
          COMMIT;
       END IF;
   END IF;
  -- if db_id is unknown, try using db_name
  ELSIF (db_name IS NOT NULL) THEN
    BEGIN
      SELECT db.db_key, db.curr_dbinc_key
        INTO local.db_key, local.dbinc_key
        FROM db, dbinc
       WHERE db.curr_dbinc_key = dbinc.dbinc_key
         AND dbinc.db_name   = setDatabase.db_name;
    EXCEPTION
      WHEN no_data_found THEN
        raise_application_error(-20001, 'Database not found');
      WHEN too_many_rows THEN
        raise_application_error(-20005, 'Database name is ambiguous');
    END;
  ELSE
    raise_application_error(-20006, 'Database name is missing');
  END IF;
  this_db_key := local.db_key;
  this_dbinc_key := local.dbinc_key;
  cntbs := 0;
   -- call setDatabase from rcvman package. Note that in this we call
   -- recovery catalog version!
  dbms_rcvman.setDatabase (db_name, reset_scn, reset_time, db_id);
END setDatabase;
-- These two versions of setDatabase are not used by RMAN, they are shorthand
-- methods of invoking setDatabase when you are accessing the recovery catalog
-- schema from a tool like Sql*Plus, and you want to invoke some of the
-- stored procecdures in the dbms_rcvcat or dbms_rcvman packages.
PROCEDURE setDatabase(dbinc_key number) IS
  dbinc_row dbinc%ROWTYPE;
  db_row    db%ROWTYPE;
BEGIN
  select * into dbinc_row from dbinc where dbinc_key = setDatabase.dbinc_key;
  select * into db_row from db where db_key = dbinc_row.db_key;
  setDatabase(dbinc_row.db_name,
              dbinc_row.reset_scn,
              dbinc_row.reset_time,
              db_row.db_id);
END setDatabase;
procedure setDatabase IS
  dbinc_row dbinc%ROWTYPE;
BEGIN
  select * into dbinc_row from dbinc;
  setDatabase(dbinc_row.dbinc_key);
END setDatabase;
/*-----------------------------*
* Recovery Catalog Checkpoint *
*-----------------------------*/

PROCEDURE lockForCkpt IS
local_time           DATE;
BEGIN
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  -- We need to acquire lock on dbinc before reading target database cf
  -- in order to give ckptNeeded with correct water marks.
  -- Otherwise RMAN may signal RMAN-20035 or RMAN-20033 errors when
  -- resync's are done in parallel.
  SELECT cf_create_time INTO local_time FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key FOR UPDATE;
  -- Lock obtained here is either released by ckptNeeded, cancelCkpt
  -- or endCkpt
END;
FUNCTION ckptNeeded(
  ckp_scn         IN NUMBER
,ckp_cf_seq      IN NUMBER
,cf_version      IN DATE
,cf_type         IN NUMBER
,high_df_recid   IN NUMBER
,high_orl_recid  IN NUMBER
,high_cdf_recid  IN NUMBER
,high_al_recid   IN NUMBER
,high_bp_recid   IN NUMBER
,high_do_recid   IN NUMBER
,high_offr_recid IN NUMBER
,high_pc_recid   IN NUMBER DEFAULT NULL -- for compatibility
,high_conf_recid IN NUMBER DEFAULT NULL -- for compatibility
,rltime          IN DATE   DEFAULT NULL -- for compatibility
,high_ts_recid   IN NUMBER DEFAULT NULL -- for compatibility
,high_bs_recid   IN NUMBER DEFAULT NULL -- for compatibility
) RETURN NUMBER IS
ckp_type             NUMBER;
local                dbinc%rowtype;
local_conf_recid     NUMBER;
BEGIN
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  -- Get the controlfile version timestamp and high watermarks from
  -- the recovery catalog. Lock the dbinc record to serialize resyncs.
  -- Note that ckptNeeded function returns with the dbinc record locked
  -- if a resync is needed. The client (RMAN) must call endCkpt or
  -- cancelCkpt to release the lock. Use nvl so we don't need to deal
  -- with nulls.
  SELECT cf_create_time, nvl(high_df_recid,0), nvl(high_ts_recid,0),
         nvl(high_orl_recid,0), nvl(high_cdf_recid,0), nvl(high_al_recid,0),
         nvl(high_bp_recid,0), nvl(high_do_recid,0), nvl(high_offr_recid,0),
         nvl(high_pc_recid,0), full_ckp_cf_seq, job_ckp_cf_seq,
         reset_time, nvl(high_bs_recid,0)
  INTO   local.cf_create_time, local.high_df_recid, local.high_ts_recid,
         local.high_orl_recid, local.high_cdf_recid, local.high_al_recid,
         local.high_bp_recid,  local.high_do_recid, local.high_offr_recid,
         local.high_pc_recid,  local.full_ckp_cf_seq, local.job_ckp_cf_seq,
         local.reset_time, local.high_bs_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key FOR UPDATE;
  -- In case of configuration, the high_conf_recid number is stored
  -- db table.
  SELECT nvl(high_conf_recid,0)
  INTO   local_conf_recid
  FROM   db
  WHERE  db.db_key = this_db_key FOR UPDATE;
  ckp_type := RESYNC_NONE;
  IF (rltime IS NOT NULL AND rltime != local.reset_time) THEN
    -- We have not yet issued a RESET DATABASE after doing RESETLOGS,
    -- or we are not using the latest incarnation.  In either case, we
    -- don't want to do implicit resync now, so tell caller resync is not
    -- needed now.
    -- rltime will be NULL if called from a PRE-8.2 RMAN, in which case
    -- we cannot check it, nor is the check needed for PRE-8.2.
    deb('ckptNeeded: rltime='||to_char(rltime)||
                  ', local.reset_time='||to_char(local.reset_time));
    ckp_type := RESYNC_NONE;
    GOTO ret;
  ELSIF (cf_version = local.cf_create_time) THEN
    -- The controlfile is the same as the one seen during the last resync,
    -- so the high watermarks can be used to determine whether a resync is
    -- needed.  Full resync is possible only from a current controlfile,
    -- skip full resync checks unless the controlfile is current.
    IF (cf_type = CF_CURRENT) THEN
      deb('ckptNeeded: high_df_recid='      ||to_char(high_df_recid)||
                    ', local.high_df_recid='||to_char(local.high_df_recid));
      IF (high_df_recid > local.high_df_recid) THEN
        ckp_type := RESYNC_FULL;
        GOTO ret;
      ELSIF (high_df_recid < local.high_df_recid) THEN
        -- the high recid in the controlfile should never be less than
        -- the one in the recovery catalog. If we ever get here it probably
        -- means that the user made an operational error such as restoring
        -- controlfile from an old copy made using os utilities.
        raise_application_error(-20035, 'Invalid high recid');
      END IF;
      deb('ckptNeeded: high_ts_recid='      ||to_char(high_ts_recid)||
                    ', local.high_ts_recid='||to_char(local.high_ts_recid));
      IF (high_ts_recid > local.high_ts_recid) THEN
        ckp_type := RESYNC_FULL;
        GOTO ret;
      ELSIF (high_ts_recid < local.high_ts_recid) THEN
        raise_application_error(-20035, 'Invalid high recid');
      END IF;
      deb('ckptNeeded: high_orl_recid='      ||to_char(high_orl_recid)||
                    ', local.high_orl_recid='||to_char(local.high_orl_recid));
      IF (high_orl_recid > local.high_orl_recid) THEN
        ckp_type := RESYNC_FULL;
        GOTO ret;
      ELSIF (high_orl_recid < local.high_orl_recid) THEN
        raise_application_error(-20035, 'Invalid high recid');
      END IF;
      -- in case that we resync configuration records then we will
      -- resync in both directions. Why? Because rc_rman_configuration
      -- is not unique per database incarnaction. It is unique per
      -- database id.
      IF (high_conf_recid != local_conf_recid) THEN
        ckp_type := RESYNC_FULL;
        GOTO ret;
      END IF;
    END IF;
    deb('ckptNeeded: high_cdf_recid='||to_char(high_cdf_recid)||
                  ', local.high_cdf_recid='||to_char(local.high_cdf_recid));
    IF (high_cdf_recid > local.high_cdf_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_cdf_recid < local.high_cdf_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
    deb('ckptNeeded: high_al_recid='||to_char(high_al_recid)||
                  ', local.high_al_recid='||to_char(local.high_al_recid));
    IF (high_al_recid > local.high_al_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_al_recid < local.high_al_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
    deb('ckptNeeded: high_bp_recid='||to_char(high_bp_recid)||
                  ', local.high_bp_recid='||to_char(local.high_bp_recid));
    IF (high_bp_recid > local.high_bp_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_bp_recid < local.high_bp_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
    deb('ckptNeeded: high_bs_recid='||to_char(high_bs_recid)||
                  ', local.high_bs_recid='||to_char(local.high_bs_recid));
    IF (high_bs_recid > local.high_bs_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_bs_recid < local.high_bs_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
    deb('ckptNeeded: high_do_recid='||to_char(high_do_recid)||
                  ', local.high_do_recid='||to_char(local.high_do_recid));
    IF (high_do_recid > local.high_do_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_do_recid < local.high_do_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
    deb('ckptNeeded: high_offr_recid='||to_char(high_offr_recid)||
                  ', local.high_offr_recid='||to_char(local.high_offr_recid));
    IF (high_offr_recid > local.high_offr_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_offr_recid < local.high_offr_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
    deb('ckptNeeded: high_pc_recid='||to_char(high_pc_recid)||
                  ', local.high_pc_recid='||to_char(local.high_pc_recid));
    IF (high_pc_recid > local.high_pc_recid) THEN
      ckp_type := RESYNC_PARTIAL;
      GOTO ret;
    ELSIF (high_pc_recid < local.high_pc_recid) THEN
      raise_application_error(-20035, 'Invalid high recid');
    END IF;
  ELSE
    -- The controlfile is different from the one seen at the last resync.
    -- If the control file is current then the database must have been
    -- opened since the restore and a full resync is needed.  Otherwise
    -- we can only do a partial resync.
    IF (cf_type = CF_CURRENT) THEN
      deb('ckptNeeded: cf_type = CF_CURRENT');
      ckp_type := RESYNC_FULL;
    ELSE
      deb('ckptNeeded: cf_type != CF_CURRENT');
      ckp_type := RESYNC_PARTIAL;
    END IF;
  END IF;
<<ret>>
  -- If it looks like we need a partial resync, but this is the same controfile
  -- as last time and the cf_seq has not advanced, then we don't need a resync.
  -- One of the circular record high water marks may still be zero because
  -- we haven't been passed any records since we reset the high water mark
  -- to zero.  This happens when a backup controlfile is mounted and no
  -- new circular records have been added for some record type.
  IF (ckp_type = RESYNC_PARTIAL AND
      cf_version = local.cf_create_time AND
      ckp_cf_seq = greatest(local.job_ckp_cf_seq, local.full_ckp_cf_seq)) THEN
     deb('ckptNeeded: cf_seq has not advanced - do not need a resync');
     ckp_type := RESYNC_NONE;
  END IF;
  -- if resync is not needed, release the lock on dbinc and conf.
  IF (ckp_type = RESYNC_NONE) THEN
    rollback;
  END IF;
  deb('ckptNeeded: returning ckp_type='||ckp_type);
  RETURN ckp_type;
-- rollback on error to release the lock on dbinc
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;
END ckptNeeded;
PROCEDURE beginCkpt(
  ckp_scn       IN NUMBER
,ckp_cf_seq    IN NUMBER
,cf_version    IN DATE
,ckp_time      IN DATE
,ckp_type      IN VARCHAR2
,ckp_db_status IN VARCHAR2
,high_df_recid IN NUMBER
,cf_type       IN VARCHAR2 DEFAULT 'CURRENT'   -- for compatibility reasons
) IS
local   ckp%rowtype;
BEGIN
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  -- We do not want to process any circular record with a stamp that is
  -- less than kccdivts.  These records are records in a backup controlfile
  -- that existed at the time the controlfile was made into a backup.
  -- We don't want to process them because they could pollute the recovery
  -- catalog with obsolete records for things that have been deleted since
  -- the backup controlfile was created.  recover.bsq will not pass us such
  -- records, but it seems like a good idea for this package to enforce
  -- this anyway.
  kccdivts := date2stamp(cf_version);           -- save in pkg global
  -- lock the dbinc to allow only one checkpoint at a time (per dbinc) and
  -- and select the information needed to ensure that the checkpoint is valid
  SELECT dbinc_key, ckp_scn, cf_create_time,
         decode(beginCkpt.ckp_type, 'FULL', full_ckp_cf_seq,
                greatest(job_ckp_cf_seq, full_ckp_cf_seq))
  INTO local.dbinc_key, local.ckp_scn, local.cf_create_time, local.ckp_cf_seq
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key FOR UPDATE;
  -- find the previous checkpoint for this dbinc
  SELECT max(ckp_key)
    INTO local.ckp_key
    FROM ckp
    WHERE dbinc_key = this_dbinc_key;
  IF (local.ckp_key IS NULL) THEN
    -- this the first checkpoint since registering this database incarnation,
    -- so there are no previous checkpoints to validate against. Just update
    -- the ckp_scn and ckp_cf_seqs for the next resync.
    UPDATE dbinc SET
      cf_create_time = beginCkpt.cf_version,
      ckp_scn =
        decode(beginCkpt.ckp_type, 'FULL', beginCkpt.ckp_scn, 0),
      full_ckp_cf_seq =
        decode(beginCkpt.ckp_type, 'FULL', beginCkpt.ckp_cf_seq, 0),
      job_ckp_cf_seq =
        decode(beginCkpt.ckp_type, 'PARTIAL', beginCkpt.ckp_cf_seq, 0)
    WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSIF (cf_type = 'CURRENT') THEN
    -- If this is a full resync then make sure that the controlfile
    -- checkpoint scn is not less than the highest checkpoint scn so far.
    -- We cannot allow full resync from an old controlfile because it might
    -- logically corrupt the tablespace and datafile information.
    -- This check is skipped for a partial resync doesn't usually have
    -- a checkpoint scn and partial resync from an old controlfile is harmless
    IF (ckp_type = 'FULL') THEN
      IF  (ckp_scn < local.ckp_scn) THEN
        deb('cf scn='||ckp_scn||',catalog cf scn='||local.ckp_scn);
        raise_application_error(-20032, 'Invalid checkpoint SCN');
      ELSIF (ckp_scn = local.ckp_scn AND ckp_cf_seq < local.ckp_cf_seq) THEN
        deb('cf seq='||ckp_cf_seq||',catalog cf seq='||local.ckp_cf_seq);
        raise_application_error(-20033, 'Invalid checkpoint cf seq#');
      ELSIF (ckp_scn = local.ckp_scn AND ckp_cf_seq = local.ckp_cf_seq) THEN
        raise_application_error(-20034, 'Resync not needed');
      END IF;
    END IF;
    IF (cf_version = local.cf_create_time) THEN
      -- Since the cf_version (kccdivts) is the same as last time, this
      -- controlfile is the same controlfile from which we last resynced, so
      -- ckp_cf_seq must advance.  If ckp_cf_seqs are the same then the
      -- controlfile has not changed since the previous resync, so resync is
      -- not needed.
      IF (ckp_cf_seq < local.ckp_cf_seq) THEN
        deb('cf seq='||ckp_cf_seq||',catalog cf seq='||local.ckp_cf_seq);
        raise_application_error(-20033, 'Invalid checkpoint cf seq#');
      ELSIF (ckp_cf_seq = local.ckp_cf_seq) THEN
        raise_application_error(-20034, 'Resync not needed');
      END IF;
      -- update the ckp_scn and ckp_cf_seqs for the next resync.
      UPDATE dbinc SET
        ckp_scn =
          greatest(ckp_scn,
            decode(beginCkpt.ckp_type, 'FULL', beginCkpt.ckp_scn, 0)),
        full_ckp_cf_seq =
          greatest(full_ckp_cf_seq,
            decode(beginCkpt.ckp_type, 'FULL', beginCkpt.ckp_cf_seq, 0)),
        job_ckp_cf_seq =
          greatest(job_ckp_cf_seq,
            decode(beginCkpt.ckp_type, 'PARTIAL', beginCkpt.ckp_cf_seq, 0))
      WHERE dbinc.dbinc_key = this_dbinc_key;
    ELSE
      -- The controlfile has been recreated since the previous checkpoint.
      -- Reset the recid highwater marks to nulls so that the resync will
      -- process all records from the controlfile.
      UPDATE dbinc SET
        cf_create_time = beginCkpt.cf_version,
        ckp_scn =
          decode(beginCkpt.ckp_type, 'FULL', beginCkpt.ckp_scn, 0),
        full_ckp_cf_seq =
          decode(beginCkpt.ckp_type, 'FULL', beginCkpt.ckp_cf_seq, 0),
        job_ckp_cf_seq =
          decode(beginCkpt.ckp_type, 'PARTIAL', beginCkpt.ckp_cf_seq, 0),
        high_ts_recid = NULL,
        high_df_recid = NULL,
        high_rt_recid = NULL,
        high_orl_recid = NULL,
        high_offr_recid = 0,
        high_rlh_recid = 0,
        high_al_recid = 0,
        high_bs_recid = 0,
        high_bp_recid = 0,
        high_bdf_recid = 0,
        high_cdf_recid = 0,
        high_brl_recid = 0,
        high_bcb_recid = 0,
        high_ccb_recid = 0,
        high_do_recid = 0,
        high_pc_recid = 0,
        high_bsf_recid = 0
      WHERE dbinc.dbinc_key = this_dbinc_key;
    END IF;
  END IF;
  -- record resyncs
  BEGIN
    INSERT INTO ckp
       (ckp_key, ckp_scn, ckp_cf_seq, cf_create_time, ckp_time,
        dbinc_key, ckp_type, ckp_db_status, resync_time, high_df_recid)
    VALUES
       (rman_seq.nextval, ckp_scn, ckp_cf_seq, cf_version, ckp_time,
        this_dbinc_key, ckp_type, ckp_db_status, sysdate, high_df_recid);
    -- set package state variables
    SELECT rman_seq.currval INTO this_ckp_key FROM dual;
  EXCEPTION
    WHEN dup_val_on_index THEN
      IF cf_type != 'CURRENT' THEN
        -- this is a non-current controlfile resync. Because we don't track the
        -- high water marks for this type of controlfile, ckptNeeded
        -- always returns PARTIAL resync for non-current controlfile.
        -- So, we optimize here that resync is not needed if no controlfile
        -- txn is done. But, if some controlfile txn is done, then ALL
        -- circular records (including duplicate) are resynced.
        raise_application_error(-20034, 'Resync not needed');
      ELSE
        RAISE;
      END IF;
  END;
                
  this_ckp_scn := ckp_scn;
  this_ckp_time := ckp_time;
  this_cf_type := cf_type;
-- rollback on error to release the lock on dbinc
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;
END beginCkpt;
PROCEDURE endCkpt IS
BEGIN
  checkResync;
  IF (tsRec.ts# IS NOT NULL) THEN
    raise_application_error(-20041, 'Tablespace resync not completed');
  END IF;
  IF (dfRec.file# IS NOT NULL) THEN
    raise_application_error(-20051, 'Datafile resync not completed');
  END IF;
  commit;                               -- commit all of our changes
  this_ckp_key := NULL;                 -- and update state variable
  this_ckp_scn := NULL;
  this_ckp_time := NULL;
  this_cf_type := NULL;
END endCkpt;
PROCEDURE cancelCkpt IS
BEGIN
  rollback;
  IF (this_ckp_key IS NOT NULL) THEN
    -- rollback and reset state variables
    this_ckp_key := NULL;
    this_ckp_scn := NULL;
    this_ckp_time := NULL;
  END IF;
  IF tsQ%ISOPEN  THEN CLOSE tsQ;  END IF;
  IF dfQ%ISOPEN  THEN CLOSE dfQ;  END IF;
  IF rtQ%ISOPEN  THEN CLOSE rtQ;  END IF;
  IF orlQ%ISOPEN THEN CLOSE orlQ; END IF;
  IF bpq%ISOPEN  THEN CLOSE bpq;  END IF;
END cancelCkpt;
/*-------------------*
* Tablespace Resync *
*-------------------*/

PROCEDURE fetchTs IS                    -- this is private to the pkg body
BEGIN
  FETCH tsQ INTO tsRec;                 -- get next row
  IF tsQ%NOTFOUND THEN
    tsRec.ts# := MAXNUMVAL;             -- indicate end of fetch
    CLOSE tsQ;
  END IF;
END fetchTs;
PROCEDURE addTs(
  ts_name                     IN VARCHAR2
,ts#                         IN NUMBER
,create_scn                  IN NUMBER
,create_time                 IN DATE
,rbs_count                   IN NUMBER
,included_in_database_backup IN VARCHAR2
) IS
BEGIN
  INSERT INTO ts
    (dbinc_key, ts#, ts_name, create_scn,
     create_time, included_in_database_backup)
  VALUES
    (this_dbinc_key, ts#, ts_name, create_scn, create_time,
     included_in_database_backup);
  INSERT INTO tsatt(dbinc_key, ts#, create_scn, start_ckp_key, rbs_count)
  VALUES (this_dbinc_key, ts#, create_scn, this_ckp_key, rbs_count);
END addTs;
PROCEDURE dropTs(                       -- private to package body
  ts#        IN NUMBER
,create_scn IN NUMBER
,drop_scn   IN NUMBER
,drop_time  IN DATE
) IS
BEGIN
  UPDATE ts SET
    drop_scn     = dropTs.drop_scn,
    drop_time    = dropTs.drop_time
  WHERE ts.dbinc_key = this_dbinc_key
  AND   ts.ts#       = dropTs.ts#
  AND   ts.create_scn = dropTs.create_scn;
END dropTs;
FUNCTION beginTableSpaceResync(
  high_ts_recid IN NUMBER,
  force         IN BOOLEAN DEFAULT FALSE)
RETURN BOOLEAN IS
BEGIN
  checkResync;
  -- if the force is TRUE that means we want resync even if righ_ts_recid
  -- is smaller than high_ts_recid.
  -- if the high_ts_recid in the controlfile is equal to the high_ts_recid
  -- stored in the rcvcat then the tablespace information in the controlfile
  -- has not changed since the previous resync, so there is no reason to
  -- resync it. If the high_ts_recid has been incremented since the previous
  -- resync then the tablespace information needs to be resynced again.
  SELECT high_ts_recid INTO last_ts_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;
  IF (high_ts_recid = last_ts_recid AND NOT force) THEN
    RETURN FALSE;
  ELSIF (high_ts_recid > last_ts_recid OR last_ts_recid IS NULL OR
         high_ts_recid IS NULL OR force) THEN
    last_ts_recid := high_ts_recid;
    OPEN tsQ;                           -- just open that cursor please
    fetchTs;                            -- do priming read
    last_ts# := -1;                     -- initialize for ordering assert
    RETURN TRUE;
  ELSE
    raise_application_error(-20035, 'Invalid high recid');
  END IF;
END beginTableSpaceResync;
PROCEDURE checkTableSpace(
  ts_name                     IN VARCHAR2
,ts#                         IN NUMBER
,create_scn                  IN NUMBER
,create_time                 IN DATE
,rbs_count                   IN NUMBER   DEFAULT NULL
,included_in_database_backup IN VARCHAR2 DEFAULT NULL
) IS
-- Bug 1478785.
-- 8.0.5- rman versions doesn't accept default value as string.
-- To maintain compatibility pass NULL as default value.
idb    varchar2(3) := nvl(included_in_database_backup, 'YES');
                                                   -- actual default value
BEGIN
  IF (tsRec.ts# IS NULL) THEN   -- assert beginTableSpaceResync was called
    raise_application_error(-20040, 'Tablespace resync not started');
  END IF;
  IF (last_ts# >= ts#) THEN             -- assert rows passed in ascending
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  last_ts# := ts#;                      -- for checking next time
  -- all tablespaces that exist at a checkpoint  must have
  -- create_scn <= ckp_scn. Assert this since the correctness of
  -- rc_ckp_tablespace view depends on this assumption
  IF (create_scn > this_ckp_scn) THEN
    raise_application_error(-20042, 'Invalid tablespace create SCN');
  END IF;
  -- If the current tablespace in tsRec has a lower ts# than the tablespace
  -- we are currently checking, then it must have been dropped.  Note
  -- multiple such tablespaces may exist in the recovery catalog, so drop
  -- all such.
  WHILE (ts# > tsRec.ts#) LOOP
    dropTs(tsRec.ts#, tsRec.create_scn, this_ckp_scn, this_ckp_time);
    fetchTs;
  END LOOP;
  IF (ts# < tsRec.ts#) THEN
    -- this tablespace is new and must be inserted to rcvcat
    addTs(ts_name, ts#, create_scn, create_time, rbs_count,idb);
  ELSE -- (ts# = tsRec.ts#)
    IF (create_scn = tsRec.create_scn) THEN
      -- this is an existing tablespace which is already recorded in rcvcat
      -- check that create_time and ts_name match
      IF (create_time <> tsRec.create_time) THEN
        raise_application_error(-20043, 'Invalid tablespace create time');
      END IF;
      IF (ts_name <> tsRec.ts_name) THEN
        raise_application_error(-20044, 'Invalid tablespace name');
      END IF;
      -- if included_in_database_backup field has changed then update ts table
      -- note that the initial value may be null, hence the nvl function
      IF (idb <> nvl(tsRec.included_in_database_backup,'XX')) THEN
        UPDATE ts SET ts.included_in_database_backup =
                                  checkTableSpace.included_in_database_backup
        WHERE  ts.dbinc_key  = this_dbinc_key
        AND    ts.ts#        = tsRec.ts#
        AND    ts.create_scn = tsRec.create_scn;
      END IF;
      -- if rbs_count field has changed to a not null value, update tsatt
      -- note that the initial value may be null, hence the nvl function
      IF (rbs_count <> nvl(tsRec.rbs_count,-1)) THEN
        UPDATE tsatt SET end_ckp_key = this_ckp_key
        WHERE tsatt.dbinc_key = this_dbinc_key
        AND   tsatt.ts# = tsRec.ts#
        AND   tsatt.create_scn = tsRec.create_scn
        AND   tsatt.end_ckp_key IS NULL;
        INSERT INTO tsatt(dbinc_key, ts#, create_scn, start_ckp_key, rbs_count)
        VALUES(this_dbinc_key, tsRec.ts#, tsRec.create_scn, this_ckp_key,
               rbs_count);
      END IF;
    ELSIF (create_scn > tsRec.create_scn) THEN
      -- this tablespace has been recreated, mark the old one dropped and
      -- insert the new one into recovery catalog
      dropTs(tsRec.ts#, tsRec.create_scn, create_scn, create_time);
      addTs(ts_name, ts#, create_scn, create_time, rbs_count, idb);
    ELSE -- (create_scn < tsRec.create_scn)
      -- The client is passing us a tablespace with a lower creation SCN
      -- than the one we currently have listed in the rcvcat for this ts#.
      -- This is a big NO NO.  Probably the target database has an old
      -- controlfile mounted.  Signal an error.
      raise_application_error(-20042, 'Invalid tablespace creation change#');
    END IF;
    fetchTS;                            -- get next row from TS cursor
  END IF; -- (ts# < tsRec.ts)
END checkTableSpace;
PROCEDURE endTableSpaceResync IS
BEGIN
  checkResync;
  WHILE (tsRec.ts# < MAXNUMVAL) LOOP    -- while extra tablespaces in rcvcat
    dropTs(tsRec.ts#, tsRec.create_scn, this_ckp_scn, this_ckp_time);
    fetchTs;
  END LOOP;
  -- set the state variable to indicate that tablespace resync is done
  tsRec.ts# := NULL;
  -- update high_ts_resync for the next resync
  UPDATE dbinc SET high_ts_recid = nvl(last_ts_recid, high_ts_recid)
  WHERE dbinc.dbinc_key = this_dbinc_key;
  last_ts_recid := NULL;
END endTableSpaceResync;
/*-----------------*
* Datafile Resync *
*-----------------*/

PROCEDURE fetchDF IS                    -- private to package body
BEGIN
  FETCH dfQ INTO dfRec;
  IF dfQ%NOTFOUND THEN
    dfRec.file# := MAXNUMVAL;           -- indicate end-of-fetch
    CLOSE dfQ;
  END IF;
END fetchDF;
PROCEDURE addDF(file#       IN NUMBER,  -- private to package body
                fname       IN VARCHAR2,
                create_time IN DATE,
                create_scn  IN NUMBER,
                blocks      IN NUMBER,
                block_size  IN NUMBER,
                ts#         IN NUMBER,
                stop_scn    IN NUMBER,
                stop_time   IN DATE,
                read_only   IN number,
                rfile#      IN NUMBER) IS
ts_create_scn NUMBER;
BEGIN
  SELECT create_scn INTO ts_create_scn
  FROM ts
  WHERE ts.dbinc_key = this_dbinc_key
  AND   ts.ts# = addDF.ts#
  AND   ts.drop_scn IS NULL;            -- in case ts#'s are reused
  INSERT INTO df(dbinc_key, file#, create_scn, create_time,
                 ts#, ts_create_scn, block_size, stop_scn, stop_time,
                 read_only, rfile#)
  VALUES(this_dbinc_key, file#, create_scn, create_time, ts#, ts_create_scn,
         block_size, stop_scn, stop_time, read_only, rfile#);
  --
  -- Bug 1332121: Insert 0 for blocks rather than NULL for new records
  -- of datafile as 'list' command in 8.1.5- RMAN will fail with ORA-1405.
  --
  INSERT INTO dfatt(dbinc_key, file#, create_scn, start_ckp_key, fname, blocks)
  VALUES(this_dbinc_key, file#, create_scn, this_ckp_key, fname,
         nvl(blocks,0));
END addDf;
procedure setDatafileSize(file#      IN number
                         ,create_scn IN number
                         ,blocks     IN number) IS
begin
  if (this_dbinc_key is NULL) then
    raise_application_error(-20020, 'Database incarnation not set');
  end if;
  update dfatt
  set dfatt.blocks = setDatafileSize.blocks
  where dbinc_key = this_dbinc_key
  and   dfatt.file# = setDatafileSize.file#
  and   dfatt.create_scn = setDatafileSize.create_scn
  and   end_ckp_key is null;
  commit;
end setDatafileSize;
PROCEDURE dropDf(                       -- private to package body
  file# IN NUMBER
,create_scn IN NUMBER
,drop_scn IN NUMBER
,drop_time IN DATE
) IS
BEGIN
  -- adjust the drop_scn and drop_time of the tablespace. We can do this
  -- because datafiles are always dropped with their tablespaces.
--  update ts set
--    drop_scn     = least(drop_scn,dropDf.drop_scn),
--    drop_time    = least(drop_time,dropDf.drop_time)
--  where ts_key =
--    (select ts_key from df
--     where df_key = dropDf.df_key);
  UPDATE df SET
    drop_scn     = dropDf.drop_scn,
    drop_time    = dropDf.drop_time
  WHERE df.dbinc_key  = this_dbinc_key
  AND   df.file#      = dropDf.file#
  AND   df.create_scn = dropDf.create_scn;
END dropDf;
FUNCTION beginDataFileResync(
  high_df_recid IN NUMBER
) RETURN BOOLEAN IS
BEGIN
  checkResync;
  IF (tsRec.ts# IS NOT NULL) THEN
    raise_application_error(-20041, 'Tablespace resync not completed');
  END IF;
  SELECT high_df_recid INTO last_df_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;
  IF (high_df_recid = last_df_recid) THEN
    RETURN FALSE;
  ELSIF (high_df_recid > last_df_recid OR last_df_recid IS NULL) THEN
    last_df_recid := high_df_recid;
    OPEN dfQ;
    fetchDf;                            -- do priming read
    last_file# := -1;                   -- initialize for ordering assert
    RETURN TRUE;
  ELSE
    raise_application_error(-20035, 'Invalid high recid');
  END IF;
END beginDataFileResync;
PROCEDURE checkDataFile(file#         IN  NUMBER,
                        fname         IN  VARCHAR2,
                        create_scn    IN  NUMBER,
                        create_time   IN  DATE,
                        blocks        IN  NUMBER,
                        block_size    IN  NUMBER,
                        ts#           IN  NUMBER,
                        stop_scn      IN  NUMBER,
                        read_only     IN  NUMBER,
                        stop_time     IN  DATE     DEFAULT NULL,
                        rfile#        IN  NUMBER   DEFAULT NULL,
                        aux_fname     IN  VARCHAR2 DEFAULT NULL)
                        -- default to null for backwards compatibility
IS
BEGIN
   IF (dfRec.file# IS NULL) THEN -- assert beginDataFileResync was called
      raise_application_error(-20050, 'Datafile resync not started');
   END IF;
   IF (last_file# >= file#) THEN        -- assert rows passed in ascending
      raise_application_error(-20036, 'Invalid record order');
   END IF;
   last_file# := file#;                 -- for checking next call
   IF (create_scn > this_ckp_scn) THEN
      raise_application_error(-20052, 'Invalid datafile create SCN');
   END IF;
   -- if the datafile in dfRec has a lower file# than the datafile
   -- we are currently checking, it means that the datafile in dfRec has been
   -- dropped (with its tablespace) and the file# is not currently in use.
   -- We mark the file dropped at ckp_scn - 1 since we can't find out the
   -- exact drop scn.
   WHILE (file# > dfRec.file#) LOOP
      dropDf(dfRec.file#, dfRec.create_scn, this_ckp_scn, this_ckp_time);
      fetchDf;
   END LOOP;
   IF (file# < dfRec.file#) THEN
      -- this datafile is new and must be inserted into rcvcat
      addDF(file#, fname, create_time, create_scn, blocks, block_size, ts#,
            stop_scn, stop_time, read_only, rfile#);
       -- set the database clonename (alias aux_name)
       -- Note that in case that RMAN is 9.0 or greater then aux_fname cannot
       -- be NULL. So, if aux_fname is NULL we will not change it.
      IF (aux_fname is not NULL) THEN
        setCloneName(dfRec.file#, dfRec.create_scn, aux_fname);
      END IF;
   ELSE -- (file# = dfRec.file#)
      IF (create_scn = dfRec.create_scn) THEN
         -- this is an existing datafile which is already recorded in rcvcat
         -- check that create_time and ts# match
         IF (create_time <> dfRec.create_time) THEN
            raise_application_error(-20053, 'Invalid datafile create time');
         END IF;
         IF (ts# <> dfRec.ts#) THEN
            raise_application_error(-20054, 'Invalid datafile ts#');
         END IF;
         -- if fname or blocks have changed then update dfatt
         -- mark datafile copies with same name deleted too###
         IF (fname <> dfRec.fname OR blocks <> dfrec.blocks) THEN
           -- If the new datafile name is the same as the clone_name for
           -- the datafile, then set the cloneName to be the old filename.
           -- We presume that the old filename is a valid file and is suitable
           -- for use as the clone name because it was suitable as the real
           -- filename up until now.
           IF (fname = dfRec.clone_fname) THEN
               setCloneName(dfRec.file#, dfRec.create_scn, dfRec.fname);
           END IF;
           UPDATE dfatt                 -- old dfatt now obsolete
              SET end_ckp_key = this_ckp_key
              WHERE dfatt.dbinc_key = this_dbinc_key
              AND   dfatt.file# = dfRec.file#
              AND   dfatt.create_scn = dfRec.create_scn
              AND   dfatt.end_ckp_key IS NULL;
            INSERT INTO dfatt(dbinc_key, file#, create_scn, start_ckp_key,
              end_ckp_key, fname, blocks)
              VALUES(this_dbinc_key, dfRec.file#, dfRec.create_scn,
              this_ckp_key, NULL, fname, blocks);
         END IF;
         -- If the stop SCN has changed, update the df record.
         IF ((stop_scn <> dfrec.stop_scn) OR
             (stop_scn IS NULL AND dfrec.stop_scn IS NOT NULL) OR
             (stop_scn IS NOT NULL AND dfrec.stop_scn IS NULL)) THEN
            UPDATE df SET
              stop_scn     = checkDataFile.stop_scn,
              stop_time    = checkDataFile.stop_time,
              read_only    = checkDataFile.read_only
              WHERE df.dbinc_key  = this_dbinc_key
              AND   df.file#      = dfRec.file#
              AND   df.create_scn = dfRec.create_scn;
         END IF;
         -- If the aux_fname has changed, update the df record.
         -- Note that in case that RMAN is 9.0 or greater then aux_fname cannot
         -- be NULL.
         IF (aux_fname is not NULL) THEN
            setCloneName(dfRec.file#, dfRec.create_scn, aux_fname);
         END IF;
      ELSIF (create_scn > dfRec.create_scn) THEN
         -- this datafile has been been recreated
         -- We could probably assert that the tablespace that contained the old
         -- incarnation of this datafile has been dropped, but I'd
         -- rather not be that strict.
         -- The old incarnation of the datafile must have been dropped before
         -- new incarnation was created, so mark the old one dropped at
         -- create_scn. This guarantees that it never appears that two
         -- incarnations of the same datafile existed at the same scn time.
         dropDf(dfRec.file#, dfRec.create_scn, create_scn, create_time);
         addDf(file#, fname, create_time, create_scn, blocks, block_size, ts#,
           stop_scn, stop_time, read_only, rfile#);
      ELSE -- (create_scn < dfRec.create_scn)
         -- The client passed us a create SCN for this datafile that is
         -- less than the SCN in the rcvcat.  I.e., the target database's
         -- controlfile now contains a previous incarnation of this datafile.
         -- This can happen only if the user has some old controlfile, or
         -- has done a resetlogs and not told us about it.
         raise_application_error(-20052, 'Invalid datafile create SCN');
      END IF;
      fetchDF;                          -- get next row from DF cursor
   END IF; -- (file# < dfRec.file#)
END checkDataFile;
PROCEDURE endDataFileResync IS
BEGIN
  checkResync;
  -- check if there are any datafiles in rcvcat that the client didn't check
  WHILE (dfRec.file# < MAXNUMVAL) LOOP
    -- if we ever allow drop datafile, replace error signalling with dropDf
    dropDf(dfRec.file#, dfRec.create_scn, this_ckp_scn, this_ckp_time);
    fetchDf;
  END LOOP;
  -- set the state variable to indicate that datafile resync is done
  dfRec.file# := NULL;
  -- update high_df_resync for the next resync
  UPDATE dbinc SET high_df_recid = last_df_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;
  last_df_recid := NULL;
END endDataFileResync;
/*---------------------*
* Redo Thread resync  *
*---------------------*/

PROCEDURE fetchRt IS
BEGIN
  FETCH rtQ INTO rtRec;
  IF rtQ%NOTFOUND THEN
    rtRec.thread# := MAXNUMVAL;
    CLOSE rtQ;
  END IF;
END fetchRt;
PROCEDURE addRt(
  thread#        IN NUMBER
,last_sequence# IN NUMBER
,enable_scn     IN NUMBER
,enable_time    IN DATE
,disable_scn    IN NUMBER
,disable_time   IN DATE
,status         IN VARCHAR2
) IS
BEGIN
  INSERT INTO rt
    (dbinc_key, thread#, sequence#,
     enable_scn, enable_time, disable_scn, disable_time, status)
  VALUES
    (this_dbinc_key, thread#, last_sequence#,
     enable_scn, enable_time, disable_scn, disable_time, status);
END addRt;
PROCEDURE dropRt(thread# IN NUMBER) IS
BEGIN
--  update rt set drop_ckp_key = this_ckp_key
  DELETE FROM rt
  WHERE rt.dbinc_key = this_dbinc_key
  AND   rt.thread#   = dropRt.thread#;
END dropRt;
FUNCTION beginThreadResync(
  high_rt_recid IN NUMBER
) RETURN BOOLEAN IS
BEGIN
  checkResync;
  SELECT high_rt_recid INTO last_rt_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;
  IF (high_rt_recid = last_rt_recid) THEN
    RETURN FALSE;
  ELSIF (high_rt_recid > last_rt_recid OR last_rt_recid IS NULL) THEN
    last_rt_recid := high_rt_recid;
    OPEN rtQ;
    fetchRt;                            -- do priming read
    last_thread# := -1;
    RETURN TRUE;
  ELSE
    raise_application_error(-20035, 'Invalid high recid');
  END IF;
END beginThreadResync;
PROCEDURE checkThread(
  thread#        IN NUMBER
,last_sequence# IN NUMBER
,enable_scn     IN NUMBER
,enable_time    IN DATE
,disable_scn    IN NUMBER
,disable_time   IN DATE
,status         IN VARCHAR2
) IS
BEGIN
  IF (rtRec.thread# IS NULL) THEN
    raise_application_error(-20061, 'Thread resync not started');
  END IF;
  IF (last_thread# >= thread#) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  last_thread# := thread#;
  WHILE (thread# > rtRec.thread#) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropRt(rtRec.thread#);
    fetchRt;
  END LOOP;
  IF (thread# < rtRec.thread#) THEN
    -- this thread is new and must be inserted into rcvcat
    addRt(thread#, last_sequence#, enable_scn, enable_time,
          disable_scn, disable_time, status);
  ELSE -- (thread# = rtRec.thread#)
    -- this is an existing thread, just update the information
    UPDATE rt SET
      sequence# = checkThread.last_sequence#,
      enable_scn = checkThread.enable_scn,
      enable_time = checkThread.enable_time,
      disable_scn = checkThread.disable_scn,
      disable_time = checkThread.disable_time,
      status = checkThread.status
    WHERE rt.dbinc_key = this_dbinc_key
    AND   rt.thread# = checkThread.thread#;
    fetchRt;
  END IF;
END checkThread;
PROCEDURE endThreadResync IS
BEGIN
  WHILE (rtRec.thread# < MAXNUMVAL) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropRt(rtRec.thread#);
    fetchRt;
  END LOOP;
  rtRec.thread# := NULL;
  -- update high_rt_resync for the next resync
  UPDATE dbinc SET high_rt_recid = last_rt_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;
  last_rt_recid := NULL;
END endThreadResync;
/*------------------------*
* Online Redo Log resync *
*------------------------*/

PROCEDURE fetchOrl IS
BEGIN
  FETCH orlQ INTO orlRec;
  IF orlQ%NOTFOUND THEN
    orlRec.fname := chr(255);      -- assume chr(255) is greater than any name
    CLOSE orlQ;
  END IF;
END fetchOrl;
PROCEDURE addOrl(
  thread#        IN NUMBER
,group#         IN NUMBER
,fname          IN VARCHAR2
) IS
BEGIN
  INSERT INTO orl
    (dbinc_key, thread#, group#, fname)
  VALUES
    (this_dbinc_key, thread#, group#, fname);
END addOrl;
PROCEDURE dropOrl(fname IN VARCHAR2) IS
BEGIN
--  update orl set drop_ckp_key = this_ckp_key
  DELETE FROM orl
  WHERE orl.dbinc_key = this_dbinc_key
  AND   orl.fname     = dropOrl.fname;
END dropOrl;
FUNCTION beginOnlineRedoLogResync(
  high_orl_recid IN NUMBER
) RETURN BOOLEAN IS
BEGIN
  checkResync;
  SELECT high_orl_recid INTO last_orl_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;
  IF (high_orl_recid = last_orl_recid) THEN
    RETURN FALSE;
  ELSIF (high_orl_recid > last_orl_recid OR last_orl_recid IS NULL) THEN
    last_orl_recid := high_orl_recid;
    OPEN orlQ;
    fetchOrl;
    last_fname := chr(1);           -- assume chr(1) is less than any name
    RETURN TRUE;
  ELSE
    raise_application_error(-20035, 'Invalid high recid');
  END IF;
END beginOnlineRedoLogResync;
PROCEDURE checkOnlineRedoLog(
  thread#        IN NUMBER
,group#         IN NUMBER
,fname          IN VARCHAR2
) IS
BEGIN
  IF (orlRec.fname IS NULL) THEN
    raise_application_error(-20061, 'Thread resync not started');
  END IF;
  IF (last_fname >= fname) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  last_fname := fname;
  WHILE (fname > orlRec.fname) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropOrl(orlRec.fname);
    fetchOrl;
  END LOOP;
  IF (fname < orlRec.fname) THEN
    -- this thread is new and must be inserted into rcvcat
    addOrl(thread#, group#, fname);
  ELSE -- (fname = orlRec.fname)
    -- this is an existing thread, just update the information
    UPDATE orl SET
      thread# = checkOnlineRedoLog.thread#,
      group# = checkOnlineRedoLog.group#
    WHERE orl.dbinc_key = this_dbinc_key
    AND   orl.fname = checkOnlineRedoLog.fname;
    fetchOrl;
  END IF;
END checkOnlineRedoLog;
PROCEDURE endOnlineRedoLogResync IS
BEGIN
  WHILE (orlRec.fname != chr(255)) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropOrl(orlRec.fname);
    fetchOrl;
  END LOOP;
  orlRec.fname := NULL;
  -- update high_orl_resync for the next resync
  UPDATE dbinc SET high_orl_recid = last_orl_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;
  last_orl_recid := NULL;
END endOnlineRedoLogResync;
/*-----------------------------------*
* RMAN Configuration records resync *
*-----------------------------------*/

FUNCTION beginConfigResync(
  high_conf_recid IN NUMBER
) RETURN NUMBER IS
BEGIN
  checkResync;
  SELECT high_conf_recid INTO last_conf_recid
  FROM   db
  WHERE  db.db_key = this_db_key;
  IF (high_conf_recid = last_conf_recid) THEN
    RETURN CONFIGRESYNC_NO;                                -- no resync needed
  ELSIF (last_conf_recid IS NULL OR high_conf_recid > last_conf_recid) THEN
    last_conf_recid := high_conf_recid;
    RETURN CONFIGRESYNC_TORC;                   -- we need resync from CF to RC
  ELSE
    last_conf_recid := high_conf_recid;
    RETURN CONFIGRESYNC_TOCF;                   -- we need resync from RC to CF
  END IF;
END beginConfigResync;
PROCEDURE endConfigResync IS
BEGIN
  -- update high_conf_recid for the next resync
  UPDATE db SET high_conf_recid = last_conf_recid
  WHERE db.db_key = this_db_key;
  last_conf_recid := NULL;
END endConfigResync;
PROCEDURE getConfig(
   conf#          OUT    number
  ,name           IN OUT varchar2
  ,value          IN OUT varchar2
  ,first          IN     boolean)
IS
   eof          boolean := FALSE;
BEGIN
   -- call getConfig from rcvman package. Note that in this we call
   -- recovery catalog version!
   dbms_rcvman.getConfig(conf#, name, value, first);
END getConfig;
/*-------------------------*
* Redo Log History resync *
*-------------------------*/

FUNCTION beginLogHistoryResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     -- find the highest log history recid that has been recorded in rcvcat
     -- and return it to the caller
     SELECT high_rlh_recid INTO last_rlh_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_rlh_recid := 0;
  END IF;
  RETURN last_rlh_recid;
END beginLogHistoryResync;
PROCEDURE checkLogHistory(
  rlh_recid   IN NUMBER
,rlh_stamp   IN NUMBER
,thread#     IN NUMBER
,sequence#   IN NUMBER
,low_scn     IN NUMBER
,low_time    IN DATE
,next_scn    IN NUMBER
) IS
local   rlh%rowtype;
BEGIN
  IF (last_rlh_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (rlh_recid < last_rlh_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (rlh_recid > last_rlh_recid + 1) THEN
    -- there is gap in log history
    -- not sure what we should do here
    NULL;
  END IF;
  last_rlh_recid := rlh_recid;
  BEGIN
    INSERT INTO rlh(
       rlh_key, dbinc_key, rlh_recid, rlh_stamp, thread#, sequence#,
       low_scn, low_time, next_scn)
    VALUES(
       rman_seq.nextval, this_dbinc_key, rlh_recid, rlh_stamp,
       thread#, sequence#, low_scn, low_time, next_scn);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- this log history record already exists in rcvcat. Get the existing
      -- log history record to validate some data.
      BEGIN
        SELECT rlh.rlh_key, rlh.rlh_recid, rlh.sequence#, rlh.low_scn
        INTO   local.rlh_key, local.rlh_recid, local.sequence#, local.low_scn
        FROM rlh
        WHERE rlh.dbinc_key = this_dbinc_key
        AND   rlh.rlh_recid = checkLogHistory.rlh_recid
        AND   rlh.rlh_stamp = checkLogHistory.rlh_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          -- It is possible that the this record has a different recid/stamp
          -- than the existing one has, but has the same thread#, sequence#,
          -- low_scn (in other words the unique contraint rlh_u1 is violated).
          -- This can happen if the controlfile is recreated and media
          -- recovery inserts the log history record to the newly created
          -- controlfile. We could select the existing record using the
          -- thread#, sequence# and low_scn to make sure that other columns
          -- (except recid and stamp) match or even update recid and stamp
          -- to the new values, but it doesn't seem necessary to do so.
          RETURN;
      END;
      -- check that sequence# and low_scn match
      IF (sequence# <> local.sequence#) THEN
        raise_application_error(-20070, 'Invalid log history sequence#');
      END IF;
      IF (low_scn <> local.low_scn) THEN
        raise_application_error(-20071, 'Invalid log history low SCN');
      END IF;
  END;
END checkLogHistory;
PROCEDURE endLogHistoryResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     -- set the high_rlh_recid for the next resync
     UPDATE dbinc SET high_rlh_recid = last_rlh_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_rlh_recid := NULL;
END endLogHistoryResync;
/*-------------------------*
* Archived Log resync     *
*-------------------------*/

FUNCTION beginArchivedLogResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_al_recid INTO last_al_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_al_recid := 0;
  END IF;
  RETURN last_al_recid;
END beginArchivedLogResync;
PROCEDURE checkArchivedLog(
  al_recid    IN NUMBER
,al_stamp    IN NUMBER
,thread#     IN NUMBER
,sequence#   IN NUMBER
,reset_scn   IN NUMBER
,reset_time  IN DATE
,low_scn     IN NUMBER
,low_time    IN DATE
,next_scn    IN NUMBER
,next_time   IN DATE
,blocks      IN NUMBER
,block_size  IN NUMBER
,fname       IN VARCHAR2
,archived    IN VARCHAR2
,completion_time IN DATE
,status      IN VARCHAR2
,is_standby  IN VARCHAR2
,dictionary_begin IN VARCHAR2
,dictionary_end IN VARCHAR2
) IS
local  al%rowtype;
my_dbinc_key NUMBER;
BEGIN
  IF (last_al_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (al_recid < last_al_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (al_recid > last_al_recid + 1) THEN
    -- there is gap in archived log
    -- not sure what we should do here
    NULL;
  END IF;
  last_al_recid := al_recid;
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  IF (al_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- see if this log is a log that was cleared by a resetlogs.  if so,
  -- skip it.
  IF (sequence# = 0) THEN
    RETURN;
  END IF;
  -- find the database incarnation of this archived log
  my_dbinc_key := checkIncarnation(reset_scn, reset_time);
  BEGIN
    IF (status = 'D') THEN
       -- Do not bother to insert this record.
       NULL;
    ELSE
       INSERT INTO al
         (al_key, dbinc_key, al_recid, al_stamp, thread#, sequence#,
          low_scn, low_time, next_scn, next_time,
          fname, fname_hashkey, archived, blocks, block_size,
          completion_time, status, is_standby,
          dictionary_begin, dictionary_end)
       VALUES
         (rman_seq.nextval, my_dbinc_key, al_recid, al_stamp, thread#,
          sequence#, low_scn, low_time, next_scn, next_time,
          fname, substr(fname,1,10)||substr(fname, -10),
          archived, blocks, checkArchivedLog.block_size, completion_time,
          status, is_standby, dictionary_begin, dictionary_end);
    END IF;
    -- mark any previous archived logs with the same name as deleted
    -- Note that only logs that belong to current database and that have
    -- archived or copied before this log are marked deleted
    UPDATE /*+ index(al al_i_fname_status)*/ al SET status = 'D'
    WHERE al.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
    AND   al.fname = checkArchivedLog.fname
    AND   al.fname_hashkey = substr(checkArchivedLog.fname,1,10)||
                          substr(checkArchivedLog.fname,-10)
    AND   al.status != 'D'
    AND   al.al_stamp <= checkArchivedLog.al_stamp
    AND   NOT (al.al_recid = checkArchivedLog.al_recid AND
               al.al_stamp = checkArchivedLog.al_stamp AND
               al.is_standby = checkArchivedLog.is_standby);
    -- note that also cleared entries are inserted into rcvcat.
    -- update log history entry. If fname is null then the log was cleared
    UPDATE rlh SET
      status = decode(fname, NULL, 'C', status)
    WHERE rlh.dbinc_key = my_dbinc_key
    AND   rlh.thread#   = checkArchivedLog.thread#
    AND   rlh.sequence# = checkArchivedLog.sequence#
    AND   rlh.low_scn   = checkArchivedLog.low_scn;
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- this archived log already exist in rcvcat. Get the existing archived
      -- record to validate it.
      BEGIN
        SELECT al.al_recid, al.fname
        INTO   local.al_recid, local.fname
        FROM al
        WHERE al.dbinc_key = my_dbinc_key
        AND   al.al_recid = checkArchivedLog.al_recid
        AND   al.al_stamp = checkArchivedLog.al_stamp
        AND   al.is_standby = checkArchivedLog.is_standby;
      EXCEPTION
        WHEN no_data_found THEN
          -- It is possible to end up here if the transaction that inserted
          -- the archived log record hasn't committed yet. Although that
          -- should not happen because all resyncs are currently serialized.
          RETURN;
      END;
      -- check that fname matches
      IF (fname <> local.fname) THEN
        raise_application_error(-20080, 'Invalid archived log name');
      END IF;
  END;
END checkArchivedLog;
PROCEDURE endArchivedLogResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_al_recid = last_al_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_al_recid := NULL;
END endArchivedLogResync;
/*-------------------------*
* Offline range resync    *
*-------------------------*/

FUNCTION beginOfflineRangeResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_offr_recid INTO last_offr_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_offr_recid := 0;
  END IF;
  RETURN last_offr_recid;
END beginOfflineRangeResync;
PROCEDURE checkOfflineRange(
  offr_recid     IN NUMBER
,offr_stamp     IN NUMBER
,file#          IN NUMBER
,create_scn     IN NUMBER
,offline_scn    IN NUMBER
,online_scn     IN NUMBER
,online_time    IN DATE
,cf_create_time IN DATE
) IS
   local   offr%rowtype;
   stamp   NUMBER;
BEGIN
  IF (last_offr_recid IS NULL AND offr_recid IS NOT NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  deb('In Checkofflinerange.  off-recid: '||offr_recid||
      '  offr-stamp: '||offr_stamp||'  create_scn:'||create_scn||
      '  offline_scn: '||offline_scn);
  IF (offr_recid IS NULL) THEN
     stamp := 0;
     -- This offline range is a current offline range (from the kccfe).
     -- Since we have a unique index on recid and stamp, we must change
     -- at least one of them.  We use the offr_key as the recid, and
     -- set the stamp to zero to avoid issues with nulls.
     deb('Inserting KCCFE range into OFFR.  Stamp set to zero');
  ELSE
     deb('Checking recids for KCCFOR record, has to be in right order '||
         '  offr_recid: '||offr_recid||'  last_offr_recid:'||last_offr_recid);
     stamp := offr_stamp;
     IF (offr_recid < last_offr_recid) THEN
        raise_application_error(-20036, 'Invalid record order');
     END IF;
     IF (offr_recid > last_offr_recid + 1) THEN
        -- there is gap in offline range records
        NULL;
     END IF;
     last_offr_recid := offr_recid;
     deb('Recids are OK, setting last_offr_recid to: '||last_offr_recid);
  END IF;
  BEGIN
    deb('Inserting offline range into offr...');
    INSERT INTO offr(
       offr_key, dbinc_key, offr_recid, offr_stamp,
       file#, create_scn, offline_scn, online_scn, online_time, cf_create_time)
    VALUES(
      rman_seq.nextval, this_dbinc_key,
      nvl(offr_recid, rman_seq.nextval), stamp,
      file#,create_scn, offline_scn, online_scn, online_time, cf_create_time);
  EXCEPTION
    WHEN dup_val_on_index THEN
      deb('Got duplicate value on index while inserting into OFFR');
      -- This offline range record already exists in rcvcat.  Get the existing
      -- record and validate it.
      BEGIN
      deb('Looking for existing offline record with dbinc_key: '||
          this_dbinc_key||' offr_recid: '||offr_recid||
          '  offr_stamp: '||stamp);
      SELECT offr.file#, offr.offline_scn, offr.online_scn
        INTO local.file#, local.offline_scn, local.online_scn
        FROM offr
       WHERE offr.dbinc_key = this_dbinc_key
         AND offr.offr_recid = checkOfflineRange.offr_recid
         AND offr.offr_stamp = stamp;
      EXCEPTION
        WHEN no_data_found THEN
          deb('No existing offline record with that recid/stamp found');
          -- It is possible that this record has a different recid/stamp than
          -- the existing one, but it has the same file#, offline_scn and
          -- cf_create_time.  I don I dont know how this could happen...
          -- Fsanchez 10/2001 - Actually this happens when multiple recover
          -- datafiles are performed under specific circumstances.  In that
          -- case, the offline range in kccofr has the same offline_scn but
          -- a different online_scn, the kccofr has a smaller online_scn
          -- and the kccfe record has already been inserted with the same
          -- offline_scn but a larger online_scn.
          -- If a third offline range needed to be created by the kernel, when
          -- the kccfe is tried to be push to kccofr but as the offline_scn
          -- is the same as the already entered record in the kccofr, the
          -- kccfe record is just ignored and not entered.
          -- End Fsanchez-entry.
          -- ... ,but if
          -- it does, we simply update the recid/stamp of the existing
          -- record if this will advance the recid.  We want to keep
          -- the highest recid in the catalog since this record will age out
          -- last.  If the stamp is zero, then we are simply re-processing
          -- the current offline range.  In this case, ignore it since
          -- we already have it in the catalog.
          -- Fsanchez 10/2001 - Well, we have to enable first the same
          -- kind of logic that kccofr uses, so we will check if the stamp of
          -- the previous OFFR record entered was zero, i.e. from kccfe, if so
          -- and the offline_scn is the same but the online_scn is not, then
          -- we will take the online_scn of the kccofr record and update with
          -- that the OFFR record.  The recid/stamp will be changed to
          -- indicate the actual recid and stamp of the kccofr record,
          -- End Fsanchez-entry.
          BEGIN
            deb('Retrying to look for offline record, '||
                'but now with dbinc_key: '||this_dbinc_key||
                ' file#: '||file#||'  create_scn: '||
                create_scn||'  offline_scn: '||offline_scn||
                '  create_time: '||cf_create_time);
            SELECT offr.file#, offr.offline_scn, offr.online_scn,
                   create_scn, offr.offr_stamp
            INTO   local.file#, local.offline_scn, local.online_scn,
                   local.create_scn, local.offr_stamp
            FROM offr
            WHERE offr.dbinc_key = this_dbinc_key
            AND   offr.file# = checkOfflineRange.file#
            -- Add create_scn to the check as create_scn is part of
            -- the index now
            AND   offr.create_scn = checkOfflineRange.create_scn
            AND   offr.offline_scn = checkOfflineRange.offline_scn
            AND   offr.cf_create_time = checkOfflineRange.cf_create_time;
            IF (stamp = 0) THEN
               deb('Stamp is zero, no need to update');
               NULL;
            ELSE
              IF local.offr_stamp = 0 AND
                 local.online_scn <> checkOfflineRange.online_scn THEN
                  deb('Fixing KCCFE offline range from online_scn: '||
                      local.online_scn||'  to: '||
                      checkOfflineRange.online_scn);
                  -- Fake that online_scn is OK
                  local.online_scn := checkOfflineRange.online_scn;
              END IF;
              deb('Update offr for offline record with dbinc_key: '||
                    this_dbinc_key||' file#: '||file#||'  create_scn: '||
                    create_scn||'  offline_scn: '||offline_scn||
                    '  create_time: '||cf_create_time);
              UPDATE offr SET
                     offr_recid = checkOfflineRange.offr_recid,
                     offr_stamp = checkOfflineRange.stamp,
                     online_scn = checkOfflineRange.online_scn
               WHERE offr.dbinc_key = this_dbinc_key
                 AND offr.file# = checkOfflineRange.file#
                  -- Add create_scn to the check as create_scn is part
                  -- of the index now
                 AND offr.create_scn = checkOfflineRange.create_scn
                 AND offr.offline_scn = checkOfflineRange.offline_scn
                 AND offr.cf_create_time = checkOfflineRange.cf_create_time
                 AND ((offr.offr_stamp = 0)
                  OR  (offr.offr_recid < checkOfflineRange.offr_recid));
            END IF;
            EXCEPTION
               WHEN no_data_found THEN
               deb ('Error should be signalled because data was not found');
                 -- We should signal an error here.  This means there is
                 -- some index on the offr table that we don't know about.
                 RETURN;
            END;
          END;
      deb('Final checks. File number OK?  File#: '||file#||
          '  local.file#: '||local.file#);
      -- check that file# and offline_scn match
      IF (file# <> local.file#) THEN
        raise_application_error(-20085, 'Invalid file#');
      END IF;
      deb('Offline_scn OK?  Offline_scn: '||offline_scn||
          '  local.offline_scn: '||local.offline_scn);
      IF (offline_scn <> local.offline_scn) THEN
        raise_application_error(-20086, 'Invalid offline SCN');
      END IF;
      deb('Online_scn OK?  Online_scn: '||online_scn||
          '  local.online_SCN: '||local.online_SCN);
      IF (online_scn <> local.online_scn) THEN
        raise_application_error(-20087, 'Invalid online SCN');
      END IF;
  END;
  deb('Exiting Checkofflinerange.');
END;
PROCEDURE endOfflineRangeResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_offr_recid = last_offr_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_offr_recid := NULL;
END endOfflineRangeResync;
/*-------------------------*
* Backup Set resync       *
*-------------------------*/

-- updateBackupSetRec calculates and updates the status of a backup set based
-- on its backup pieces.  The backup set is 'A' (available) if there is an
-- available copy of all of its pieces on one device type.  The backup set is
-- 'D' (deleted) if all bp's are deleted.  Otherwise it is marked 'O' (other).
PROCEDURE updateBackupSetRec(bs_key IN NUMBER,
                             act_corrupt IN BOOLEAN DEFAULT TRUE) IS
  total_pieces     NUMBER;
  available_pieces NUMBER;
  corrupt_count    NUMBER := 0;
  new_status       VARCHAR2(1);
BEGIN
  SELECT pieces INTO total_pieces
  FROM bs
  WHERE bs.bs_key = updateBackupSetRec.bs_key;
  SELECT max(count(DISTINCT piece#)) INTO available_pieces
  FROM  bp
  WHERE bp.bs_key = updateBackupSetRec.bs_key
  AND   bp.status = 'A'
  GROUP BY device_type;
  IF (act_corrupt) THEN
     SELECT count(*) INTO corrupt_count
     FROM rc_backup_corruption rcbcb
     WHERE rcbcb.bs_key = updateBackupSetRec.bs_key;
  END IF;
  IF (total_pieces = 0) THEN
    -- Bug 1467871: Remove dummy records inserted in 8.1.6- versions RMAN
    new_status := 'D';
  ELSIF (available_pieces = total_pieces) THEN
    new_status := 'A';
  ELSE
    BEGIN
       -- set new_status to 'O' (other) if some non-deleted rows found
       SELECT 'O' INTO new_status FROM bp
        WHERE bp.bs_key = updateBackupSetRec.bs_key
          AND bp.status != 'D'
          AND rownum < 2;
    EXCEPTION WHEN no_data_found THEN
       new_status := 'D'; -- all pieces are deleted or not there
    END;
  END IF;
  IF new_status in ('O', 'A') OR corrupt_count != 0 THEN
     -- corruption table is required for BMR. For sake of this, bs record
     -- should be present event if new_status is 'D'.
     UPDATE bs SET status = new_status
      WHERE bs.bs_key = updateBackupSetRec.bs_key;
  ELSE
     -- Note that the things in the backup set will automatically be deleted
     -- because the referential integrity contraints use ON DELETE CASCADE
     DELETE FROM bs
      WHERE bs.bs_key = updateBackupSetRec.bs_key;
  END IF;
END updateBackupSetRec;
FUNCTION beginBackupSetResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_bs_recid INTO last_bs_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_bs_recid := 0;
  END IF;
  RETURN last_bs_recid;
END beginBackupSetResync;
PROCEDURE checkBackupSet(
  bs_recid             IN NUMBER
,bs_stamp             IN NUMBER
,set_stamp            IN NUMBER
,set_count            IN NUMBER
,bck_type             IN VARCHAR2
,incr_level           IN NUMBER   DEFAULT NULL
,pieces               IN NUMBER
,start_time           IN DATE
,completion_time      IN DATE
,controlfile_included IN VARCHAR2 DEFAULT NULL
,input_file_scan_only IN VARCHAR2 DEFAULT NULL
,keep_options         IN NUMBER   DEFAULT NULL
,keep_until           IN DATE     DEFAULT NULL
) IS
local   bs%rowtype;
newbskey number;
BEGIN
  IF (last_bs_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (bs_recid < last_bs_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (bs_recid > last_bs_recid + 1) THEN
    -- there is gap in backup set records
    NULL;
  END IF;
  last_bs_recid := bs_recid;
  IF (bs_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  IF (bck_type NOT IN ('D','I','L') OR bck_type IS NULL) THEN
    raise_application_error(-20090, 'Invalid backup set type');
  END IF;
  IF (incr_level NOT IN (0,1,2,3,4) OR
      (bck_type NOT IN ('D','I') AND incr_level <> 0)) THEN
    raise_application_error(-20091, 'Invalid backup set level');
  END IF;
  BEGIN
     select rman_seq.nextval into newbskey from dual;
     -- insert the backup set with status 'D'. Backup piece resync will mark
     -- the backup set available if all pieces are found.
    INSERT INTO bs
      (bs_key, db_key, bs_recid, bs_stamp, set_stamp, set_count,
       bck_type, incr_level, pieces, start_time, completion_time, status,
       controlfile_included, input_file_scan_only, keep_options, keep_until)
    VALUES
      (newbskey, this_db_key, bs_recid, bs_stamp, set_stamp, set_count,
       bck_type, incr_level, pieces, start_time, completion_time, 'D',
       decode(controlfile_included, 'SBY','STANDBY','YES','BACKUP','NONE'),
       input_file_scan_only, keep_options, keep_until);
    cntbs := cntbs + 1;
    insertedbs(cntbs) := newbskey;
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- backup set is already in rcvcat
      BEGIN
        SELECT bs_key, completion_time INTO local.bs_key, local.completion_time
        FROM bs
        WHERE bs.db_key = this_db_key
        AND   bs.bs_recid = checkBackupSet.bs_recid
        AND   bs.bs_stamp = checkBackupSet.bs_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          -- if select doesn't see the backup set, another session must have
          -- inserted it, but not committed yet. Give up, let the other session
          -- to insert the backup set
        RETURN;
      END;
      IF (completion_time <> local.completion_time) THEN
        raise_application_error(-20092, 'Invalid completion time');
      END IF;
  END;
END checkBackupSet;
PROCEDURE endBackupSetResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     -- update high_bs_recid for the next resync
     UPDATE dbinc SET high_bs_recid = last_bs_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_bs_recid := NULL;
END endBackupSetResync;
/*-------------------------*
* Backup piece resync     *
*-------------------------*/

FUNCTION beginBackupPieceResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_bp_recid INTO last_bp_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_bp_recid := 0;
  END IF;
  RETURN last_bp_recid;
END beginBackupPieceResync;
PROCEDURE checkBackupPiece(
  bp_recid    IN NUMBER
,bp_stamp    IN NUMBER
,set_stamp   IN NUMBER
,set_count   IN NUMBER
,piece#      IN NUMBER
,tag         IN VARCHAR2
,device_type IN VARCHAR2
,handle      IN VARCHAR2
,comments    IN VARCHAR2
,media       IN VARCHAR2
,concur      IN VARCHAR2
,start_time  IN DATE
,completion_time IN DATE
,status      IN VARCHAR2
,copy#       IN NUMBER default 1
,media_pool  IN NUMBER default 0
) IS
localbs  bs%rowtype;
localbp  bp%rowtype;
BEGIN
  IF (last_bp_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (bp_recid < last_bp_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (bp_recid > last_bp_recid + 1) THEN
    -- there is gap in backup set records
    -- not sure what we should do here
    NULL;
  END IF;
  last_bp_recid := bp_recid;
  IF (bp_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
--  IF (status = 'D') THEN
--     RETURN;
--  END IF;
  -- find the key, recid, and # of pieces for the backup set
  BEGIN
    SELECT bs_key, bs_recid, pieces
    INTO localbs.bs_key, localbs.bs_recid, localbs.pieces
    FROM bs
    WHERE bs.db_key = this_db_key
    AND   bs.set_stamp = checkBackupPiece.set_stamp
    AND   bs.set_count = checkBackupPiece.set_count;
  EXCEPTION
    WHEN no_data_found THEN
      IF status != 'D' THEN
        select rman_seq.nextval into localbs.bs_key from dual;
        INSERT INTO bs
          (bs_key, db_key, bs_recid, bs_stamp,
           set_stamp, set_count,
           bck_type, incr_level, pieces, start_time, completion_time, status,
           controlfile_included)
        VALUES
          -- Since we don't know the recid/stamp of the bs record, just
          -- use 0.  0 is not a naturally occuring recid/stamp value, so
          -- this will serve to indicate how these records got inserted.
          -- There is no unique constraint on these columns, so using
          -- a constant here is OK.  The: reason we don't use NULL is because
          -- old RMAN versions don't use a null indicator when they select
          -- this column.
          (localbs.bs_key, this_db_key, 0, 0,
          checkBackupPiece.set_stamp, checkBackupPiece.set_count,
          NULL, NULL, checkBackupPiece.piece#,
          checkBackupPiece.start_time, checkBackupPiece.completion_time, 'O',
          'NONE');
        cntbs := cntbs + 1;
        insertedbs(cntbs) := localbs.bs_key;
      ELSE
         -- no backupset records available
         RETURN;
      END IF;
  END;
  IF localbs.bs_recid is null AND
     checkBackupPiece.piece# > localbs.pieces AND status != 'D' THEN
    -- update those bs records created in the above no_data_found exception
    -- everytime checkBackupPiece is called
    UPDATE bs
       SET bs.pieces = greatest(checkBackupPiece.piece#, nvl(bs.pieces,0))
     WHERE bs.db_key = this_db_key
       AND bs.set_stamp = checkBackupPiece.set_stamp
       AND bs.set_count = checkBackupPiece.set_count
       AND bs.bck_type IS NULL;
  END IF;
  BEGIN
    IF status != 'D' THEN
      INSERT INTO bp
        (bp_key, bs_key, piece#, db_key, bp_recid, bp_stamp, tag, device_type,
         copy#, handle, handle_hashkey, comments, media, media_pool, concur,
         start_time, completion_time, status)
      VALUES
        (rman_seq.nextval, localbs.bs_key, piece#, this_db_key,
         bp_recid, bp_stamp,
         tag, device_type, copy#, handle,
         substr(device_type,1,10)||substr(handle,1,10)||substr(handle,-10),
         comments, media, media_pool,
         decode(concur,'YES','Y','NO','N'), start_time, completion_time, status);
      -- If there is a piece with the
      -- same device_type and handle we assume that it was written over by
      -- current piece and we mark it deleted.
      -- ### removed nvl(,_NULL)
      -- mark the matching backup pieces as deleted in a loop, so we mark all
      -- their backup sets too.
      FOR bprec IN bpq(device_type, handle, bp_recid, bp_stamp) LOOP
        UPDATE bp SET status = 'D' WHERE bp.bp_key = bprec.bp_key;
      END LOOP;
    END IF;
    -- validate the backup set
    updateBackupSetRec(localbs.bs_key);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- check if the backup piece record is already in rcvcat
      BEGIN
        SELECT bp_key, piece#
        INTO localbp.bp_key, localbp.piece#
        FROM bp
        WHERE bp.bs_key = localbs.bs_key
        AND   bp.bp_recid = checkBackupPiece.bp_recid
        AND   bp.bp_stamp = checkBackupPiece.bp_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the piece#
      IF (piece# <> localbp.piece#) THEN
        raise_application_error(-20093, 'Invalid piece#');
      END IF;
  END;
END checkBackupPiece;
PROCEDURE endBackupPieceResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_bp_recid = last_bp_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_bp_recid := NULL;
END endBackupPieceResync;
/*-------------------------*
* Backup Datafile resync  *
*-------------------------*/

PROCEDURE addBackupControlFile(
  bs_key             IN NUMBER
,bcf_recid          IN NUMBER
,bcf_stamp          IN NUMBER
,dbinc_key          IN NUMBER
,ckp_scn            IN NUMBER
,ckp_time           IN DATE
,create_time        IN DATE
,min_offr_recid     IN NUMBER
,blocks             IN NUMBER
,block_size         IN NUMBER
,controlfile_type   IN VARCHAR2
,cfile_abck_year    IN number
,cfile_abck_mon_day IN number
,cfile_abck_seq     IN number
) IS
local    bcf%rowtype;
BEGIN
  BEGIN
    INSERT INTO bcf(bcf_key, bs_key, dbinc_key, bcf_recid, bcf_stamp,
                    ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
                    controlfile_type, blocks, autobackup_date,
                    autobackup_sequence)
    VALUES (rman_seq.nextval, bs_key, dbinc_key, bcf_recid, bcf_stamp,
            ckp_scn, ckp_time, create_time, min_offr_recid,block_size,
            controlfile_type, blocks,
            decode(cfile_abck_year, 0, NULL,
                                    to_date(to_char(cfile_abck_year)||
                                      lpad(to_char(cfile_abck_mon_day), 4, '0'),
                                      'YYYYMMDD', 'NLS_CALENDAR=Gregorian')),
            cfile_abck_seq);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- this backup controlfile record already exists in rcvcat
      BEGIN
        SELECT ckp_scn INTO local.ckp_scn
        FROM bcf
        WHERE bcf.dbinc_key = addBackupControlFile.dbinc_key
        AND   bcf.bcf_recid = addBackupControlFile.bcf_recid
        AND   bcf.bcf_stamp = addBackupControlFile.bcf_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the ckp_scn
      IF (ckp_scn <> local.ckp_scn) THEN
        raise_application_error(-20095, 'Invalid ckp_scn');
      END IF;
  END;
END addBackupControlFile;
PROCEDURE addBackupDataFile(
  bs_key          IN NUMBER
,bdf_recid       IN NUMBER
,bdf_stamp       IN NUMBER
,file#           IN NUMBER
,create_scn      IN NUMBER
,dbinc_key       IN NUMBER
,incr_level      IN NUMBER
,incr_scn        IN NUMBER
,ckp_scn         IN NUMBER
,ckp_time        IN DATE
,abs_fuzzy_scn   IN NUMBER
,datafile_blocks IN NUMBER
,blocks          IN NUMBER
,block_size      IN NUMBER
,completion_time IN DATE
) IS
local    bdf%rowtype;
BEGIN
  BEGIN
    INSERT INTO bdf(bdf_key, dbinc_key, bdf_recid, bdf_stamp, bs_key,
       file#, create_scn, incr_level, incr_scn,
       ckp_scn, ckp_time, abs_fuzzy_scn, datafile_blocks, blocks, block_size,
       completion_time)
    VALUES
      (rman_seq.nextval, dbinc_key, bdf_recid, bdf_stamp, bs_key,
       file#, create_scn, incr_level, incr_scn,
       ckp_scn,ckp_time, abs_fuzzy_scn, datafile_blocks, blocks, block_size,
       completion_time);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- this backup datafile record already exist in rcvcat
      BEGIN
        SELECT file#, create_scn INTO local.file#, local.create_scn
        FROM bdf
        WHERE bdf.dbinc_key = addBackupDataFile.dbinc_key
        AND   bdf.bdf_recid = addBackupDataFile.bdf_recid
        AND   bdf.bdf_stamp = addBackupDataFile.bdf_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the file# and creation scn
      IF (file# <> local.file#) THEN
        raise_application_error(-20096, 'Invalid file#');
      END IF;
      IF (create_scn <> local.create_scn) THEN
        raise_application_error(-20097, 'Invalid create scn');
      END IF;
  END;
END addBackupDataFile;
FUNCTION beginBackupDataFileResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_bdf_recid INTO last_bdf_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_bdf_recid := 0;
  END IF;
  RETURN last_bdf_recid;
END beginBackupDataFileResync;
PROCEDURE checkBackupDataFile(
  bdf_recid       IN NUMBER
,bdf_stamp       IN NUMBER
,set_stamp       IN NUMBER
,set_count       IN NUMBER
,file#           IN NUMBER
,create_scn      IN NUMBER
,create_time     IN DATE
,reset_scn       IN NUMBER
,reset_time      IN DATE
,incr_level      IN NUMBER
,incr_scn        IN NUMBER
,ckp_scn         IN NUMBER
,ckp_time        IN DATE
,abs_fuzzy_scn   IN NUMBER
,datafile_blocks IN NUMBER
,blocks          IN NUMBER
,block_size      IN NUMBER
,min_offr_recid  IN NUMBER
,completion_time IN DATE
,controlfile_type
                  IN VARCHAR2  DEFAULT NULL
,cfile_abck_year IN NUMBER    DEFAULT NULL
,cfile_abck_mon_day
                  IN NUMBER    DEFAULT NULL
,cfile_abck_seq  IN NUMBER    DEFAULT NULL
) IS
bs_key NUMBER;
dbinc_key NUMBER;
BEGIN
  IF (last_bdf_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (bdf_recid < last_bdf_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (bdf_recid > last_bdf_recid + 1) THEN
    -- there is gap in backup set records
    NULL;
  END IF;
  last_bdf_recid := bdf_recid;
  IF (bdf_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- find the key of the backup set
  BEGIN
    SELECT bs_key INTO bs_key
    FROM bs
    WHERE bs.db_key = this_db_key
    AND   bs.set_stamp = checkBackupDataFile.set_stamp
    AND   bs.set_count = checkBackupDataFile.set_count;
  EXCEPTION
    WHEN no_data_found THEN
       -- Bug 1467871: bs_key should be inserted either in
       --              checkBackupSet (or) checkBackupPiece
       -- Exception would occur only when backupset records and
       -- backuppiece records ages out. Ignore silently these
       -- orphaned backup datafile records rather than inserting a 'D'
       -- record in BS table which will create dummy records with
       -- 8.1.6- version of RMAN and this catalog version.
       return;
  END;
  BEGIN
    -- update only those bs records created in no_data_found exception
    -- of checkBackupPiece, checkBackupDataFile and checkBackupRedoLog
    IF (checkBackupDatafile.incr_level > 0) THEN
      UPDATE bs SET bs.incr_level = checkBackupDataFile.incr_level,
                    bs.bck_type = 'I'
      WHERE bs.db_key = this_db_key
      AND   bs.set_stamp = checkBackupDatafile.set_stamp
      AND   bs.set_count = checkBackupDatafile.set_count
      AND   bs.bck_type IS NULL;
    ELSE
      UPDATE bs SET bs.incr_level = checkBackupDataFile.incr_level,
                    bs.bck_type = 'D'
      WHERE bs.db_key = this_db_key
      AND   bs.set_stamp = checkBackupDatafile.set_stamp
      AND   bs.set_count = checkBackupDatafile.set_count
      AND   bs.bck_type IS NULL;
    END IF;
    IF (file# = 0 and controlfile_type is not null) then
      UPDATE bs SET bs.controlfile_included=
                 decode(checkBackupDatafile.controlfile_type,'B','BACKUP',
                                                             'S','STANDBY',
                                                             'NONE')
      WHERE bs.db_key = this_db_key
      AND   bs.set_stamp = checkBackupDatafile.set_stamp
      AND   bs.set_count = checkBackupDatafile.set_count
      AND   bs.controlfile_included = 'NONE';
    END IF;
  END;
  -- find the database incarnation key
  dbinc_key := checkIncarnation(reset_scn, reset_time);
  IF (file# = 0) THEN
    addBackupControlFile(bs_key, bdf_recid, bdf_stamp, dbinc_key,
         ckp_scn, ckp_time, create_time, min_offr_recid, blocks, block_size,
         controlfile_type, cfile_abck_year, cfile_abck_mon_day, cfile_abck_seq);
  ELSE
    addBackupDataFile(bs_key, bdf_recid, bdf_stamp, file#, create_scn,
         dbinc_key, incr_level, incr_scn, ckp_scn, ckp_time,
         abs_fuzzy_scn, datafile_blocks, blocks, block_size, completion_time);
  END IF;
END checkBackupDataFile;
PROCEDURE endBackupDataFileResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_bdf_recid = last_bdf_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_bdf_recid := NULL;
END endBackupDataFileResync;
/*-----------------------*
* Backup SPFILE resync  *
*-----------------------*/

FUNCTION beginBackupSpFileResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_bsf_recid INTO last_bsf_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_bsf_recid := 0;
  END IF;
  RETURN last_bsf_recid;
END beginBackupSpFileResync;
PROCEDURE addBackupSpFile(
  bs_key             IN NUMBER
,bsf_recid          IN NUMBER
,bsf_stamp          IN NUMBER
,modification_time  IN DATE
,bytes              IN NUMBER
) IS
local    bsf%rowtype;
BEGIN
  INSERT INTO bsf(bsf_key, bs_key, db_key, bsf_recid, bsf_stamp,
                  modification_time, bytes)
  VALUES (rman_seq.nextval, bs_key, this_db_key, bsf_recid, bsf_stamp,
          modification_time, bytes);
EXCEPTION
  WHEN dup_val_on_index THEN
    -- this backup SPFILE record already exists in rcvcat
    BEGIN
      SELECT modification_time INTO local.modification_time
      FROM bsf
      WHERE bsf.db_key = this_db_key
      AND   bsf.bsf_recid = addBackupSpFile.bsf_recid
      AND   bsf.bsf_stamp = addBackupSpFile.bsf_stamp;
    EXCEPTION
      WHEN no_data_found THEN
        RETURN;
    END;
    -- check if the modification time differs
    IF (modification_time <> local.modification_time) THEN
      raise_application_error(-20101, 'Invalid modification_time');
    END IF;
END addBackupSpFile;
PROCEDURE checkBackupSpFile(
  bsf_recid         IN NUMBER
,bsf_stamp         IN NUMBER
,set_stamp         IN NUMBER
,set_count         IN NUMBER
,modification_time IN DATE
,bytes             IN NUMBER
) IS
bs_key NUMBER;
BEGIN
  IF (last_bsf_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
IF (bsf_recid < last_bsf_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (bsf_recid > last_bsf_recid + 1) THEN
    -- there is gap in backup set records
    NULL;
  END IF;
  last_bsf_recid := bsf_recid;
  IF (bsf_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- find the key of the backup set
  BEGIN
    SELECT bs_key INTO bs_key
    FROM bs
    WHERE bs.db_key = this_db_key
    AND   bs.set_stamp = checkBackupSpFile.set_stamp
    AND   bs.set_count = checkBackupSpFile.set_count;
  EXCEPTION
    WHEN no_data_found THEN
       return;
  END;
  addBackupSpFile(bs_key, bsf_recid, bsf_stamp, modification_time, bytes);
END checkBackupSpFile;
PROCEDURE endBackupSpFileResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_bsf_recid = last_bsf_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_bsf_recid := NULL;
END endBackupSpFileResync;
/*-------------------------*
* Backup Redo Log resync  *
*-------------------------*/

FUNCTION beginBackupRedoLogResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_brl_recid INTO last_brl_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_brl_recid := 0;
  END IF;
  RETURN last_brl_recid;
END beginBackupRedoLogResync;
PROCEDURE checkBackupRedoLog(
  brl_recid    IN NUMBER
,brl_stamp    IN NUMBER
,set_stamp    IN NUMBER
,set_count    IN NUMBER
,thread#      IN NUMBER
,sequence#    IN NUMBER
,reset_scn    IN NUMBER
,reset_time   IN DATE
,low_scn      IN NUMBER
,low_time     IN DATE
,next_scn     IN NUMBER
,next_time    IN DATE
,blocks       IN NUMBER
,block_size   IN NUMBER
) IS
local brl%rowtype;
BEGIN
  IF (last_brl_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (brl_recid < last_brl_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (brl_recid > last_brl_recid + 1) THEN
    -- there is gap in backup set records
    -- not sure what we should do here
    NULL;
  END IF;
  last_brl_recid := brl_recid;
  IF (brl_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  -- find the key of the backup set
  BEGIN
    SELECT bs_key INTO local.bs_key
    FROM bs
    WHERE bs.db_key = this_db_key
    AND   bs.set_stamp = checkBackupRedoLog.set_stamp
    AND   bs.set_count = checkBackupRedoLog.set_count;
  EXCEPTION
    WHEN no_data_found THEN
       RETURN;
      select rman_seq.nextval into local.bs_key from dual;
      INSERT INTO bs
        (bs_key, db_key, bs_recid, bs_stamp,
         set_stamp, set_count,
         bck_type, incr_level, pieces, start_time, completion_time, status,
         controlfile_included)
      VALUES
        (local.bs_key, this_db_key, 0, 0,
        checkBackupRedoLog.set_stamp, checkBackupRedoLog.set_count,
        NULL, NULL, 0, NULL, NULL, 'D', 'NONE');
     cntbs := cntbs + 1;
     insertedbs(cntbs) := local.bs_key;
  END;
  BEGIN
    -- update only those bs records created in no_data_found exception
    -- of checkBackupPiece, checkBackupDataFile and checkBackupRedoLog
    UPDATE bs SET bs.bck_type = 'L'
    WHERE bs.db_key = this_db_key
    AND   bs.set_stamp = checkBackupRedoLog.set_stamp
    AND   bs.set_count = checkBackupRedoLog.set_count
    AND   bs.bck_type IS NULL;
  END;
  -- find the dbinc_key
  local.dbinc_key := checkIncarnation(reset_scn, reset_time);
  BEGIN
    INSERT INTO brl
        (brl_key, dbinc_key, brl_recid, brl_stamp,
         thread#, sequence#, low_scn, low_time, next_scn, next_time,
         blocks, block_size, bs_key)
    VALUES
        (rman_seq.nextval, local.dbinc_key, brl_recid, brl_stamp,
         thread#, sequence#, low_scn, low_time, next_scn, next_time,
         blocks, block_size, local.bs_key);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- the backup redo log record already exists
      BEGIN
        SELECT low_scn INTO local.low_scn
        FROM brl
        WHERE brl.dbinc_key = local.dbinc_key
        AND   brl.brl_recid = checkBackupRedoLog.brl_recid
        AND   brl.brl_stamp = checkBackupRedoLog.brl_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the low_scn
      IF (low_scn <> local.low_scn) THEN
        raise_application_error(-20098, 'Invalid low scn');
      END IF;
  END;
END checkBackupRedoLog;
PROCEDURE endBackupRedoLogResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_brl_recid = last_brl_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_brl_recid := NULL;
END endBackupRedoLogResync;
/*----------------------------*
* Datafile Copy resync       *
*----------------------------*/

PROCEDURE addControlFileCopy(
  ccf_recid        IN NUMBER
,ccf_stamp        IN NUMBER
,fname            IN VARCHAR2
,tag              IN VARCHAR2
,dbinc_key        IN NUMBER
,ckp_scn          IN NUMBER
,ckp_time         IN DATE
,create_time      IN DATE
,min_offr_recid   IN NUMBER
,block_size       IN NUMBER
,completion_time  IN DATE
,status           IN VARCHAR2
,controlfile_type IN VARCHAR2 DEFAULT NULL
,keep_options     IN NUMBER   DEFAULT NULL
,keep_until       IN DATE     DEFAULT NULL
) IS
local    ccf%rowtype;
BEGIN
  BEGIN
    IF (status <> 'D') THEN
       INSERT INTO ccf(ccf_key, dbinc_key, ccf_recid, ccf_stamp, fname,
          fname_hashkey, tag,
          ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
          completion_time, status, controlfile_type, keep_options, keep_until)
       VALUES (rman_seq.nextval, dbinc_key, ccf_recid, ccf_stamp, fname,
         substr(fname,1,10)||substr(fname,-10), tag,
         ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
         completion_time, status, controlfile_type, keep_options, keep_until);
    END IF;
    -- mark duplicate copies deleted
    UPDATE ccf SET status = 'D'
    WHERE ccf.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
    AND   ccf.fname = addControlFileCopy.fname
    AND   ccf.fname_hashkey = substr(addControlFileCopy.fname, 1, 10) ||
                          substr(addControlFileCopy.fname, -10)
    AND   ccf.status != 'D'
    AND   ccf.ccf_stamp <= addControlFileCopy.ccf_stamp
    AND   NOT (ccf.ccf_recid = addControlFileCopy.ccf_recid AND
               ccf.ccf_stamp = addControlFileCopy.ccf_stamp);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- the controlfile copy exists already
      BEGIN
        SELECT ckp_scn INTO local.ckp_scn
        FROM ccf
        WHERE ccf.dbinc_key = addControlFileCopy.dbinc_key
        AND   ccf.ccf_recid = addControlFileCopy.ccf_recid
        AND   ccf.ccf_stamp = addControlFileCopy.ccf_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the ckp_scn
      IF (ckp_scn <> local.ckp_scn) THEN
        raise_application_error(-20095, 'Invalid ckp_scn');
      END IF;
  END;
END addControlFileCopy;
PROCEDURE addDataFileCopy(
  cdf_recid       IN NUMBER
,cdf_stamp       IN NUMBER
,fname           IN VARCHAR2
,tag             IN VARCHAR2
,file#           IN NUMBER
,create_scn      IN NUMBER
,dbinc_key       IN NUMBER
,incr_level      IN NUMBER
,ckp_scn         IN NUMBER
,ckp_time        IN DATE
,onl_fuzzy       IN VARCHAR2
,bck_fuzzy       IN VARCHAR2
,abs_fuzzy_scn   IN NUMBER
,rcv_fuzzy_scn   IN NUMBER
,rcv_fuzzy_time  IN DATE
,blocks          IN NUMBER
,block_size      IN NUMBER
,completion_time IN DATE
,status          IN VARCHAR2
,keep_options    IN NUMBER
,keep_until      IN DATE
,scanned         IN VARCHAR2
) IS
local    cdf%rowtype;
BEGIN
  BEGIN
    IF (status <> 'D') THEN
       INSERT INTO cdf(cdf_key, dbinc_key, cdf_recid, cdf_stamp,
          file#, create_scn, fname, fname_hashkey, tag, incr_level,
          ckp_scn, ckp_time, onl_fuzzy, bck_fuzzy, abs_fuzzy_scn,
          rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, completion_time,
          status, keep_options, keep_until, scanned)
       VALUES
         (rman_seq.nextval, dbinc_key, cdf_recid, cdf_stamp,
          file#, create_scn, fname, substr(fname,1,10)||substr(fname, -10),
          tag, incr_level, ckp_scn, ckp_time,
          decode(onl_fuzzy,'YES','Y','NO','N'),
          decode(bck_fuzzy,'YES','Y','NO','N'), abs_fuzzy_scn,
          rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, completion_time,
          status, keep_options, keep_until,
          decode(scanned,'YES','Y','NO','N'));
    END IF;
    -- mark previous datafile copies with the same fname deleted
    -- since the new datafile copy has overwritten them.
    UPDATE cdf SET status = 'D'
    WHERE cdf.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE db_key = this_db_key)
    AND   cdf.fname = addDataFileCopy.fname
    AND   cdf.fname_hashkey = substr(addDataFileCopy.fname, 1, 10) ||
                          substr(addDataFileCopy.fname, -10)
    AND   cdf.status != 'D'
    AND   NOT (cdf.cdf_recid = addDataFileCopy.cdf_recid AND
               cdf.cdf_stamp = addDataFileCopy.cdf_stamp);
  EXCEPTION
    WHEN dup_val_on_index THEN
      BEGIN
        SELECT file#, create_scn INTO local.file#, local.create_scn
        FROM cdf
        WHERE cdf.dbinc_key = addDataFileCopy.dbinc_key
        AND   cdf.cdf_recid = addDataFileCopy.cdf_recid
        AND   cdf.cdf_stamp = addDataFileCopy.cdf_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the file# and creation scn
      IF (file# <> local.file#) THEN
        raise_application_error(-20096, 'Invalid file');
      END IF;
      IF (create_scn <> local.create_scn) THEN
        raise_application_error(-20097, 'Invalid create scn');
      END IF;
  END;
END addDataFileCopy;
FUNCTION beginDataFileCopyResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_cdf_recid INTO last_cdf_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_cdf_recid := 0;
  END IF;
  RETURN last_cdf_recid;
END beginDataFileCopyResync;
PROCEDURE checkDataFileCopy(
  cdf_recid        IN NUMBER
,cdf_stamp        IN NUMBER
,fname            IN VARCHAR2
,tag              IN VARCHAR2
,file#            IN NUMBER
,create_scn       IN NUMBER
,create_time      IN DATE
,reset_scn        IN NUMBER
,reset_time       IN DATE
,incr_level       IN NUMBER
,ckp_scn          IN NUMBER
,ckp_time         IN DATE
,onl_fuzzy        IN VARCHAR2
,bck_fuzzy        IN VARCHAR2
,abs_fuzzy_scn    IN NUMBER
,rcv_fuzzy_scn    IN NUMBER
,rcv_fuzzy_time   IN DATE
,blocks           IN NUMBER
,block_size       IN NUMBER
,min_offr_recid   IN NUMBER
,completion_time  IN DATE
,status           IN VARCHAR2
,controlfile_type IN VARCHAR2 DEFAULT NULL
,keep_options     IN NUMBER   DEFAULT NULL
,keep_until       IN DATE     DEFAULT NULL
,scanned          IN VARCHAR2 DEFAULT 'NO'
) IS
dbinc_key NUMBER;
BEGIN
  IF (last_cdf_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (cdf_recid < last_cdf_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (cdf_recid > last_cdf_recid + 1) THEN
    -- there is gap in backup set records
    -- not sure what we should do here
    NULL;
  END IF;
  last_cdf_recid := cdf_recid;
  IF (cdf_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- find the dbinc_key
  dbinc_key := checkIncarnation(reset_scn, reset_time);
  IF (file# = 0) THEN
    addControlFileCopy(cdf_recid, cdf_stamp, fname, tag, dbinc_key,
         ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
         completion_time, status, controlfile_type, keep_options, keep_until);
  ELSE
    addDataFileCopy(cdf_recid, cdf_stamp, fname, tag, file#, create_scn,
         dbinc_key, incr_level, ckp_scn, ckp_time,
         onl_fuzzy, bck_fuzzy, abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time,
         blocks, block_size, completion_time, status,
         keep_options, keep_until, scanned);
  END IF;
END checkDataFileCopy;
PROCEDURE endDataFileCopyResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_cdf_recid = last_cdf_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_cdf_recid := NULL;
END endDataFileCopyResync;
/*----------------------------*
* Proxy Datafile resync      *
*----------------------------*/

PROCEDURE addProxyControlFile(
  dbinc_key       IN NUMBER
,xcf_recid       IN NUMBER
,xcf_stamp       IN NUMBER
,tag             IN VARCHAR2
,ckp_scn         IN NUMBER
,ckp_time        IN DATE
,create_time     IN DATE
,min_offr_recid  IN NUMBER
,block_size      IN NUMBER
,device_type     IN VARCHAR2
,handle          IN VARCHAR2
,comments        IN VARCHAR2
,media           IN VARCHAR2
,media_pool      IN NUMBER
,start_time      IN VARCHAR2
,completion_time IN DATE
,status          IN VARCHAR2
,controlfile_type
                  IN VARCHAR2
,keep_options    IN NUMBER
,keep_until      IN DATE
) IS
local    xcf%rowtype;
BEGIN
  BEGIN
    IF (status <> 'D') THEN
      INSERT INTO xcf(xcf_key, dbinc_key, xcf_recid, xcf_stamp, tag,
        ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
        device_type, handle, handle_hashkey, comments, media, media_pool,
        start_time, completion_time, status, controlfile_type, keep_options, keep_until)
      VALUES (rman_seq.nextval, dbinc_key, xcf_recid, xcf_stamp, tag,
        ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
        device_type, handle,
        substr(device_type,1,10)||substr(handle,1,10)||substr(handle,-10),
        comments, media, media_pool, start_time, completion_time, status,
        controlfile_type, keep_options, keep_until);
    END IF;
    -- mark duplicate copies deleted
    UPDATE xcf SET status = 'D'
    WHERE xcf.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
    AND   xcf.device_type = addProxyControlFile.device_type
    AND   xcf.handle = addProxyControlFile.handle
    AND   xcf.handle_hashkey =
              substr(addProxyControlFile.device_type, 1, 10) ||
              substr(addProxyControlFile.handle, 1, 10) ||
              substr(addProxyControlFile.handle, -10)
    AND   xcf.status != 'D'
    AND   xcf.xcf_stamp <= addProxyControlFile.xcf_stamp
    AND   NOT (xcf.xcf_recid = addProxyControlFile.xcf_recid AND
               xcf.xcf_stamp = addProxyControlFile.xcf_stamp);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- this proxy controlfile backup already exists in the recovery catalog
      BEGIN
        SELECT ckp_scn INTO local.ckp_scn
        FROM xcf
        WHERE xcf.dbinc_key = addProxyControlFile.dbinc_key
        AND   xcf.xcf_recid = addProxyControlFile.xcf_recid
        AND   xcf.xcf_stamp = addProxyControlFile.xcf_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the ckp_scn
      IF (ckp_scn <> local.ckp_scn) THEN
        raise_application_error(-20095, 'Invalid ckp_scn');
      END IF;
  END;
END addProxyControlFile;
PROCEDURE addProxyDataFile(
  dbinc_key       IN NUMBER
,xdf_recid       IN NUMBER
,xdf_stamp       IN NUMBER
,tag             IN VARCHAR2
,file#           IN NUMBER
,create_scn      IN NUMBER
,incr_level      IN NUMBER
,ckp_scn         IN NUMBER
,ckp_time        IN DATE
,onl_fuzzy       IN VARCHAR2
,bck_fuzzy       IN VARCHAR2
,abs_fuzzy_scn   IN NUMBER
,rcv_fuzzy_scn   IN NUMBER
,rcv_fuzzy_time  IN DATE
,blocks          IN NUMBER
,block_size      IN NUMBER
,device_type     IN VARCHAR2
,handle          IN VARCHAR2
,comments        IN VARCHAR2
,media           IN VARCHAR2
,media_pool      IN NUMBER
,start_time      IN VARCHAR2
,completion_time IN DATE
,status          IN VARCHAR2
,keep_options    IN number   DEFAULT NULL
,keep_until      IN date     DEFAULT NULL
) IS
local    xdf%rowtype;
BEGIN
  BEGIN
    IF (status <> 'D') THEN
      INSERT INTO xdf(xdf_key, dbinc_key, xdf_recid, xdf_stamp,
         file#, create_scn, tag, incr_level,
         ckp_scn, ckp_time, onl_fuzzy, bck_fuzzy, abs_fuzzy_scn,
         rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size,
         device_type, handle, handle_hashkey, comments, media, media_pool,
         start_time, completion_time, status,
         keep_options, keep_until)
      VALUES
        (rman_seq.nextval, dbinc_key, xdf_recid, xdf_stamp,
         file#, create_scn, tag,
         incr_level, ckp_scn, ckp_time, decode(onl_fuzzy,'YES','Y','NO','N'),
         decode(bck_fuzzy,'YES','Y','NO','N'), abs_fuzzy_scn,
         rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size,
         device_type, handle,
         substr(device_type,1,10)||substr(handle,1,10)||substr(handle,-10),
         comments, media, media_pool, start_time, completion_time, status,
         keep_options, keep_until);
    END IF;
    -- mark previous datafile copies with the same fname deleted
    -- since the new datafile copy has overwritten them.
    UPDATE xdf SET status = 'D'
    WHERE xdf.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE db_key = this_db_key)
    AND   xdf.device_type = addProxyDataFile.device_type
    AND   xdf.handle = addProxyDataFile.handle
    AND   xdf.handle_hashkey =
              substr(addProxyDataFile.device_type, 1, 10) ||
              substr(addProxyDataFile.handle, 1, 10) ||
              substr(addProxyDataFile.handle, -10)
    AND   xdf.status != 'D'
    AND   NOT (xdf.xdf_recid = addProxyDataFile.xdf_recid AND
               xdf.xdf_stamp = addProxyDataFile.xdf_stamp);
  EXCEPTION
    WHEN dup_val_on_index THEN
      BEGIN
        SELECT file#, create_scn INTO local.file#, local.create_scn
        FROM xdf
        WHERE xdf.dbinc_key = addProxyDataFile.dbinc_key
        AND   xdf.xdf_recid = addProxyDataFile.xdf_recid
        AND   xdf.xdf_stamp = addProxyDataFile.xdf_stamp;
      EXCEPTION
        WHEN no_data_found THEN
          RETURN;
      END;
      -- check the file# and creation scn
      IF (file# <> local.file#) THEN
        raise_application_error(-20096, 'Invalid file');
      END IF;
      IF (create_scn <> local.create_scn) THEN
        raise_application_error(-20097, 'Invalid create scn');
      END IF;
  END;
END addProxyDataFile;
-- Note that this function will be used to start the resync of both proxy
-- datafiles and archived logs, because they both share the same recids.
FUNCTION beginProxyResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_pc_recid INTO last_xdf_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_xdf_recid := 0;
  END IF;
  last_xal_recid := last_xdf_recid;
  RETURN last_xdf_recid;
END beginProxyResync;
PROCEDURE checkProxyDataFile(
  xdf_recid       IN NUMBER
,xdf_stamp       IN NUMBER
,tag             IN VARCHAR2
,file#           IN NUMBER
,create_scn      IN NUMBER
,create_time     IN DATE
,reset_scn       IN NUMBER
,reset_time      IN DATE
,incr_level      IN NUMBER
,ckp_scn         IN NUMBER
,ckp_time        IN DATE
,onl_fuzzy       IN VARCHAR2
,bck_fuzzy       IN VARCHAR2
,abs_fuzzy_scn   IN NUMBER
,rcv_fuzzy_scn   IN NUMBER
,rcv_fuzzy_time  IN DATE
,blocks          IN NUMBER
,block_size      IN NUMBER
,min_offr_recid  IN NUMBER
,device_type     IN VARCHAR2
,handle          IN VARCHAR2
,comments        IN VARCHAR2
,media           IN VARCHAR2
,media_pool      IN NUMBER
,start_time      IN DATE
,completion_time IN DATE
,status          IN VARCHAR2
,controlfile_type
                  IN VARCHAR2 DEFAULT NULL
,keep_options    IN NUMBER   DEFAULT NULL
,keep_until      IN DATE     DEFAULT NULL
) IS
dbinc_key NUMBER;
BEGIN
  IF (last_xdf_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (xdf_recid < last_xdf_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  -- It is OK to have a gap in the recid for proxy records, because
  -- v$proxy_datafile and v$proxy_archivedlog share the same recid sequence.
  last_xdf_recid := xdf_recid;
  IF (xdf_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- find the dbinc_key that this proxy backup belongs to.  It is not
  -- necessarily this_dbinc_key.
  dbinc_key := checkIncarnation(reset_scn, reset_time);
  IF (file# = 0) THEN
    addProxyControlFile(dbinc_key, xdf_recid, xdf_stamp, tag,
         ckp_scn, ckp_time, create_time, min_offr_recid, block_size,
         device_type, handle, comments, media, media_pool, start_time,
         completion_time, status, controlfile_type, keep_options, keep_until);
  ELSE
    addProxyDataFile(dbinc_key, xdf_recid, xdf_stamp, tag, file#, create_scn,
         incr_level, ckp_scn, ckp_time,
         onl_fuzzy, bck_fuzzy, abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time,
         blocks, block_size, device_type, handle, comments, media, media_pool,
         start_time, completion_time, status, keep_options, keep_until);
  END IF;
END checkProxyDataFile;
PROCEDURE endProxyResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_pc_recid = GREATEST(NVL(last_xdf_recid,0),
                                               NVL(last_xal_recid,0))
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_xdf_recid := NULL;
  last_xal_recid := NULL;
END endProxyResync;
/*----------------------------*
* Corrupt Block resync       *
*----------------------------*/

FUNCTION beginBackupCorruptionResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_bcb_recid INTO last_bcb_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_bcb_recid := 0;
  END IF;
  RETURN last_bcb_recid;
END beginBackupCorruptionResync;
PROCEDURE checkBackupCorruption(
  bcb_recid       IN NUMBER
,bcb_stamp       IN NUMBER
,set_stamp       IN NUMBER
,set_count       IN NUMBER
,piece#          IN NUMBER
,file#           IN NUMBER
,block#          IN NUMBER
,blocks          IN NUMBER
,corrupt_scn     IN NUMBER
,marked_corrupt  IN VARCHAR2
,corruption_type IN VARCHAR2
) IS
local   bcb%rowtype;
BEGIN
  IF (last_bcb_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (bcb_recid < last_bcb_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (bcb_recid > last_bcb_recid + 1) THEN
    -- there is gap in deleted object records
    -- not sure what we should do here
    NULL;
  END IF;
  last_bcb_recid := bcb_recid;
  IF (bcb_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- find the bdf_key to which this corrupt block belongs
  BEGIN
    SELECT bdf_key INTO local.bdf_key
    FROM  bdf, bs
    WHERE bdf.bs_key = bs.bs_key
    AND   bs.db_key = this_db_key
    AND   bs.set_stamp = checkBackupCorruption.set_stamp
    AND   bs.set_count = checkBackupCorruption.set_count
    AND   bdf.file# =    checkBackupCorruption.file#;
  EXCEPTION
    WHEN no_data_found THEN
      -- if bdf_key is not found, ignore this corrupt block
      RETURN;
  END;
  BEGIN
    INSERT INTO bcb
      (bdf_key, bcb_recid, bcb_stamp, piece#, block#, blocks,
       corrupt_scn, marked_corrupt, corruption_type)
    VALUES
      (local.bdf_key, bcb_recid, bcb_stamp, piece#, block#, blocks,
       corrupt_scn, decode(marked_corrupt,'YES','Y','NO','N'),
       corruption_type);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- the corrupt block is already in rcvcat, so do nothing
      RETURN;
  END;
END checkBackupCorruption;
PROCEDURE endBackupCorruptionResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_bcb_recid = last_bcb_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_bcb_recid := NULL;
END endBackupCorruptionResync;
FUNCTION beginCopyCorruptionResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_ccb_recid INTO last_ccb_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_ccb_recid := 0;
  END IF;
  RETURN last_ccb_recid;
END beginCopyCorruptionResync;
PROCEDURE checkCopyCorruption(
  ccb_recid       IN NUMBER
,ccb_stamp       IN NUMBER
,cdf_recid       IN NUMBER
,cdf_stamp       IN NUMBER
,file#           IN NUMBER
,block#          IN NUMBER
,blocks          IN NUMBER
,corrupt_scn     IN NUMBER
,marked_corrupt  IN VARCHAR2
,corruption_type IN VARCHAR2
) IS
local   ccb%rowtype;
BEGIN
   IF (last_ccb_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (ccb_recid < last_ccb_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (ccb_recid > last_ccb_recid + 1) THEN
    -- there is gap in deleted object records
    -- not sure what we should do here
    NULL;
  END IF;
  last_ccb_recid := ccb_recid;
  IF (ccb_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- find the cdf_key to which this corrupt block belongs
  BEGIN
    SELECT cdf_key INTO local.cdf_key
    FROM  cdf
    WHERE cdf.dbinc_key = this_dbinc_key
    AND   cdf.cdf_recid = checkCopyCorruption.cdf_recid
    AND   cdf.cdf_stamp = checkCopyCorruption.cdf_stamp
    AND   cdf.file# = checkCopyCorruption.file#;
  EXCEPTION
    WHEN no_data_found THEN
      -- if cdf_key is not found, ignore this corrupt block
      RETURN;
  END;
  BEGIN
    INSERT INTO ccb
      (cdf_key, ccb_recid, ccb_stamp, block#, blocks,
       corrupt_scn, marked_corrupt, corruption_type)
    VALUES
      (local.cdf_key, ccb_recid, ccb_stamp, block#, blocks,
       corrupt_scn, decode(marked_corrupt,'YES','Y','NO','N'),
       corruption_type);
  EXCEPTION
    WHEN dup_val_on_index THEN
      -- the corrupt block is already in rcvcat, so do nothing
      RETURN;
  END;
END checkCopyCorruption;
PROCEDURE endCopyCorruptionResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_ccb_recid = last_ccb_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_ccb_recid := NULL;
END endCopyCorruptionResync;
/*----------------------------*
* Deleted Object resync      *
*----------------------------*/

FUNCTION beginDeletedObjectResync RETURN NUMBER IS
BEGIN
  checkResync;
  IF (this_cf_type = 'CURRENT') THEN
     SELECT high_do_recid INTO last_do_recid
     FROM dbinc
     WHERE dbinc.dbinc_key = this_dbinc_key;
  ELSE
     last_do_recid := 0;
  END IF;
  RETURN last_do_recid;
END beginDeletedObjectResync;
PROCEDURE checkDeletedObject(
  do_recid        IN NUMBER
,do_stamp        IN NUMBER
,object_type     IN VARCHAR2
,object_recid    IN NUMBER
,object_stamp    IN NUMBER
,object_data     IN NUMBER DEFAULT NULL)
IS
  local            bp%rowtype;
  new_status       VARCHAR2(1);
  rc               boolean;
  keep_options     number := NULL;
  keep_until       date   := NULL;
BEGIN
  IF (last_do_recid IS NULL) THEN
    raise_application_error(-20037, 'Invalid last recid');
  END IF;
  IF (do_recid < last_do_recid) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  IF (do_recid > last_do_recid + 1) THEN
    -- there is gap in deleted object records
    -- not sure what we should do here
    NULL;
  END IF;
  last_do_recid := do_recid;
  IF (do_stamp < kccdivts) THEN
     RETURN;                    -- obsolete record from a backup controlfile
  END IF;
  -- change/delete the objects. If the update fails to update a row,
  -- we don't care; it just means that the object has been deleted
  -- from or never inserted into rcvcat.
  IF (object_type like 'BACKUP SET%') THEN
    IF (object_type = 'BACKUP SET KEEP UNTIL') THEN
       keep_until := stamp2date(object_data);
    ELSIF (object_type = 'BACKUP SET KEEP OPTIONS') THEN
       keep_options := object_data;
    ELSE
        raise_application_error(2999,
           'Internal error in checkDeletedObject(): bad object_type '||
           object_type);
    END IF;
    changeBackupSet(object_recid, object_stamp,
                    keep_options, keep_until);
  END IF;
  IF (object_type like 'BACKUP PIECE%') THEN
    IF (object_type = 'BACKUP PIECE') THEN
       new_status := 'D';
    ELSIF (object_type = 'BACKUP PIECE AVAILABLE') THEN
       new_status := 'A';
    ELSIF (object_type = 'BACKUP PIECE EXPIRED') THEN
       new_status := 'X';
    ELSIF (object_type = 'BACKUP PIECE UNAVAILABLE') THEN
       new_status := 'U';
    ELSE
       raise_application_error(20999,
           'Internal error in checkDeletedObject(): bad object_type '||
           object_type);
    END IF;
    changeBackupPiece(object_recid,object_stamp,new_status);
  END IF;
  IF (object_type like 'DATAFILE COPY%') THEN
    IF (object_type = 'DATAFILE COPY') THEN
       new_status := 'D';
    ELSIF (object_type = 'DATAFILE COPY AVAILABLE') THEN
       new_status := 'A';
    ELSIF (object_type = 'DATAFILE COPY EXPIRED') THEN
       new_status := 'X';
    ELSIF (object_type = 'DATAFILE COPY UNAVAILABLE') THEN
       new_status := 'U';
    ELSIF (object_type = 'DATAFILE COPY KEEP UNTIL') THEN
       new_status := NULL;
       keep_until := stamp2date(object_data);
    ELSIF (object_type = 'DATAFILE COPY KEEP OPTIONS') THEN
       new_status := NULL;
       keep_options := object_data;
    ELSE
        raise_application_error(20999,
           'Internal error in checkDeletedObject(): bad object_type '||
           object_type);
    END IF;
    changeDatafileCopy(object_recid, object_stamp, new_status,
                       keep_options, keep_until);
  END IF;
  IF (object_type like 'ARCHIVED LOG%') THEN
    IF (object_type = 'ARCHIVED LOG') THEN
       new_status := 'D';
    ELSIF (object_type = 'ARCHIVED LOG AVAILABLE') THEN
       new_status := 'A';
    ELSIF (object_type = 'ARCHIVED LOG EXPIRED') THEN
       new_status := 'X';
    ELSIF (object_type = 'ARCHIVED LOG UNAVAILABLE') THEN
       new_status := 'U';
    ELSE
       raise_application_error(20999,
           'Internal error in checkDeletedObject(): bad object_type '||
           object_type);
    END IF;
    changeArchivedLog(object_recid, object_stamp, new_status);
  END IF;
  IF (object_type like 'PROXY COPY%') THEN
    IF (object_type = 'PROXY COPY') THEN
       new_status := 'D';
    ELSIF (object_type = 'PROXY COPY AVAILABLE') THEN
       new_status := 'A';
    ELSIF (object_type = 'PROXY COPY EXPIRED') THEN
       new_status := 'X';
    ELSIF (object_type = 'PROXY COPY UNAVAILABLE') THEN
       new_status := 'U';
    ELSIF (object_type = 'PROXY COPY KEEP UNTIL') THEN
       new_status := NULL;
       keep_until := stamp2date(object_data);
    ELSIF (object_type = 'PROXY COPY KEEP OPTIONS') THEN
       new_status := NULL;
       keep_options := object_data;
    ELSE
       raise_application_error(20999,
           'Internal error in checkDeletedObject(): bad object_type '||
           object_type);
    END IF;
    changeProxyCopy(object_recid, object_stamp, new_status,
                    keep_options, keep_until);
  END IF;
END checkDeletedObject;
PROCEDURE endDeletedObjectResync IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE dbinc SET high_do_recid = last_do_recid
     WHERE dbinc.dbinc_key = this_dbinc_key;
  END IF;
  last_do_recid := NULL;
END endDeletedObjectResync;
/*-------------------*
* Change Procedures *
*-------------------*/

/*
* In these change procedures, we don't check that we found the record,
* because we are processing a DL record - i.e. we already processed the
* other cf records and hence it might already be flagged deleted.  This
* applies to any status change because we might make it available and
* then delete the object, so the object would be marked as deleted when
* we process the object and the available status change (first DL) would
* fail to find the object, so would a following DL that marks it deleted.
*/

PROCEDURE changeDatafileCopy(
  cdf_recid    IN NUMBER
,cdf_stamp    IN NUMBER
,status       IN VARCHAR2
,keep_options IN NUMBER DEFAULT NULL
,keep_until   IN DATE   DEFAULT NULL)
IS
  local    dbinc%rowtype;
  fno      cdf.file#%type;
BEGIN
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  -- determine if this is a controlfile copy or datafile copy
  BEGIN
     SELECT file# into fno
       FROM cdf
      WHERE cdf.cdf_recid = changeDatafileCopy.cdf_recid
      AND   cdf.cdf_stamp = changeDatafileCopy.cdf_stamp;
  EXCEPTION
     WHEN no_data_found THEN
        BEGIN
           SELECT 0 into fno
             FROM ccf
            WHERE ccf.ccf_recid = changeDatafileCopy.cdf_recid
            AND   ccf.ccf_stamp = changeDatafileCopy.cdf_stamp;
           -- this is a controlfile, so call changeControlfileCopy
           changeControlfileCopy(cdf_recid, cdf_stamp, status,
                                 keep_options, keep_until);
           RETURN;
        EXCEPTION
           WHEN no_data_found THEN
              RETURN; -- already deleted (we are processing a DL record)
        END;
     WHEN OTHERS THEN
        RAISE;
  END;
  IF  status IS NULL THEN
    -- there is no need to change status so check if need
    -- to change keep attributes
    IF keep_until IS NOT NULL THEN
      UPDATE cdf SET keep_until = changeDatafileCopy.keep_until
      WHERE cdf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND   cdf.cdf_recid = changeDatafileCopy.cdf_recid
      AND   cdf.cdf_stamp = changeDatafileCopy.cdf_stamp;
    END IF;
    IF keep_options IS NOT NULL THEN
      UPDATE cdf SET keep_options = changeDatafileCopy.keep_options
      WHERE cdf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND   cdf.cdf_recid = changeDatafileCopy.cdf_recid
      AND   cdf.cdf_stamp = changeDatafileCopy.cdf_stamp;
    END IF;
  ELSIF status IN ('A','U','X') THEN
    UPDATE cdf SET status = changeDatafileCopy.status
    WHERE cdf.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND   cdf.cdf_recid = changeDatafileCopy.cdf_recid
    AND   cdf.cdf_stamp = changeDatafileCopy.cdf_stamp;
  ELSIF status IN ('R','D') THEN
    DELETE FROM cdf
    WHERE cdf.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND   cdf.cdf_recid = changeDatafileCopy.cdf_recid
    AND   cdf.cdf_stamp = changeDatafileCopy.cdf_stamp;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;
  -- changeDatafileCopy is an atomic operation
  -- if called from deleted objects RESYNC don't release lock on db_inc,
  -- let endCkpt commit all changes.
  IF (this_ckp_key IS NULL) THEN
    commit;
  END IF;
END changeDatafileCopy;
PROCEDURE changeControlfileCopy(
  cdf_recid    IN NUMBER
,cdf_stamp    IN NUMBER
,status       IN VARCHAR2
,keep_options IN NUMBER DEFAULT NULL
,keep_until   IN DATE   DEFAULT NULL
) IS
local    dbinc%rowtype;
BEGIN
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  IF  status IS NULL THEN
    -- there is no need to change status so check if need
    -- to change keep stuff
    IF keep_until IS NOT NULL THEN
      UPDATE ccf SET keep_until = changeControlfileCopy.keep_until
      WHERE  ccf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND    ccf.ccf_recid = changeControlfileCopy.cdf_recid
      AND    ccf.ccf_stamp = changeControlfileCopy.cdf_stamp;
    END IF;
    IF keep_options IS NOT NULL THEN
      UPDATE ccf SET keep_options = changeControlfileCopy.keep_options
      WHERE  ccf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND    ccf.ccf_recid = changeControlfileCopy.cdf_recid
      AND    ccf.ccf_stamp = changeControlfileCopy.cdf_stamp;
    END IF;
  ELSIF status IN ('A','U','X') THEN
    UPDATE ccf SET status = changeControlfileCopy.status
    WHERE  ccf.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND    ccf.ccf_recid = changeControlfileCopy.cdf_recid
    AND    ccf.ccf_stamp = changeControlfileCopy.cdf_stamp;
  ELSIF status IN ('R','D') THEN
    DELETE FROM ccf
    WHERE ccf.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND   ccf.ccf_recid = changeControlfileCopy.cdf_recid
    AND   ccf.ccf_stamp = changeControlfileCopy.cdf_stamp;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;
  -- changeControlfileCopy is an atomic operation
  -- if called from deleted objects RESYNC don't release lock on db_inc,
  -- let endCkpt commit all changes.
  IF (this_ckp_key IS NULL) THEN
    commit;
  END IF;
END changeControlfileCopy;
PROCEDURE changeArchivedLog(
  al_recid IN NUMBER
,al_stamp IN NUMBER
,status   IN VARCHAR2)
IS
BEGIN
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  IF status IN ('A','U','X') THEN
    UPDATE al SET status = changeArchivedLog.status
    WHERE al.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND   al.al_recid = changeArchivedLog.al_recid
    AND   al.al_stamp = changeArchivedLog.al_stamp;
  ELSIF status IN ('R','D') THEN
    -- Bug 1186598 - always delete the row.
    -- see compatibility change in translateArchivedLogPattern
    -- and getArchivedLog
    DELETE FROM al
    WHERE al.dbinc_key IN
      (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
      AND al.al_recid = changeArchivedLog.al_recid
      AND al.al_stamp = changeArchivedLog.al_stamp;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;
  -- changeArchivedLog is an atomic operation
  -- if called from deleted objects RESYNC don't release lock on db_inc,
  -- let endCkpt commit all changes.
  IF (this_ckp_key IS NULL) THEN
    commit;
  END IF;
END changeArchivedLog;
PROCEDURE changeBackupSet(
  recid        IN number
,stamp        IN number
,keep_options IN number
,keep_until   IN date
) IS
local    bs%rowtype;
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  BEGIN
    SELECT bs_key INTO local.bs_key
    FROM bs
    WHERE bs.db_key = this_db_key
    AND   bs.bs_recid = changeBackupSet.recid
    AND   bs.bs_stamp = changeBackupSet.stamp;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      RETURN; -- already deleted (we are processing a DL record)
  END;
  IF keep_until IS NOT NULL THEN
    UPDATE bs SET bs.keep_until = changeBackupSet.keep_until
    WHERE  bs.bs_key = local.bs_key;
  END IF;
  IF keep_options IS NOT NULL THEN
    UPDATE bs SET bs.keep_options = changeBackupSet.keep_options
    WHERE  bs.bs_key = local.bs_key;
  END IF;
  -- changeBackupSet is an atomic operation
  -- if called from deleted objects RESYNC don't release lock on db_inc,
  -- let endCkpt commit all changes.
  IF (this_ckp_key IS NULL) THEN
    commit;
  END IF;
END changeBackupSet;
PROCEDURE changeBackupPiece(
  bp_recid IN NUMBER
,bp_stamp IN NUMBER
,status   IN VARCHAR2
) IS
local    bp%rowtype;
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  BEGIN
    SELECT bs_key INTO local.bs_key
    FROM bp
    WHERE bp.db_key = this_db_key
    AND   bp.bp_recid = changeBackupPiece.bp_recid
    AND   bp.bp_stamp = changeBackupPiece.bp_stamp;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      RETURN; -- already deleted (we are processing a DL record)
  END;
  IF status in ('A','U','X') THEN
    UPDATE bp SET status = changeBackupPiece.status
    WHERE bp.db_key = this_db_key
    AND   bp.bp_recid = changeBackupPiece.bp_recid
    AND   bp.bp_stamp = changeBackupPiece.bp_stamp;
  ELSIF status = 'D' THEN
    DELETE FROM bp
    WHERE bp.db_key = this_db_key
    AND   bp.bp_recid = changeBackupPiece.bp_recid
    AND   bp.bp_stamp = changeBackupPiece.bp_stamp;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;
  -- revalidate the backup set
  updateBackupSetRec(local.bs_key);
  -- changeBackupPiece is an atomic operation
  -- if called from deleted objects RESYNC don't release lock on db_inc,
  -- let endCkpt commit all changes.
  IF (this_ckp_key IS NULL) THEN
    commit;
  END IF;
END changeBackupPiece;
PROCEDURE changeProxyCopy(
  pc_recid     IN NUMBER
,pc_stamp     IN NUMBER
,status       IN VARCHAR2
,keep_options IN NUMBER DEFAULT NULL
,keep_until   IN DATE   DEFAULT NULL
) IS
BEGIN
  IF this_db_key IS NULL THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  IF status IS NULL THEN
    -- there is no need to change status so check if need
    -- to change keep stuff
    IF keep_until IS NOT NULL THEN
      UPDATE xdf SET xdf.keep_until = changeProxyCopy.keep_until
      WHERE   xdf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND    xdf.xdf_recid = changeProxyCopy.pc_recid
      AND    xdf.xdf_stamp = changeProxyCopy.pc_stamp;
      -- if it wasn't a proxy datafile, maybe it's a proxy controlfile
      IF sql%rowcount = 0 THEN
        UPDATE xcf SET xcf.keep_until = changeProxyCopy.keep_until
        WHERE  xcf.dbinc_key in
          (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
        AND   xcf.xcf_recid = changeProxyCopy.pc_recid
        AND   xcf.xcf_stamp = changeProxyCopy.pc_stamp;
      END IF;
    END IF;
    IF keep_options IS NOT NULL THEN
      UPDATE  xdf SET xdf.keep_options = changeProxyCopy.keep_options
      WHERE   xdf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND    xdf.xdf_recid = changeProxyCopy.pc_recid
      AND    xdf.xdf_stamp = changeProxyCopy.pc_stamp;
      -- if it wasn't a proxy datafile, maybe it's a proxy controlfile
      IF sql%rowcount = 0 THEN
        UPDATE xcf SET xcf.keep_options = changeProxyCopy.keep_options
        WHERE  xcf.dbinc_key in
          (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
        AND   xcf.xcf_recid = changeProxyCopy.pc_recid
        AND   xcf.xcf_stamp = changeProxyCopy.pc_stamp;
      END IF;
    END IF;
  ELSIF status in ('A','U','X') THEN
    UPDATE xdf SET status = changeProxyCopy.status
    WHERE xdf.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND   xdf.xdf_recid = changeProxyCopy.pc_recid
    AND   xdf.xdf_stamp = changeProxyCopy.pc_stamp;
    -- if it wasn't a proxy datafile, maybe it's a proxy controlfile
    IF sql%rowcount = 0 THEN
      UPDATE xcf SET status = changeProxyCopy.status
      WHERE xcf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND   xcf.xcf_recid = changeProxyCopy.pc_recid
      AND   xcf.xcf_stamp = changeProxyCopy.pc_stamp;
    END IF;
  ELSIF status IN ('R','D') THEN
    DELETE FROM xdf
    WHERE xdf.dbinc_key in
      (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
    AND   xdf.xdf_recid = changeProxyCopy.pc_recid
    AND   xdf.xdf_stamp = changeProxyCopy.pc_stamp;
    -- if it wasn't a proxy datafile, maybe it's a proxy controlfile
    IF sql%rowcount = 0 THEN
      DELETE FROM xcf
      WHERE xcf.dbinc_key in
        (select dbinc_key from dbinc where dbinc.db_key = this_db_key)
      AND   xcf.xcf_recid = changeProxyCopy.pc_recid
      AND   xcf.xcf_stamp = changeProxyCopy.pc_stamp;
    END IF;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;
  -- changeProxyCopy is an atomic operation
  -- if called from deleted objects RESYNC don't release lock on db_inc,
  -- let endCkpt commit all changes.
  IF (this_ckp_key IS NULL) THEN
    commit;
  END IF;
END changeProxyCopy;
/*----------------------------*
* Stored Script Procedures   *
*----------------------------*/

PROCEDURE createScript(name IN VARCHAR2) IS
  foo NUMBER;
BEGIN
  scr_key := NULL;                      -- for safety
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  SELECT count(*)
    INTO foo
    FROM scr
    WHERE scr.db_key = this_db_key
    AND   scr.scr_name = createScript.name;
  IF foo > 0 THEN
    raise_application_error(-20401, 'script already exists');
  END IF;
  INSERT INTO scr VALUES(rman_seq.nextval, this_db_key, name);
  SELECT rman_seq.currval INTO scr_key FROM dual;
  scr_line := 1;
  -- createScript is an atomic operation
  commit;
END;
PROCEDURE replaceScript(name IN VARCHAR2) IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  SELECT scr_key INTO scr_key
  FROM scr
  WHERE scr.db_key = this_db_key AND
        scr.scr_name = replaceScript.name;
  DELETE FROM scrl WHERE scrl.scr_key = dbms_rcvcat.scr_key;
  scr_line := 1;
  -- replaceScript is an atomic operation
   commit;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    createScript(name);
END;
PROCEDURE putLine(line IN VARCHAR2) IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  IF (scr_key IS NULL) THEN
    raise_application_error(-20402, 'createScript or replaceScript not done');
  END IF;
  INSERT INTO scrl VALUES(scr_key, scr_line, line);
  scr_line := scr_line + 1;
END;
PROCEDURE deleteScript(name IN VARCHAR2) IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  SELECT scr_key INTO scr_key
  FROM scr
  WHERE scr.db_key = this_db_key AND
        scr.scr_name = deleteScript.name;
  DELETE FROM scr
  WHERE scr.scr_key = dbms_rcvcat.scr_key;
  scr_key := NULL;
  -- deleteScript is an atomic operation
  commit;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    scr_key := NULL;
    raise_application_error(-20400, 'stored script not found');
END;
PROCEDURE getScript(name IN VARCHAR2) IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  SELECT scr_key INTO scr_key
  FROM scr
  WHERE scr.db_key = this_db_key AND
        scr.scr_name = getScript.name;
  IF scrlQ%ISOPEN THEN
    CLOSE scrlQ;
  END IF;
  OPEN scrlQ(scr_key);
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    scr_key := NULL;
    raise_application_error(-20400, 'stored script not found');
END;
FUNCTION getLine RETURN VARCHAR2 IS
  scrl_row   scrlQ%rowtype;
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  IF NOT scrlQ%ISOPEN THEN
    raise_application_error(-20401, 'getScript not done');
  END IF;
  FETCH scrlQ INTO scrl_row;
  RETURN scrl_row.text;
END;
PROCEDURE commitChanges IS
BEGIN
  commit;
END;
-- version info
-- Return all the protocol versions that we support, one at a time.
-- Return them in ascending version number order.
FUNCTION getPackageVersion RETURN VARCHAR2 IS
BEGIN
  if version_counter > version_max_index then
    version_counter := 1;
    return null;
  end if;
  version_counter := version_counter + 1;
  return version_list(version_counter - 1);
END;
FUNCTION getCatalogVersion RETURN VARCHAR2 IS
version rcver.version%type;
BEGIN
  IF NOT rcverQ%ISOPEN THEN
    open rcverQ;
  END IF;
  FETCH rcverQ into version;
  IF rcverQ%NOTFOUND THEN  -- end of fetch
    close rcverQ;
    return NULL;
  END IF;
  RETURN version;
END;
/*---------------------------------------*
* Procedures for clone database support *
*---------------------------------------*/

PROCEDURE setCloneName(file#            IN NUMBER
                      ,creation_change# IN NUMBER
                      ,clone_fname      IN VARCHAR2) IS
lfname df.clone_fname%TYPE;
BEGIN
   deb('setCloneName: file#='         ||to_char(file#)||
                   ', creation_fname='||to_char(creation_change#)||
                   ', clone_fname='   ||clone_fname);
  -- Note: if target database has been just upgraded to 9.0 then
  -- clone_fname is unknown and we will not change recovery catalog.
  IF (clone_fname = 'UNKNOWN') THEN
     RETURN;
  END IF;
  IF (clone_fname = 'NONE') THEN
     lfname := NULL;
  ELSE
     lfname := clone_fname;
  END IF;
  UPDATE df SET df.clone_fname = lfname WHERE
  df.dbinc_key = this_dbinc_key AND
  df.file# = setCloneName.file# AND
  df.create_scn = setCloneName.creation_change#;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    raise_application_error(-20105, 'datafile missing');
END;
-- We need need this function in RCVCAT PL/SQL package because recover.txt
-- cannot access Recovery Catalog RCVMAN PL/SQL package.
FUNCTION getCloneName( file#            IN NUMBER
                      ,creation_change# IN NUMBER) RETURN VARCHAR2 IS
ret df.clone_fname%TYPE;
BEGIN
  -- call getCloneName from rcvman package. Note that in this we call
  -- recovery catalog version!
  ret := dbms_rcvman.getCloneName(file#, creation_change#);
  RETURN ret;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    raise_application_error(-20105, 'datafile missing');
END;
/*-----------------------------------*
* Procedures for RMAN configuration *
*-----------------------------------*/

PROCEDURE setConfig(conf#            IN NUMBER
                   ,name             IN VARCHAR2
                   ,value            IN VARCHAR2) IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  INSERT INTO conf(db_key, conf#, name, value)
  VALUES(this_db_key, conf#, name, value);
EXCEPTION
  WHEN dup_val_on_index THEN
    UPDATE conf SET
           conf.name = name,
           conf.value = value WHERE conf.conf# = conf#
                              AND   conf.db_key = this_db_key;
  RETURN;
END;
PROCEDURE resetConfig IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  DELETE conf
  WHERE  conf.db_key = this_db_key;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
      -- if db_key is not found, ignore this operation
  RETURN;
END;
PROCEDURE deleteConfig(conf#            IN NUMBER) IS
BEGIN
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  DELETE conf WHERE conf.conf#  = conf# AND
                    conf.db_key = this_db_key;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
      -- if nothing is found, ignore this operation
  RETURN;
END;
/*-------------------------*
* Catalog upgrade support *
*-------------------------*/

/*
* NOTES:
*
* These procedures *must* tolerate being called *before* dbinc_key
* has been set.
*/

PROCEDURE bsStatusRecalc(status IN varchar2) IS
cursor bsQ(status varchar2) IS
  SELECT bs_key
  FROM bs
  WHERE bs.status = bsStatusRecalc.status;
bsQrec bsQ%ROWTYPE;
BEGIN
  FOR bsQrec in bsQ(status) LOOP
-- delete obsolete rc_backup_corruption entries too created by
-- 'backup validate' command.
    updateBackupSetRec(bsQrec.bs_key, FALSE);
  END LOOP;
  commit;
END;
/*-------------------*
* Utility functions *
*-------------------*/

PROCEDURE reNormalize(newname IN varchar2, oldname OUT varchar2) IS
BEGIN
   IF newname IS NULL THEN -- initialize
      IF reNorm_dfatt_c%ISOPEN THEN
         CLOSE reNorm_dfatt_c;
      END IF;
      IF reNorm_orl_c%ISOPEN THEN
         CLOSE reNorm_orl_c;
      END IF;
      IF reNorm_al_c%ISOPEN THEN
         CLOSE reNorm_al_c;
      END IF;
      IF reNorm_bp_c%ISOPEN THEN
         CLOSE reNorm_bp_c;
      END IF;
      IF reNorm_ccf_c%ISOPEN THEN
         CLOSE reNorm_ccf_c;
      END IF;
      IF reNorm_dfatt_c%ISOPEN THEN
         CLOSE reNorm_cdf_c;
      END IF;
      reNorm_state := RENORM_DFATT;
   ELSE -- update the previous row
      IF reNorm_state = RENORM_DFATT THEN
         UPDATE dfatt SET fname = newname WHERE CURRENT OF reNorm_dfatt_c;
      ELSIF reNorm_state = RENORM_ORL THEN
         UPDATE orl SET fname = newname WHERE CURRENT OF reNorm_orl_c;
      ELSIF reNorm_state = RENORM_AL THEN
         UPDATE al SET fname = newname,
           fname_hashkey = substr(newname,1,10) || substr(newname, -10)
           WHERE CURRENT OF reNorm_al_c;
      ELSIF reNorm_state = RENORM_BP THEN
         UPDATE bp SET handle = newname,
           handle_hashkey = substr(device_type,1,10) ||
                            substr(newname,1,10)     ||
                            substr(newname,-10)
           WHERE CURRENT OF reNorm_bp_c;
      ELSIF reNorm_state = RENORM_CCF THEN
         UPDATE ccf SET fname = newname,
           fname_hashkey = substr(newname,1,10) || substr(newname, -10)
           WHERE CURRENT OF reNorm_ccf_c;
      ELSIF reNorm_state = RENORM_CDF THEN
         UPDATE cdf SET fname = newname,
           fname_hashkey = substr(newname,1,10) || substr(newname, -10)
           WHERE CURRENT OF reNorm_cdf_c;
      END IF;
   END IF;
   IF reNorm_state = RENORM_DFATT THEN
      IF NOT reNorm_dfatt_c%ISOPEN THEN
         OPEN reNorm_dfatt_c;
      END IF;
      FETCH reNorm_dfatt_c INTO oldname;
      IF reNorm_dfatt_c%NOTFOUND THEN
         CLOSE reNorm_dfatt_c;
         reNorm_state := RENORM_ORL;
      END IF;
   END IF;
   IF reNorm_state = RENORM_ORL THEN
      IF NOT reNorm_orl_c%ISOPEN THEN
         OPEN reNorm_orl_c;
      END IF;
      FETCH reNorm_orl_c INTO oldname;
      IF reNorm_orl_c%NOTFOUND THEN
         CLOSE reNorm_orl_c;
         reNorm_state := RENORM_AL;
      END IF;
   END IF;
   IF reNorm_state = RENORM_AL THEN
      IF NOT reNorm_al_c%ISOPEN THEN
         OPEN reNorm_al_c;
      END IF;
      FETCH reNorm_al_c INTO oldname;
      IF reNorm_al_c%NOTFOUND THEN
         CLOSE reNorm_al_c;
         reNorm_state := RENORM_BP;
      END IF;
   END IF;
   IF reNorm_state = RENORM_BP THEN
      IF NOT reNorm_bp_c%ISOPEN THEN
         OPEN reNorm_bp_c;
      END IF;
      FETCH reNorm_bp_c INTO oldname;
      IF reNorm_bp_c%NOTFOUND THEN
         CLOSE reNorm_bp_c;
         reNorm_state := RENORM_CCF;
      END IF;
   END IF;
   IF reNorm_state = RENORM_CCF THEN
      IF NOT reNorm_ccf_c%ISOPEN THEN
         OPEN reNorm_ccf_c;
      END IF;
      FETCH reNorm_ccf_c INTO oldname;
      IF reNorm_ccf_c%NOTFOUND THEN
         CLOSE reNorm_ccf_c;
         reNorm_state := RENORM_CDF;
      END IF;
   END IF;
   IF reNorm_state = RENORM_CDF THEN
      IF NOT reNorm_cdf_c%ISOPEN THEN
         OPEN reNorm_cdf_c;
      END IF;
      FETCH reNorm_cdf_c INTO oldname;
      IF reNorm_cdf_c%NOTFOUND THEN
         CLOSE reNorm_cdf_c;
         reNorm_state := NULL;
         oldname := NULL;
         commit;
      END IF;
   END IF;
END reNormalize;
-- The sanityCheck procedure can be used for any cleaning up of the recovery
-- catalog that can be done solely by examination of the recovery catalog
-- itself.  It is the last thing that is done during resync, before commit.
-- Forward declaration of functions and procedures used by sanityCheck
PROCEDURE cleanupResyncedBS;
PROCEDURE cleanupCKP;
PROCEDURE sanityCheck IS
BEGIN
  cleanupResyncedBS;
  cleanupCKP;
END sanityCheck;
PROCEDURE cleanupResyncedBS IS
   cnt    number;
   aux    number;
BEGIN
  -- Check that no backup sets have just been resynced
  -- for which there are no pieces.  This can happen when backup sets are
  -- created in advance of the pieces and later on, no backup piece is actually
  -- inserted.
  IF (cntbs is NULL) THEN
    raise_application_error(-20107, 'invalid bskey counter');
  ELSIF (cntbs = 0) THEN
    return;
  ELSE
    FOR aux IN 1 .. cntbs LOOP
      BEGIN
        SELECT count(*) into cnt from bs where bs_key = insertedbs(aux);
        IF cnt > 0 THEN
          updateBackupSetRec(insertedbs(aux));
        END IF;
      END;
    END LOOP;
    cntbs := 0;
  END IF;
END cleanupResyncedBS;
PROCEDURE cleanupCKP IS
  scn       NUMBER;
  seq       NUMBER;
  ckp_key_1 NUMBER;
  ckp_key_2 NUMBER;
BEGIN
  -- Remove unneeded rows from the CKP table.  We can remove all rows EXCEPT:
  --   The rows that are referenced by foreign key columns from other tables.
  --   For the query in beginckpt, the row with the highest ckp_key.
  --   The one or two rows that are needed by getCheckpoint.
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  dbms_rcvman.getCheckpoint(scn, seq, ckp_key_1, ckp_key_2);
  deb('cleanupCKP scn=' || to_char(scn));
  deb('cleanupCKP seq=' || to_char(seq));
  deb('cleanupCKP ckp_key_1=' || to_char(ckp_key_1));
  deb('cleanupCKP ckp_key_2=' || to_char(ckp_key_2));
  DELETE from ckp
    WHERE dbinc_key = this_dbinc_key AND
    ckp_key NOT IN (ckp_key_1, ckp_key_2) AND
    ckp_key NOT IN
     (SELECT nvl(max(ckp_key),0) FROM ckp WHERE dbinc_key =this_dbinc_key UNION
      SELECT start_ckp_key FROM tsatt where dbinc_key = this_dbinc_key UNION
      SELECT nvl(end_ckp_key,0) FROM tsatt where dbinc_key = this_dbinc_key UNION
      SELECT start_ckp_key FROM dfatt where dbinc_key = this_dbinc_key UNION
      SELECT nvl(end_ckp_key,0) FROM dfatt where dbinc_key = this_dbinc_key);
   deb('cleanupCKP deleted ' || to_char(sql%rowcount) || ' rows from ckp table');
END cleanupCKP;
-- PUT-GET removed for 9.0.1 production
-- FUNCTION getsfile (this_fname IN  VARCHAR2,
--                    createfile IN  NUMBER,
--                    newfile    OUT NUMBER) RETURN NUMBER IS
-- this_file_key    number;
-- BEGIN
--    newfile := 0;
--    IF (this_db_key IS NULL) THEN
--      raise_application_error(-20021, 'Database not set');
--    END IF;
--
--    IF this_fname IS NULL THEN
--       deb('getsfile returning this_db_key as filename is null:'||this_db_key);
--       return this_db_key;
--    END IF;
--
--    deb('getsfile, fname:'||this_fname||'  createfile:'||createfile);
--    BEGIN
--       SELECT sfile_key
--         INTO this_file_key
--         FROM sfile
--        WHERE fname = this_fname
--          AND db_key = this_db_key;
--    EXCEPTION
--       WHEN no_data_found THEN
--          newfile := 1;
--          this_file_key := 0;
--    END;
--
--    IF this_file_key = 0 THEN
--       deb('File does not exist in catalog, fname:'||this_fname);
--       IF createfile = 1 THEN
--          SELECT rman_seq.nextval INTO this_file_key FROM dual;
--          deb('New key for fname:'||this_fname||
--              '   is: '||this_file_key);
--          INSERT INTO sfile
--             (sfile_key, db_key, fname, flen, fstamp, contents)
--          VALUES
--             (this_file_key, this_db_key, this_fname, 0, sysdate, empty_blob());
--       END IF;
--    ELSE
--       deb('File found in catalog, fname:'||this_fname||
--           '   key: '||this_file_key);
--       IF createfile = 1 THEN
--          UPDATE sfile SET
--             contents = empty_blob(),
--                 flen = 0
--           WHERE sfile_key = this_file_key;
--       END IF;
--    END IF;
--    deb('getsfile returning key:'||this_file_key||' for fname:'||
--        this_fname||'   newfile:'||newfile);
--    RETURN this_file_key;
-- END getsfile;
FUNCTION getDbid RETURN NUMBER IS
  dbid   NUMBER;
BEGIN
  SELECT db.db_id
    INTO dbid
    FROM db
   WHERE db_key = this_db_key
     AND curr_dbinc_key = this_dbinc_key;
  RETURN dbid;
  EXCEPTION
     WHEN no_data_found THEN
        raise_application_error(-20001, 'Database not found');
END getDbid;
FUNCTION beginIncarnationResync RETURN NUMBER IS
BEGIN
  checkResync;
  
  IF (this_cf_type = 'CURRENT') THEN
     SELECT last_kccdivts INTO last_kccdivts
     FROM db
     WHERE db.db_key = this_db_key;
  ELSE
     last_kccdivts := 0;
  END IF;
  IF (last_kccdivts IS NULL) THEN
     last_kccdivts := 0;
  END IF;
  RETURN last_kccdivts;
END beginIncarnationResync;
-- Return the correct incarnation for the supplied resetlogs data, taking
-- the current 'this_db_key' into account.  If no matching incarnation is
-- found, then an artificial one is added.
FUNCTION checkIncarnation(reset_scn         IN NUMBER,
                          reset_time        IN DATE,
                          prior_reset_scn   IN NUMBER DEFAULT NULL,
                          prior_reset_time  IN DATE DEFAULT NULL,
                          db_name           IN VARCHAR2 DEFAULT 'UNKNOWN')
                        RETURN NUMBER IS
local             dbinc%rowtype;
prior_dbinc_key   number := NULL;
BEGIN
  BEGIN
    SELECT dbinc_key, parent_dbinc_key, db_name
       INTO local.dbinc_key, local.parent_dbinc_key, local.db_name
    FROM dbinc
    WHERE dbinc.db_key = this_db_key
    AND   dbinc.reset_scn = checkIncarnation.reset_scn
    AND   dbinc.reset_time = checkIncarnation.reset_time;
  EXCEPTION
    WHEN no_data_found THEN
      local.dbinc_key := NULL;
      local.parent_dbinc_key := NULL;
      local.db_name := 'UNKNOWN';
  END;
  IF (local.parent_dbinc_key IS NULL AND
      checkIncarnation.prior_reset_scn IS NOT NULL) THEN
     BEGIN
        SELECT dbinc_key
           INTO prior_dbinc_key
        FROM dbinc
        WHERE dbinc.db_key = this_db_key
        AND   dbinc.reset_scn = checkIncarnation.prior_reset_scn
        AND   dbinc.reset_time = checkIncarnation.prior_reset_time;
     EXCEPTION
       WHEN no_data_found THEN
          prior_dbinc_key := NULL;
     END;
  END IF;
  IF (local.dbinc_key IS NOT NULL) THEN
     -- parent_dbinc_key not filled up?
    IF (local.parent_dbinc_key IS NULL AND
        prior_dbinc_key IS NOT NULL) THEN
      UPDATE dbinc SET parent_dbinc_key = prior_dbinc_key
      WHERE dbinc.dbinc_key = local.dbinc_key;
    END IF;
     -- db_name not filled up?
    IF (local.db_name != 'UNKNOWN' AND
        checkIncarnation.db_name != 'UNKNOWN') THEN
      UPDATE dbinc SET db_name = checkIncarnation.db_name
      WHERE dbinc.dbinc_key = local.dbinc_key;
    END IF;
    RETURN local.dbinc_key;
  END IF;
    
  -- the database incarnation was not found, create an artificial one
  BEGIN
    INSERT INTO dbinc
      (dbinc_key, db_key, db_name, reset_scn, reset_time, parent_dbinc_key)
    VALUES
      (rman_seq.nextval, this_db_key,
       checkIncarnation.db_name, checkIncarnation.reset_scn,
       checkIncarnation.reset_time, prior_dbinc_key);
  EXCEPTION
    WHEN dup_val_on_index THEN
      raise_application_error(-20009, 'Db incarnation already registered');
  END;
  SELECT rman_seq.currval INTO local.dbinc_key FROM dual;
  RETURN local.dbinc_key;
END checkIncarnation;
PROCEDURE endIncarnationResync(high_kccdivts IN NUMBER) IS
BEGIN
  IF (this_cf_type = 'CURRENT') THEN
     UPDATE db SET last_kccdivts = endIncarnationResync.high_kccdivts
     WHERE db.db_key = this_db_key;
  END IF;
  last_kccdivts := NULL;
END endIncarnationResync;
/*--------------------------------------------------*
* Package Instantiation:  Initialize Package State *
*--------------------------------------------------*/

BEGIN
  tsRec.ts# := NULL;                    -- not in TableSpaceResync
  dfRec.file# := NULL;                  -- not in middle of dfResync
  version_list(1) := '08.00.04';
  -- In 8.0.5 the following changes were made:
  -- 1. Allow null for fname and blocks in checkDatafile.  This was
  -- done for bug 612344, which had to do with datafiles that are
  -- MISSING or UNNAMED (KCCFECKD bit set).  In these cases, the controlfile
  -- does not contain a valid filename, and also implies the filesize
  -- which is in the fileheader cannot be obtained either.
  version_list(2) := '08.00.05';
  -- In 8.1.3 the following changes were made:
  -- 1. Added 'X' (expired) backup piece status.
  version_list(3) := '08.01.03';
  -- In 8.1.6 the following changes were made:
  -- 1) add stopTime to checkDatafile()
  version_list(4) := '08.01.06';
  -- In 8.1.7 the following changes were made:
  -- 1) Add controlfile_type to bcf
  -- 2) Add controlfile_included to bs
  -- 3) Add controlfile_type to ccf
  -- 4) Add controlfile_type to xcf
  -- 5) Add is_standby to al
  -- 6) Add input_file_scan_only to bs
  version_list(5) := '08.01.07';
  -- In 9.0.0 the following changes were made:
  -- 1) add setConfig(), deleteConfig(), resetConfig(),
  --        beginConfigResync() and endConfigREsync().
  -- 2) Added 'X' (expired) status for CC, DC, AL objects.
  -- 3) Added blocks to bcf
  version_list(6) := '09.00.00';
  -- In 9.2.0 the following changes were made:
  -- 1) add beinBackupSpFileResync, addBackupSpFile, checkBackupSpFile,
  --    endBackupSpFileResync
  -- 2) add beginIncarnationResync, endIncarnationResync, checkIncarnation
  -- 3) Never do a full resync when controlfile is not CURRENT and
  --    don't update high water marks.
  version_list(7) := '09.02.00';
  version_max_index := 7;
END dbms_rcvcat;

See Also

List of packages