DBMS_RCVCAT |
VALID |
05/12/2002 |
05/12/2002 |
Total: 71 procedure(s)
Total: 26 function(s)
RMAN |
DBMS_RCVCAT |
PACKAGE BODY |
Total: 1 used by reference(s)
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;
PROCEDURE setDebugOn;
PROCEDURE setDebugOff;
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
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
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
-- 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.
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
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
);
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
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
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
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.
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.
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.
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.
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;
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;
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;
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;
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;
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.
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;
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;
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.
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
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;
procedure setCloneName(file# IN number
,creation_change# IN number
,clone_fname IN varchar2);
FUNCTION getCloneName(file# IN number
,creation_change# IN number) RETURN VARCHAR2;
PROCEDURE setConfig(conf# IN NUMBER
,name IN VARCHAR2
,value IN VARCHAR2);
PROCEDURE resetConfig;
PROCEDURE deleteConfig(conf# IN NUMBER);
function getPackageVersion return varchar2;
function getCatalogVersion return varchar2;
PROCEDURE bsStatusRecalc(status IN varchar2);
-- 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;
MAXNUMVAL CONSTANT NUMBER := 2**32-1;
catalog_version CONSTANT VARCHAR2(12) := '09.02.00';
-- 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;
-- 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:
-- 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;
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;
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;
-- 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;
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;
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;
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;
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;
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;
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;
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;
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 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;
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;
-- 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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
List of packages