# 
# $Header: recover.txt 23-sep-99.19:06:58 fsanchez Exp $
#
## ****************************************************************************
## * RECOVER.BSQ is now a DERIVED OBJECT.  If you are editing                 *
## * .../rdbms/admin/recover.bsq, STOP and edit                               *
## * .../rdbms/src/client/tools/rcvman/recover.txt instead.  recover.bsq is   *
## * derived from recover.txt, and any changes made directly to recover.bsq   *
## * will be lost.                                                            *
## ****************************************************************************
#
# When adding new libunits that are not related to the CREATE/DROP/UPGRADE
# CATALOG commands, search for STOPSTOP and add your libunits before that
# marker.  Because the PL/SQL packages used by CREATE/UPGRADE CATALOG are so
# large, and those commands are seldom used, we don't load their libunits until
# those commands are executed.  So, if libunits for other commands are added
# after the STOPSTOP marker, they will not be loaded during normal RMAN
# initialization.
#
#    MODIFIED (MM/DD/YY)
#    fsanchez  09/23/99 - add DELETED column to v$backup_piece and v$proxy_df
#    swerthei  09/14/99 - add dbms_rcvcat.reNormalize
#    gpongrac  08/04/99 - add config table
#    gpongrac  08/02/99 - only resync circular recs with stamp >= kccdivts
#    fsanchez  07/22/99 - 935058
#    gpongrac  07/27/99 - change msg 8511 to include tag                       
#    gpongrac  07/14/99 - add stop_time to df table                            
#    gpongrac  07/12/99 - fix sequence# in 8060 message
#    gpongrac  07/06/99 - change rcver to 8.1.6                                
#    gpongrac  06/15/99 - report obsolete support for new interface
#    gpongrac  01/12/99 - 8.2 restructure
#    dbeusee   09/10/98 - bug-728666                                           
#    gpongrac  02/26/99 - autolocate feature
#    gpongrac  04/08/99 - bug 867928: change comments for pkg bodies
#    fsanchez  04/07/99 - 787067_2
#    fsanchez  01/11/99 - comments_tspitr
#    swerthei  12/10/98 - workaround for bug 756629
#    mluong    12/01/98 - fix conflicts in comments
#    rlu       11/25/98 - bug_621515
#    dbeusee   11/13/98 - misc_815: Handle LIST RECOVERABLE UNTIL TIME...
#    dbeusee   11/12/98 - misc_815: fix LIST COPY LIKE...
#    gpongrac  10/19/98 - misc_815: add tagKind
#    gpongrac  10/05/98 - misc_815: remove debugging
#    dbeusee   09/17/98 - misc_815: fixed bug 713058
#    gpongrac  09/10/98 - misc_815: add fullKind mask
#    fsanchez  11/18/98 - bug-766968
#    swerthei  10/16/98 - bug 729574 - wrap to_number around bitand
#    swerthei  10/24/98 - add proxy support to REPORT OBSOLETE
#    swerthei  10/23/98 - add procedures for proxy list
#    swerthei  10/19/98 - better messages for restore validate
#    vsrihari  10/29/98 - add check_logical to backup, copy
#    fsanchez  10/07/98 - bug-607271
#    vsrihari  08/31/98 - shutdown clone before pitr import
#    gpongrac  09/28/98 - ignore plugged in tablespaces in ts cursor
#    swerthei  06/19/98 - move catrman, catnormn, statements to here
#    fsanchez  05/26/98 - clone_db
#    fsanchez  06/30/98 - clean_msgs
#    swerthei  06/17/98 - make compatible with 8.0
#    swerthei  06/03/98 - add media_pool
#    swerthei  05/19/98 - query channel for capabilities
#    swerthei  05/18/98 - proxy copy
#    fsanchez  05/15/98 - duplex_backup_set
#    gdoherty  05/25/98 - update arg to export for pitr
#    fsanchez  05/15/98 - duplex_backup_set
#    gpongrac  05/05/98 - ignore plugged but not yet converted datafiles
#    gpongrac  05/01/98 - fix 8071
#    gpongrac  04/23/98 - msglog enhancements
#    dbeusee   04/04/98 - xcheck enh.
#    fsanchez  03/30/98 - Allow duplex > 2
#    dbeusee   03/02/98 - Add missing col.
#    gpongrac  01/28/98 - change deviceALlocate to return node id
#    gpongrac  01/16/98 - bug 612344: dont change dfname if its MISSING
#    mzhou     01/14/98 - bug-607287 fix
#    gpongrac  01/09/98 - update version number to 8.1.2
#    gpongrac  12/16/97 - add exception handler to log_apply
#    gpongrac  12/11/97 - call replicate from controlfilecopy restore
#    dbeusee   11/xx/97 - restore/replicate controlfile
#    gpongrac  09/30/97 - update version #
#    mzhou     10/07/97 - add archive log current to pre_tspitr
#    gpongrac  09/05/97 - add name_log procedure
#    swerthei  08/21/97 - SMR -> SMR1, SMR2, SMR3
#    swerthei  08/14/97 - add comments
#    gpongrac  06/30/97 - keep offline clean and read-only scn in df table
#    gpongrac  06/30/97 - add current offline ranges to kccor
#    mzhou     08/25/97 - add AS SYSDBA to exp/imp
#    mzhou     08/19/97 - fix budc_del: add no_delete
#    mzhou     07/30/97 - add sql clone alter tablespace online, change rcvcat
#    mzhou     07/24/97 - replace sql alter tablespace offline with plsql code
#    gpongrac  07/23/97 - code review
#    mzhou     07/14/97 - add rename datafile phase in tspitr
#    mzhou     07/01/97 - add tspitr_0, 7, 8, and 9, add host cmds to tspitr
#    mzhou     06/12/97 - fix tspitr_1, tspitr_5 and tspitr_6
#    swerthei  05/02/97 - intercept enqueue failure from backupcontrolfile
#    swerthei  04/29/97 - add channel options
#    tpystyne  04/28/97 - tune offr query
#    mzhou     04/22/97 - add delete archivelog option
#    dalpern   04/16/97 - make fixed packages a distinct "world"
#    gpongrac  04/09/97 - change offr again
#    gpongrac  04/08/97 - fix offr
#    gpongrac  04/08/97 - do outer join for offr cursor
#    gpongrac  04/07/97 - fix msg 8088 for datafilecopy
#    gpongrac  04/03/97 - fix arg to applyOfflineRange
#    gpongrac  04/02/97 - add libmem for applying offline range
#    gpongrac  04/01/97 - fix syntax and typo
#    gpongrac  03/31/97 - update v$database
#    gpongrac  03/31/97 - change to use version_time instead of cf_create_time
#    gpongrac  03/31/97 - add cf_create_time to offr
#    gpongrac  03/31/97 - remove arg from copyControlfile
#    swerthei  04/02/97 - test for snapshot enqueue when creating backup piece
#    swerthei  03/28/97 - clear client_info during channel cleanup
#    tpystyne  03/20/97 - check cftype before resync
#    swerthei  03/20/97 - add setcmdid
#    gpongrac  03/13/97 - add 8502 after 8016
#    gpongrac  02/20/97 - add completion_time to bdf
#    gpongrac  01/31/97 - add async rpc test
#    swerthei  02/11/97 - call clearErrors after inc_scn_matches_df_scn
#    swerthei  02/10/97 - add steps for RESTORE VALIDATE
#    swerthei  01/27/97 - fix vbs_start
#    swerthei  01/24/97 - add libunits for restore-validate conversation
#    gpongrac  01/23/97 - routing to clone
#    gpongrac  01/22/97 - add tspitr libmem for switch datafile to
#    gpongrac  01/14/97 - tspitr work
#    gpongrac  01/06/97 - change krmicd.getLog iface
#    gpongrac  12/20/96 - change resync to ignore 20032 on implicit resyncs
#    gpongrac  12/18/96 - implement tspitr
#    gpongrac  12/05/96 - add controlfile restore script for tspitr
#    ajoshi    01/16/97 -
#    ajoshi    01/12/97 - Add new control file record types
#    swerthei  12/10/96 - add backup piece/set time columns
#    tpystyne  12/13/96 - maintain parent dbinc
#    swerthei  11/13/96 - do change backuppiece delete here
#    tpystyne  11/19/96 - use recover if needed
#    tpystyne  11/13/96 - trap ORA-264 in smr
#    gpongrac  11/12/96 - use mrCheck
#    gpongrac  11/05/96 - use getckpt
#    tpystyne  11/01/96 - move change from recover.bsq to krmk.pc
#    swerthei  11/01/96 - update description of v$datafile
#    gpongrac  10/30/96 - call krmicds.checkUntil
#    tpystyne  10/28/96 - implement change uncatalog
#    swerthei  10/23/96 - propogate df copy/arch log completion time to rcv cat
#    gpongrac  10/21/96 - ignore all errors from "recover cance" in cleanup (15
#    tpystyne  10/21/96 - change JOB to PARTIAL
#    gpongrac  10/14/96 - fix merge errors (error messages)
#    tpystyne  10/01/96 - implement delete input
#    tpystyne  09/24/96 - add bual_del member
#    swerthei  09/27/96 - move validate/inspect from recover.bsq to krmk.pc
#    swerthei  09/27/96 - fix cleanup step
#    swerthei  09/26/96 - add channel debug option
#    swerthei  09/23/96 - handle restore to same destination in restoredfc
#    swerthei  09/19/96 - changed validate return codes
#    swerthei  09/17/96 - don't issue RECOVER CANCEL if no recovery session
#    swerthei  09/18/96 - replicate controlfile must preserve controlfile creat
#    tpystyne  09/06/96 - add online redo log support
#    swerthei  09/06/96 - fix setlimit
#    tpystyne  09/04/96 - fix typo
#    gpongrac  08/31/96 - fix media recovery messages
#    swerthei  08/28/96 - add REPLICATE CONTROLFILE command
#    gpongrac  08/27/96 - fix msg 8507
#    gpongrac  08/23/96 - stored script support
#    gpongrac  08/21/96 - make bucf timeout if enqueue unavailable
#    tpystyne  08/23/96 - implement catalog controlfile
#    gpongrac  08/13/96 - deal with files that don't need a new incremental bac
#    swerthei  08/02/96 - catch 1507 after alter database recover cancel
#    gpongrac  07/29/96 - add log apply step
#    gpongrac  07/22/96 - media recovery work
#    swerthei  07/24/96 - 8038 -> 8038/8040
#    tpystyne  07/24/96 - add incr_level to cdf
#    gpongrac  07/19/96 - add smr step
#    gpongrac  07/15/96 - incremental backup restore
#    tpystyne  07/13/96 - implement catalog command
#    gpongrac  07/10/96 - add sql member
#    gpongrac  07/02/96 - call clearErrors if handling inconsistant_read except
#    gpongrac  06/26/96 - use exceptions in dbms_backup_restore for snapshot pr
#    swerthei  06/25/96 - new inspection algorithm
#    tpystyne  06/24/96 - implement offline range resync
#    tpystyne  06/19/96 - remove unused columns from v$tablespace
#    gpongrac  06/19/96 - bucf_name cannot reference x$kccdi
#    swerthei  06/17/96 - dont resync if target not mounted
#    gpongrac  06/17/96 - have backup current controlfile do makeandusesnapshot
#    tpystyne  06/14/96 - fix if snapshot controlfile name is set test
#    gpongrac  06/14/96 - fix 8506 usage, fix snapshot name valid test
#    gpongrac  06/11/96 - pass set_count to genPieceName
#    gpongrac  06/11/96 - bump version number
#    gpongrac  06/10/96 - fix backup backup controlfile
#    swerthei  06/07/96 - remove duplicate copy of copyalc
#    swerthei  06/07/96 - inspect files when not found
#    gpongrac  06/06/96 - new maskable messages
#    tpystyne  06/04/96 - add create_time to checkBackup/CopyControlFile
#    gpongrac  06/05/96 - have resync set the snapshot cf name to the default i
#    gpongrac  06/05/96 - reorder stuff in cleanup step
#    tpystyne  05/30/96 - implement resetdb
#    gpongrac  05/30/96 - have register db set snapshot cf name
#    tpystyne  05/30/96 - fix column names
#    gpongrac  05/29/96 - copy backup controlfile support
#    gpongrac  05/28/96 - add copy current controlfile
#    gpongrac  05/28/96 - add datafilecopy backup support
#    gpongrac  05/26/96 - change 8015 to 8026
#    swerthei  05/25/96 - different message for copy-restoration
#    gpongrac  05/24/96 - change datafilecopy copy step
#    swerthei  05/24/96 - more logging messages
#    gpongrac  05/23/96 - add logging messages
#    gpongrac  05/22/96 - add version number
#    gpongrac  05/21/96 - backup archivelog support
#    gpongrac  05/21/96 - add bucf_name
#    tpystyne  05/20/96 - add sort_area_too small exception
#    swerthei  05/19/96 - call restorecancel in cleanup, shorten cleanup
#                         code because cancel procedures are nops if not
#                         needed.
#    swerthei  05/19/96 - allocated device type is never null
#    swerthei  05/17/96 - error if all pieces applied but restore not complete
#    swerthei  05/13/96 - fix restore libunits
#    swerthei  05/08/96 - change names of some dbms_backup_restore procedures
#    gpongrac  05/08/96 - call krmicd.cleardevt in cleanup
#    gpongrac  04/30/96 - fix backup skeletons
#    swerthei  04/26/96 - continue work on restore command
#    tpystyne  04/26/96 - retry on ORA-235
#    tpystyne  04/29/96 - use v$tablespace and v$datafile
#    tpystyne  04/26/96 - retry on ORA-235
#    tpystyne  04/24/96 - fix ts$ typo
#    swerthei  04/22/96 - restore command
#    gpongrac  04/17/96 - change x$kqp324 to x$dbms_backup_restore
#    gpongrac  04/15/96 - add table defines
#    tpystyne  04/08/96 - remove dblinks and wrap_br references
#    gpongrac  04/04/96 - add dfnumber libparm to copydf
#    gpongrac  03/29/96 - call dbms_backup_Restore directly
#    gpongrac  03/22/96 - testing
#    tpystyne  03/19/96 - get high recids consistently
#    gpongrac  03/07/96 - implement backup command
#    gpongrac  03/05/96 - add mini-resync capability
#    gpongrac  02/21/96 - add copydfc
#    gpongrac  02/16/96 - add job step for datafile backup set creation
#    gpongrac  02/15/96 - always issue cfileUseCurrent before cfileMakeAndUseSn
#    gpongrac  02/08/96 - add devrel
#    tpystyne  02/08/96 - fix regdb and fullresync
#    tpystyne  01/31/96 - implement full and backup resync
#    gpongrac  02/07/96 - no userid for user-side package calls
#    tpystyne  01/28/96 - update for new fixed views
#    tpystyne  01/19/96 - fix datafile_copy resync
#    gpongrac  01/18/96 - qualify calls to krmicd with SYS
#    gpongrac  01/11/96 - add copy command support
#    tpystyne  01/11/96 - update for new controlfile fixed tables
#    gpongrac  01/10/96 - remove domain from getDbInfo
#    gpongrac  01/02/96 - rename testkrm to fullresync
#
# Note that dbms_rcvman is implemented as a regular package *and* as
# a fixed package.  The fixed package implementation is provided to allow
# the Recovery Manager to function when there is no recovery catalog package.
#
# Recovery manager must tell dbms_rcvman that it is expected to obtain info
# from the controlfile.  We cannot assume that we loaded the
# fixed package implementation of dbms_rcvman because the target database
# may be open.

# NOTE!!!!!!
#
# Whenever a new library member is added or deleted, or an existing member
# has an argument added or deleted, then the version number following
# the "library" keyword must be changed, and a corresponding change
# made to krmk.pc.

library '8.1.6.0'

define 'v$controlfile_record_section'
<<<
define table v$controlfile_record_section
(
TYPE                                    VARCHAR2(17),
RECORD_SIZE                             NUMBER,
RECORDS_TOTAL                           NUMBER,
RECORDS_USED                            NUMBER,
FIRST_INDEX                             NUMBER,
LAST_INDEX                              NUMBER,
LAST_RECID                              NUMBER
);
>>>

define 'v$database'
<<<
define table v$database
(
DBID                                    NUMBER,
NAME                                    VARCHAR2(9),
CREATED                                 DATE,
RESETLOGS_CHANGE#                       NUMBER,
RESETLOGS_TIME                          DATE,
PRIOR_RESETLOGS_CHANGE#                 NUMBER,
PRIOR_RESETLOGS_TIME                    DATE,
LOG_MODE                                VARCHAR2(12),
CHECKPOINT_CHANGE#                      NUMBER,
ARCHIVE_CHANGE#                         NUMBER,
CONTROLFILE_TYPE                        VARCHAR2(7),
CONTROLFILE_CREATED                     DATE,
CONTROLFILE_SEQUENCE#                   NUMBER,
CONTROLFILE_CHANGE#                     NUMBER,
CONTROLFILE_TIME                        DATE,
OPEN_RESETLOGS                          VARCHAR2(11),
VERSION_TIME                            DATE
);
>>>

define 'v$tablespace'
<<<
define table v$tablespace
(
TS#                                     NUMBER,
NAME                                    VARCHAR2(30)
);
>>>

define 'v$datafile'
<<<
define table v$datafile
(
FILE#                                   NUMBER,
CREATION_CHANGE#                        NUMBER,
CREATION_TIME                           DATE,
TS#                                     NUMBER,
RFILE#                                  NUMBER,
STATUS                                  VARCHAR2(7),
ENABLED                                 VARCHAR2(10),
CHECKPOINT_CHANGE#                      NUMBER,
CHECKPOINT_TIME                         DATE,
UNRECOVERABLE_CHANGE#                   NUMBER,
UNRECOVERABLE_TIME                      DATE,
LAST_CHANGE#                            NUMBER,
LAST_TIME                               DATE,
OFFLINE_CHANGE#                         NUMBER,
ONLINE_CHANGE#                          NUMBER,
ONLINE_TIME                             DATE,
BYTES                                   NUMBER,
BLOCKS                                  NUMBER,
CREATE_BYTES                            NUMBER,
BLOCK_SIZE                              NUMBER,
NAME                                    VARCHAR2(513),
PLUGGED_IN                              NUMBER
);
>>>

define 'v$thread'
<<<
define table v$thread
(
THREAD#                                 NUMBER,
STATUS                                  VARCHAR2(6),
ENABLED                                 VARCHAR2(8),
GROUPS                                  NUMBER,
INSTANCE                                VARCHAR2(16),
OPEN_TIME                               DATE,
CURRENT_GROUP#                          NUMBER,
SEQUENCE#                               NUMBER,
CHECKPOINT_CHANGE#                      NUMBER,
CHECKPOINT_TIME                         DATE,
ENABLE_CHANGE#                          NUMBER,
ENABLE_TIME                             DATE,
DISABLE_CHANGE#                         NUMBER,
DISABLE_TIME                            DATE
);
>>>

define 'v$log'
<<<
define table v$log
(
GROUP#                                  NUMBER,
THREAD#                                 NUMBER,
SEQUENCE#                               NUMBER,
BYTES                                   NUMBER,
MEMBERS                                 NUMBER,
ARCHIVED                                VARCHAR2(3),
STATUS                                  VARCHAR2(16),
FIRST_CHANGE#                           NUMBER,
FIRST_TIME                              DATE
);
>>>

define 'v$logfile'
<<<
define table v$logfile
(
GROUP#                                  NUMBER,
STATUS                                  VARCHAR2(7),
MEMBER                                  VARCHAR2(513)
);
>>>

define 'v$log_history'
<<<
define table v$log_history
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
THREAD#                                 NUMBER,
SEQUENCE#                               NUMBER,
FIRST_CHANGE#                           NUMBER,
FIRST_TIME                              DATE,
NEXT_CHANGE#                            NUMBER
);
>>>

define 'v$archived_log'
<<<
define table v$archived_log
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
NAME                                    VARCHAR2(513),
THREAD#                                 NUMBER,
SEQUENCE#                               NUMBER,
RESETLOGS_CHANGE#                       NUMBER,
RESETLOGS_TIME                          DATE,
FIRST_CHANGE#                           NUMBER,
FIRST_TIME                              DATE,
NEXT_CHANGE#                            NUMBER,
NEXT_TIME                               DATE,
BLOCKS                                  NUMBER,
BLOCK_SIZE                              NUMBER,
ARCHIVED                                VARCHAR2(3),
DELETED                                 VARCHAR2(3),
COMPLETION_TIME                         DATE
);
>>>

define 'v$offline_range'
<<<
define table v$offline_range
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
FILE#                                   NUMBER,
OFFLINE_CHANGE#                         NUMBER,
ONLINE_CHANGE#                          NUMBER,
ONLINE_TIME                             DATE
);
>>>

define 'v$backup_set'
<<<
define table v$backup_set
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
SET_STAMP                               NUMBER,
SET_COUNT                               NUMBER,
BACKUP_TYPE                             VARCHAR2(1),
CONTROLFILE_INCLUDED                    VARCHAR2(3),
INCREMENTAL_LEVEL                       NUMBER,
PIECES                                  NUMBER,
START_TIME                              DATE,
COMPLETION_TIME                         DATE,
ELAPSED_SECONDS                         NUMBER,
BLOCK_SIZE                              NUMBER,
STATUS                                  VARCHAR2(1)
);
>>>

define 'v$backup_piece'
<<<
define table v$backup_piece
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
SET_STAMP                               NUMBER,
SET_COUNT                               NUMBER,
PIECE#                                  NUMBER,
COPY#                                   NUMBER,
DEVICE_TYPE                             VARCHAR2(17),
HANDLE                                  VARCHAR2(513),
COMMENTS                                VARCHAR2(81),
MEDIA                                   VARCHAR2(65),
MEDIA_POOL                              NUMBER,
CONCUR                                  VARCHAR2(3),
TAG                                     VARCHAR2(32),
STATUS                                  VARCHAR2(1),
START_TIME                              DATE,
COMPLETION_TIME                         DATE,
ELAPSED_SECONDS                         NUMBER,
DELETED                                 VARCHAR2(3)
);
>>>

define 'v$backup_datafile'
<<<
define table v$backup_datafile
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
SET_STAMP                               NUMBER,
SET_COUNT                               NUMBER,
FILE#                                   NUMBER,
CREATION_CHANGE#                        NUMBER,
CREATION_TIME                           DATE,
RESETLOGS_CHANGE#                       NUMBER,
RESETLOGS_TIME                          DATE,
INCREMENTAL_LEVEL                       NUMBER,
INCREMENTAL_CHANGE#                     NUMBER,
CHECKPOINT_CHANGE#                      NUMBER,
CHECKPOINT_TIME                         DATE,
ABSOLUTE_FUZZY_CHANGE#                  NUMBER,
MARKED_CORRUPT                          NUMBER,
MEDIA_CORRUPT                           NUMBER,
LOGICALLY_CORRUPT                       NUMBER,
DATAFILE_BLOCKS                         NUMBER,
BLOCKS                                  NUMBER,
BLOCK_SIZE                              NUMBER,
OLDEST_OFFLINE_RANGE                    NUMBER,
COMPLETION_TIME                         DATE
);
>>>

define 'v$backup_corruption'
<<<
define table v$backup_corruption
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
SET_STAMP                               NUMBER,
SET_COUNT                               NUMBER,
PIECE#                                  NUMBER,
FILE#                                   NUMBER,
BLOCK#                                  NUMBER,
BLOCKS                                  NUMBER,
CORRUPTION_CHANGE#                      NUMBER,
MARKED_CORRUPT                          VARCHAR2(3)
);
>>>

define 'v$backup_redolog'
<<<
define table v$backup_redolog
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
SET_STAMP                               NUMBER,
SET_COUNT                               NUMBER,
THREAD#                                 NUMBER,
SEQUENCE#                               NUMBER,
RESETLOGS_CHANGE#                       NUMBER,
RESETLOGS_TIME                          DATE,
FIRST_CHANGE#                           NUMBER,
FIRST_TIME                              DATE,
NEXT_CHANGE#                            NUMBER,
NEXT_TIME                               DATE,
BLOCKS                                  NUMBER,
BLOCK_SIZE                              NUMBER
);
>>>

define 'v$datafile_copy'
<<<
define table v$datafile_copy
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
NAME                                    VARCHAR2(513),
TAG                                     VARCHAR2(32),
FILE#                                   NUMBER,
RFILE#                                  NUMBER,
CREATION_CHANGE#                        NUMBER,
CREATION_TIME                           DATE,
RESETLOGS_CHANGE#                       NUMBER,
RESETLOGS_TIME                          DATE,
INCREMENTAL_LEVEL                       NUMBER,
CHECKPOINT_CHANGE#                      NUMBER,
CHECKPOINT_TIME                         DATE,
ABSOLUTE_FUZZY_CHANGE#                  NUMBER,
RECOVERY_FUZZY_CHANGE#                  NUMBER,
RECOVERY_FUZZY_TIME                     DATE,
ONLINE_FUZZY                            VARCHAR2(3),
BACKUP_FUZZY                            VARCHAR2(3),
MARKED_CORRUPT                          NUMBER,
MEDIA_CORRUPT                           NUMBER,
LOGICALLY_CORRUPT                       NUMBER,
BLOCKS                                  NUMBER,
BLOCK_SIZE                              NUMBER,
OLDEST_OFFLINE_RANGE                    NUMBER,
DELETED                                 VARCHAR2(3),
COMPLETION_TIME                         DATE
);
>>>

define 'v$copy_corruption'
<<<
define table v$copy_corruption
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
COPY_RECID                              NUMBER,
COPY_STAMP                              NUMBER,
FILE#                                   NUMBER,
BLOCK#                                  NUMBER,
BLOCKS                                  NUMBER,
CORRUPTION_CHANGE#                      NUMBER,
MARKED_CORRUPT                          VARCHAR2(3)
);
>>>

define 'v$deleted_object'
<<<
define table v$deleted_object
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
TYPE                                    VARCHAR2(30),
OBJECT_RECID                            NUMBER,
OBJECT_STAMP                            NUMBER
);
>>>

define 'x$kccdi'
<<<
define table x$kccdi
(
ADDR                                    RAW(4),
INDX                                    NUMBER,
INST_ID                                 NUMBER,
DIDFV                                   NUMBER,
DIDFC                                   NUMBER,
DICTS                                   VARCHAR2(20),
DIDBN                                   VARCHAR2(9),
DIRDB                                   NUMBER,
DICCT                                   VARCHAR2(20),
DIFLG                                   NUMBER,
DIIRS                                   VARCHAR2(16),
DIRLS                                   VARCHAR2(16),
DIRLC                                   VARCHAR2(20),
DIRLC_I                                 NUMBER,
DIPRS                                   VARCHAR2(16),
DIPRC                                   VARCHAR2(20),
DIPRC_I                                 NUMBER,
DIRDV                                   NUMBER,
DIRDC                                   NUMBER,
DINDF                                   NUMBER,
DINOF                                   NUMBER,
DICPT                                   NUMBER,
DISCN                                   VARCHAR2(16),
DINET                                   NUMBER,
DINOT                                   NUMBER,
DIOTH                                   NUMBER,
DIOTT                                   NUMBER,
DIETB                                   RAW(8),
DIMLM                                   NUMBER,
DIMDM                                   NUMBER,
DIARH                                   NUMBER,
DIART                                   NUMBER,
DIFAS                                   VARCHAR2(16),
DICKP_SCN                               VARCHAR2(16),
DICKP_TIM                               VARCHAR2(20),
DICSQ                                   NUMBER,
DIDBI                                   NUMBER,
DISSC_SCN                               VARCHAR2(16),
DISSC_TIM                               VARCHAR2(20),
DISFP                                   NUMBER,
DIBSC                                   NUMBER,
DIPOFB                                  NUMBER,
DIPNFB                                  NUMBER,
DICOFB                                  NUMBER,
DICNFB                                  NUMBER
);
>>>

define 'x$kccfe'
<<<
define table x$kccfe
(
ADDR                                    RAW(4),
INDX                                    NUMBER,
INST_ID                                 NUMBER,
FENUM                                   NUMBER,
FECSZ                                   NUMBER,
FEBSZ                                   NUMBER,
FESTA                                   NUMBER,
FECRC_SCN                               VARCHAR2(16),
FECRC_TIM                               VARCHAR2(20),
FECRC_THR                               NUMBER,
FECRC_RBA_SEQ                           NUMBER,
FECRC_RBA_BNO                           NUMBER,
FECRC_RBA_BOF                           NUMBER,

FECRC_ETB                               RAW(8),
FECPS                                   VARCHAR2(16),
FECPT                                   VARCHAR2(20),
FECPC                                   NUMBER,
FESTS                                   VARCHAR2(16),
FESTT                                   VARCHAR2(20),
FEBSC                                   VARCHAR2(16),
FEFNH                                   NUMBER,
FEFNT                                   NUMBER,
FEDUP                                   NUMBER,
FEURS                                   VARCHAR2(16),
FEURT                                   VARCHAR2(20),
FEOFS                                   VARCHAR2(16),
FEONC_SCN                               VARCHAR2(16),
FEONC_TIM                               VARCHAR2(20),
FEONC_THR                               NUMBER,
FEONC_RBA_SEQ                           NUMBER,
FEONC_RBA_BNO                           NUMBER,
FEONC_RBA_BOF                           NUMBER,
FEONC_ETB                               RAW(8),
FEPOR                                   NUMBER,
FETSN                                   NUMBER,
FETSI                                   NUMBER,
FERFN                                   NUMBER,
FEPFT                                   NUMBER,
FEDOR                                   NUMBER,
FEPDI                                   NUMBER,
FEFDB                                   NUMBER,
FEPLG_SCN                               VARCHAR2(16)
);
>>>

define 'x$kccfn'
<<<
define table x$kccfn
(
ADDR                                    RAW(4),
INDX                                    NUMBER,
INST_ID                                 NUMBER,
FNNUM                                   NUMBER,
FNTYP                                   NUMBER,
FNFNO                                   NUMBER,
FNFWD                                   NUMBER,
FNBWD                                   NUMBER,
FNFLG                                   NUMBER,
FNNAM                                   VARCHAR2(513),
FNONM                                   VARCHAR2(513)
);
>>>

define 'v$mystat'
<<<
define table v$mystat
(
SID                                     NUMBER,
STATISTIC#                              NUMBER,
VALUE                                   NUMBER
);
>>>

define 'v$instance'
<<<
define table v$instance
(
INSTANCE_NUMBER                         NUMBER,
INSTANCE_NAME                           VARCHAR2(16),
HOST_NAME                               VARCHAR2(64),
VERSION                                 VARCHAR2(17),
STARTUP_TIME                            DATE,
STATUS                                  VARCHAR2(7),
PARALLEL                                VARCHAR2(3),
THREAD#                                 NUMBER,
ARCHIVER                                VARCHAR2(7),
LOG_SWITCH_WAIT                         VARCHAR2(11),
LOGINS                                  VARCHAR2(10),
SHUTDOWN_PENDING                        VARCHAR2(3)
);
>>>

define 'v$parameter'
<<<
define table v$parameter
(
NUM                                     NUMBER,
NAME                                    VARCHAR2(64),
TYPE                                    NUMBER,
VALUE                                   VARCHAR2(512),
ISDEFAULT                               VARCHAR2(9),
ISSES_MODIFIABLE                        VARCHAR2(5),
ISSYS_MODIFIABLE                        VARCHAR2(9),
ISMODIFIED                              VARCHAR2(10),
ISADJUSTED                              VARCHAR2(5),
DESCRIPTION                             VARCHAR2(64)
);
>>>

define 'x$kcrmx'
<<<
define table x$kcrmx
(
ADDR                                    RAW(4),
INDX                                    NUMBER,
INST_ID                                 NUMBER,
BCF                                     NUMBER,
CBR                                     NUMBER,
TBR                                     NUMBER,
SBR                                     NUMBER,
IRS                                     VARCHAR2(16),
INS                                     VARCHAR2(16),
STM                                     VARCHAR2(20),
CKPSCN                                  VARCHAR2(16),
CKPTIM                                  VARCHAR2(20),
CKPETB                                  RAW(8),
EOK                                     NUMBER,
NAM                                     VARCHAR2(513),
THR                                     NUMBER,
SCN                                     VARCHAR2(16),
TIM                                     VARCHAR2(20),
SEQ                                     NUMBER,
LOS                                     VARCHAR2(16),
FAM                                     NUMBER,
UIS                                     NUMBER,
ORT                                     VARCHAR2(20),
NRT                                     VARCHAR2(20),
FLG                                     NUMBER,
MRS                                     NUMBER,
NTX                                     NUMBER,
CTC                                     NUMBER
);
>>>

# the member name must begin with x$ in order to load it automatically
define 'x$undo$'
<<<
define table undo$
(
US#                                     NUMBER,
NAME                                    VARCHAR2(30),
USER#                                   NUMBER,
FILE#                                   NUMBER,
BLOCK#                                  NUMBER,
SCNBAS                                  NUMBER,
SCNWRP                                  NUMBER,
XACTSQN                                 NUMBER,
UNDOSQN                                 NUMBER,
INST#                                   NUMBER,
STATUS$                                 NUMBER,
TS#                                     NUMBER,
UGRP#                                   NUMBER,
KEEP                                    NUMBER,
OPTIMAL                                 NUMBER,
FLAGS                                   NUMBER,
SPARE1                                  NUMBER,
SPARE2                                  NUMBER,
SPARE3                                  NUMBER,
SPARE4                                  VARCHAR2(1000),
SPARE5                                  VARCHAR2(1000),
SPARE6                                  DATE
);
>>>

define 'x$name_log'
<<<
procedure name_log(memnum     IN number
                  ,arch_recid IN number
                  ,arch_stamp IN number
                  ,thread     IN number
                  ,sequence   IN number
                  ,fname      IN varchar2
                  ,files      IN OUT binary_integer
                  ,first_time IN OUT boolean) IS
begin
  if files < memnum then
    sys.dbms_backup_restore.backupArchivedLog(arch_recid => arch_recid,
                                              arch_stamp => arch_stamp);
    files := files + 1;
    if first_time then
       krmicd.writeMsg(8014, krmicd.getChid);
       first_time := FALSE;
    end if;
    krmicd.writeMsg(8504, to_char(thread), to_char(sequence),
                    to_char(arch_recid), to_char(arch_stamp));
  end if;
end;
>>>

define 'x$stamp2date'
<<<
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;
>>>

# refer to kcmbts in kcm.h to see where this code came from
# this function is the inverse of the stamp2date function.
define 'x$date2stamp'
<<<
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;
>>>

define 'x$dur2time'
<<<
procedure dur2time(dur   IN OUT number,
                   hours IN OUT number,
                   mins  IN OUT number,
                   secs  IN OUT number) is
begin
   hours := floor(dur*24);
   dur := dur - hours/24;

   mins := floor(dur*24*60);
   dur := dur - mins/(24*60);

   secs := dur*24*60*60;
end;
>>>
  
define 'x$kcvfh'
<<<
DEFINE TABLE x$kcvfh
(
ADDR                                    RAW(4),
INDX                                    NUMBER,
INST_ID                                 NUMBER,
HXFIL                                   NUMBER,
HXONS                                   NUMBER,
HXSTS                                   VARCHAR2(16),
HXERR                                   NUMBER,
HXVER                                   NUMBER,
FHSWV                                   NUMBER,
FHCVN                                   NUMBER,
FHDBI                                   NUMBER,
FHDBN                                   VARCHAR2(9),
FHCSQ                                   NUMBER,
FHFSZ                                   NUMBER,
FHBSZ                                   NUMBER,
FHFNO                                   NUMBER,
FHTYP                                   NUMBER,
FHRDB                                   NUMBER,
FHCRS                                   VARCHAR2(16),
FHCRT                                   VARCHAR2(20),
FHRLC                                   VARCHAR2(20),
FHRLC_I                                 NUMBER,
FHRLS                                   VARCHAR2(16),
FHPRC                                   VARCHAR2(20),
FHPRC_I                                 NUMBER,
FHPRS                                   VARCHAR2(16),
FHBTI                                   VARCHAR2(20),
FHBSC                                   VARCHAR2(16),
FHBTH                                   NUMBER,
FHSTA                                   NUMBER,
FHSCN                                   VARCHAR2(16),
FHTIM                                   VARCHAR2(20),
FHTHR                                   NUMBER,
FHRBA_SEQ                               NUMBER,
FHRBA_BNO                               NUMBER,
FHRBA_BOF                               NUMBER,
FHETB                                   RAW(8),
FHCPC                                   NUMBER,
FHRTS                                   VARCHAR2(20),
FHCCC                                   NUMBER,
FHBCP_SCN                               VARCHAR2(16),
FHBCP_TIM                               VARCHAR2(20),
FHBCP_THR                               NUMBER,
FHBCP_RBA_SEQ                           NUMBER,
FHBCP_RBA_BNO                           NUMBER,
FHBCP_RBA_BOF                           NUMBER,
FHBCP_ETB                               RAW(8),
FHBHZ                                   NUMBER,
FHXCD                                   RAW(16),
FHTSN                                   NUMBER,
FHTNM                                   VARCHAR2(30),
FHRFN                                   NUMBER,
FHAFS                                   VARCHAR2(16),
FHRFS                                   VARCHAR2(16),
FHRFT                                   VARCHAR2(20),
HXIFZ                                   NUMBER,
HXNRCV                                  NUMBER,
HXFNM                                   VARCHAR2(513),
FHPOFB                                  NUMBER,
FHPNFB                                  NUMBER
);
>>>
  
define 'x$dual'
<<<
DEFINE TABLE x$dual
(
ADDR                                    RAW(4),
INDX                                    NUMBER,
INST_ID                                 NUMBER,
DUMMY                                   VARCHAR2(1)
);
>>>

define 'v$proxy_datafile'
<<<
DEFINE TABLE v$proxy_datafile
(
RECID                                   NUMBER,
STAMP                                   NUMBER,
DEVICE_TYPE                             VARCHAR2(17),
HANDLE                                  VARCHAR2(513),
COMMENTS                                VARCHAR2(81),
MEDIA                                   VARCHAR2(65),
MEDIA_POOL                              NUMBER,
TAG                                     VARCHAR2(32),
STATUS                                  VARCHAR2(1),
DELETED                                 VARCHAR2(3),
FILE#                                   NUMBER,
CREATION_CHANGE#                        NUMBER,
CREATION_TIME                           DATE,
RESETLOGS_CHANGE#                       NUMBER,
RESETLOGS_TIME                          DATE,
CHECKPOINT_CHANGE#                      NUMBER,
CHECKPOINT_TIME                         DATE,
ABSOLUTE_FUZZY_CHANGE#                  NUMBER,
RECOVERY_FUZZY_CHANGE#                  NUMBER,
RECOVERY_FUZZY_TIME                     DATE,
INCREMENTAL_LEVEL                       NUMBER,
ONLINE_FUZZY                            VARCHAR2(3),
BACKUP_FUZZY                            VARCHAR2(3),
BLOCKS                                  NUMBER,
BLOCK_SIZE                              NUMBER,
OLDEST_OFFLINE_RANGE                    NUMBER,
START_TIME                              DATE,
COMPLETION_TIME                         DATE,
ELAPSED_SECONDS                         NUMBER
);
>>>

#
# regdb:  register a new database
#
define regdb 
<<<
-- regdb
declare
  db_name     varchar2(8);
  reset_scn   number;
  reset_time  date;
  db_id       number;
  cf_type     binary_integer;
  cf_sequence binary_integer;
begin
    select dbid, name, resetlogs_change#, resetlogs_time
    into   db_id, db_name, reset_scn, reset_time
    from v$database;
 
    dbms_rcvcat.registerDatabase
      (db_id, db_name, reset_scn, reset_time);
    krmicd.writeMsg(8006, db_name);
end;
>>>

#
# resetdb:  register a new database incarnation
#
define resetdb 
<<<
-- resetdb
declare
  db_name     varchar2(8);
  reset_scn   number;
  reset_time  date;
  parent_reset_scn   number;
  parent_reset_time  date;
  db_id       number;
  cf_type     binary_integer;
  cf_sequence binary_integer;
  dbinc_key   number;
begin
  &object&
  if (dbinc_key is NULL) then
    select dbid, name, resetlogs_change#, resetlogs_time
    into   db_id, db_name, reset_scn, reset_time
    from v$database;

    select diprs, to_date(diprc, 'MM/DD/RR HH24:MI:SS')
    into parent_reset_scn, parent_reset_time
    from x$kccdi;
 
    dbms_rcvcat.resetDatabase(db_id, db_name, reset_scn, reset_time,
                              parent_reset_scn, parent_reset_time);
    krmicd.writeMsg(8006, db_name);
  else
    select upper(value) into db_name
    from v$parameter 
    where name = 'db_name';
    dbms_rcvcat.resetDatabase(dbinc_key, db_name);
    krmicd.writeMsg(8066, dbinc_key);
  end if;
end;
>>>

#
# resync: resync recovery catalog from snapshot controlfile or
#         current controlfile.  If snapshot, this
#         will update information about tablespaces, datafiles, and
#         rollback segments.  Else just the circular record types are
#         accessed.
#
define 'resync'
<<<
-- resync
declare

  mount_status varchar2(7);
  cf_type      varchar2(7);

  db_id       number;
  db_name     varchar2(8);
  reset_scn   number;
  reset_time  date;

  snapcf      varchar2(512);            -- snapshot controlfile name
  name        varchar2(512);            -- default snapshot cf name
  cfname      varchar2(512) := NULL;    -- backup cf name

  ckp_scn     number;
  ckp_time    date;
  ckp_cf_seq  number;
  cf_create_time date;
  getckptscn  number;
  cf_version  date;                     -- v$database.version_time
  kccdivts    number;			-- date2stamp(cf_version)

  recid number;
  high_cp_recid number; -- 1
  high_rt_recid number; -- 2
  high_le_recid number; -- 3
  high_fe_recid number; -- 4
  high_fn_recid number; -- 5
  high_ts_recid number; -- 6
  high_r1_recid number; -- 7
  high_r2_recid number; -- 8
  high_lh_recid number; -- 9
  high_or_recid number; -- 10
  high_al_recid number; -- 11
  high_bs_recid number; -- 12
  high_bp_recid number; -- 13 
  high_bf_recid number; -- 14
  high_bl_recid number; -- 15
  high_dc_recid number; -- 16
  high_fc_recid number; -- 17
  high_cc_recid number; -- 18
  high_dl_recid number; -- 19
  high_pc_recid number; -- 20
  high_r4_recid number; -- 21

  full_resync boolean;    -- set by rman compiler
  implicit    boolean;    -- set by rman compiler

  read_retries number := 0;             -- retry counter for inconsistant_read
  busy_retries number := 0;             -- retry counter for ss enqueue busy
  sort_retries number := 0;             -- retry counter for sort_area_size

  rbs_count    number := NULL;
  b            boolean;
  read_only    number;

  sort_area_size   number;      -- sort_area_size from v$parameter
  rec_size         number;      -- record_size from v$controlfile_record_section
  total_recs       number;
  rec_per_chunk    integer;
  high             number;
  low              number;
  found            number;

  sort_area_too_small EXCEPTION;
  PRAGMA EXCEPTION_INIT(sort_area_too_small, -1220);

  resync_not_needed exception;
  pragma exception_init(resync_not_needed, -20034);

  -- Each time a backup controlfile is mounted, the version_time is updated.
  -- We rely on this to decide when to ignore the high water marks.  As of
  -- 8.1.6, we also rely on the version_time to filter out old circular
  -- records.  Old ones are the ones that existed when the controlfile
  -- was made into a backup.  These may be stale records if the user has
  -- since deleted them from the recovery catalog.  We do not want to process
  -- such records because they might pollute the recover catalog.  If the
  -- backup controlfile was created by RMAN, then we already resync those
  -- records anyway.
  --

  cursor rs is
    select type, last_recid from v$controlfile_record_section;

  cursor ts is
    select ts.ts#, ts.name, min(df.creation_change#) create_scn,
           min(df.creation_time) create_time
    from v$tablespace ts, v$datafile df
    where ts.ts# = df.ts#
    group by ts.ts#, ts.name having max(df.plugged_in) = 0
    order by ts.ts#;

  -- When a current controlfile is made into a backup:
  --   all stop scns are cleared
  --   all SOR bits are cleared, and the WCC bit is set if the SOR bit was on.
  --
  -- This query is only used during full resyncs, which are only taken from
  -- backup controlfiles.  That is why
  -- we use the checkpoint scn (fecps) for the stop scn rather than the 
  -- stop scn (fests), and check the WCC bit rather than the SOR bit.
  --
  -- Also note that we don't use the absence of the CGE bit to determine if
  -- a file is read-only, because we always check the WCC bit first, which
  -- will never be set concurrently with the CGE bit.
  --
  -- If fefdb <> 0, then this is a file that has been 
  -- plugged in but not yet converted (still has foreign dbid and reset scn).
  -- Such files are ignored by RMAN.
  cursor df(low_fno number, high_fno number) IS
    SELECT fenum fileno,
      to_number(fecrc_scn) create_scn,
      to_date(fecrc_tim,'MM/DD/RR HH24:MI:SS') create_time,
      fetsn tsnum,
      fnnam fname,
      fhfsz fsize,
      febsz block_size,
      to_number(feofs) offline_scn,
      to_number(feonc_scn) online_scn,
      to_date(feonc_tim,'MM/DD/RR HH24:MI:SS') online_time,
      to_number(fecps) stop_scn,
      to_date(festt, 'MM/DD/RR HH24:MI:SS') stop_time,
      to_number(bitand(festa, 4096)) clean_flag, -- This is the KCCFEWCC bit
      to_number(bitand(festa, 4)) read_enabled_flag,
      to_number(bitand(festa, 64)) missing_file_flag, -- this is KCCFECKD
      fefdb
      FROM x$kccfe, x$kccfn, x$kcvfh
      WHERE fnfno=fenum
      AND   fnfno=hxfil
      AND   fefnh=fnnum
      AND   fedup<>0
      AND   fntyp=4
      AND   fnnam IS NOT NULL
      AND   fenum between low_fno and high_fno
      ORDER BY fenum;
    
  cursor rt is
    select thread#, sequence# last_sequence#, enable_change# enable_scn,
           enable_time, disable_change# disable_scn, disable_time,
           decode(enabled, 'DISABLED', 'D', 'E') status
    from v$thread
    order by thread#;

  cursor orl is
    select l.thread#, lf.group#, lf.member fname
    from v$log l, v$logfile lf
    where l.group# = lf.group#
    order by member;

  cursor rlh(low_recid number, high_recid number) is
    select recid, stamp, thread#, sequence#, first_change# low_scn, 
           first_time low_time, next_change# next_scn
    from v$log_history
    where recid between low_recid and high_recid
    order by recid;

  -- NB:  The circular record type cursors below filter out records
  --      whose stamp is < kccdivts.  dbms_rcvcat ignored such records, so
  --      there is no point in passing them to dbms_rcvcat.  Refer to
  --      prvtrvct.sql for the reason why these are ignored.  However, we
  --      always pass the record at the high_recid slot so that dbms_rcvcat
  --      can set the high water mark.

  cursor al(low_recid number, high_recid number) is
    select recid, stamp, name, thread#, sequence#, resetlogs_change#,
           resetlogs_time, first_change#, first_time, 
           next_change#, next_time, blocks, block_size,
           decode(archived, 'YES', 'Y', 'NO', 'N', 'UNKNOWN') archived,
           decode(deleted, 'YES', 'D', 'NO', 'A', 'UNKNOWN') status,
           completion_time
    from v$archived_log
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  -- This cursor does a outer join of v$offline_range to x$kccfe.
  -- The reason is that x$kccfe does not show datafiles that have been dropped
  -- and whose file# reused. It is possible that a offline range record belongs
  -- to a dropped datafile.  In this case, we cannot know the creation SCN
  -- of the datafile that owns the offline range.  So we make it zero and
  -- insert it into the recovery catalog that way.  Such ranges are never
  -- used.  It is also possible that an offline range belongs to a
  -- dropped datafile, and for that datafile number to have been reused.
  -- To avoid associating the offline range with the wrong datafile incarnation
  -- we require that the datafile creation scn be < the offline range end.
  -- We use the offline range end rather than the start because of issues
  -- relating to V7->V8 migration of offline tablespaces, and because of
  -- TSPITR of read-only tablespaces.  Under these circumstances, the datafile
  -- creation SCN may be > than the offline range start.
  cursor offr(low_recid number, high_recid number) is
    select /*+ first_rows */ offr.recid, offr.stamp, offr.file#,
           nvl(to_number(fe.fecrc_scn), 0) creation_change#,
           offr.offline_change#, offr.online_change#, offr.online_time
    from v$offline_range offr, x$kccfe fe
    where offr.file# = fe.fenum(+)
    and   offr.online_change# > to_number(fe.fecrc_scn(+))
    and   offr.recid between low_recid and high_recid
    and   (offr.stamp >= kccdivts OR recid = high_recid)
    order by offr.recid;

  cursor bs(low_recid number, high_recid number) is
    select recid, stamp, set_stamp, set_count, backup_type, incremental_level,
           pieces, start_time, completion_time
    from v$backup_set
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;
   
  cursor bp(low_recid number, high_recid number) is
    select recid, stamp, set_stamp, set_count, piece#, copy#, tag,
           device_type, handle,
           comments, media, media_pool, concur,
           start_time, completion_time, status
    from v$backup_piece
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor bdf(low_recid number, high_recid number) is
    select recid, stamp, set_stamp, set_count, file#, 
           creation_change#, creation_time,
           resetlogs_change#, resetlogs_time, incremental_level, 
           incremental_change#, checkpoint_change#, checkpoint_time,
           absolute_fuzzy_change#, datafile_blocks, blocks, block_size,
           oldest_offline_range, completion_time
    from v$backup_datafile
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor bcb(low_recid number, high_recid number) is
    select recid, stamp, set_stamp, set_count, piece#, file#, block#, blocks,
           corruption_change#, marked_corrupt
    from v$backup_corruption
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor brl(low_recid number, high_recid number) is
    select recid, stamp, set_stamp, set_count, thread#, sequence#, 
           resetlogs_change#, resetlogs_time, first_change#, first_time, 
           next_change#, next_time, blocks, block_size
    from v$backup_redolog
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor cdf(low_recid number, high_recid number) is
    select recid, stamp, name fname, tag, file#, creation_change# create_scn,
           creation_time create_time, resetlogs_change# reset_scn,
           resetlogs_time reset_time, incremental_level incr_level,
           checkpoint_change# ckp_scn,
           checkpoint_time ckp_time, absolute_fuzzy_change# abs_fuzzy_scn,
           online_fuzzy, 
           backup_fuzzy,
           recovery_fuzzy_change# rcv_fuzzy_scn, 
           recovery_fuzzy_time rcv_fuzzy_time,
           blocks, block_size, oldest_offline_range,
           decode(deleted, 'YES', 'D', 'NO', 'A', 'UNKNOWN') status,
           completion_time
    from v$datafile_copy
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor xdf(low_recid number, high_recid number) is
    select recid, stamp, tag, file#, creation_change# create_scn,
           creation_time create_time, resetlogs_change# reset_scn,
           resetlogs_time reset_time, incremental_level incr_level,
           checkpoint_change# ckp_scn,
           checkpoint_time ckp_time, absolute_fuzzy_change# abs_fuzzy_scn,
           online_fuzzy, 
           backup_fuzzy,
           recovery_fuzzy_change# rcv_fuzzy_scn, 
           recovery_fuzzy_time rcv_fuzzy_time,
           blocks, block_size, oldest_offline_range,
           device_type, handle, comments, media, media_pool,
           status, start_time, completion_time
    from v$proxy_datafile
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor ccb(low_recid number, high_recid number) is
    select recid, stamp, copy_recid, copy_stamp, file#, block#, blocks,
           corruption_change#, marked_corrupt
    from v$copy_corruption
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

  cursor dl(low_recid number, high_recid number) is
    select recid, stamp, type object_type, object_recid, object_stamp
    from v$deleted_object
    where recid between low_recid and high_recid
      and (stamp >= kccdivts OR recid = high_recid)
    order by recid;

begin

  &resync_flag&
 
  if cfname is null then
    select status into mount_status from v$instance;
    if mount_status not in ('MOUNTED', 'OPEN') then
      if full_resync then
        krmicd.writeMsg(8034);
      else
        krmicd.writeMsg(8035);
      end if;
      return;
    end if;
  else
    mount_status := 'BACKUP';                   -- resync from backup cf
  end if;

  <<snapshot>>  -- retry on makeAndUseSnapshot failure
 
  begin

    -- use a snapshot controlfile for full resync unless a backup controlfile
    -- is specified

    if (full_resync) then  
      if (cfname is null) then
        -- see if the snapshot controlfile name is set
        select /*+ first_rows */ fnnam into snapcf 
        from x$kccdi di, x$kccfn fn
        where di.disfp = fn.fnnum                  
        and   bitand(di.diflg,8192) = 8192      -- snapshot name is valid
        and   fn.fntyp = 0;                     -- filename record type is 0
  
        -- make a snapshot of the current controlfile
        sys.dbms_backup_restore.cfileMakeAndUseSnapshot;
      else
        -- use the backup controlfile
        sys.dbms_backup_restore.cfileUseCopy(cfname);
      end if;
    end if;

  exception

    when NO_DATA_FOUND then             -- snapshot controlfilename not set
      name := sys.dbms_backup_restore.getParm(
          sys.dbms_backup_restore.SNAPSHOT_NAME);
      sys.dbms_backup_restore.cfileSetSnapshotName(name);
      krmicd.writeMsg(8029, name);
      goto snapshot;    

    when sys.dbms_backup_restore.snapshot_enqueue_busy then
      -- retry up to 5 times, waiting 10 seconds between retries
      if busy_retries = 5 then
        krmicd.writeMsg(20029, 'cannot make a snapshot controlfile');
        raise;
      end if;

      busy_retries := busy_retries + 1;
      krmicd.writeMsg(8512);
      krmicd.sleep(10*busy_retries);
      krmicd.clearErrors;
      goto snapshot;    

  end;  -- snapshot controlfile stuff

  -- Here, the controlfile X$ tables are pointing at either
  -- the snapshot controlfile, the current controlfile, or
  -- a backup controlfile.

  select controlfile_type into cf_type from v$database;
  if cf_type not in ('CURRENT','BACKUP') then
    if full_resync then
      krmicd.writeMsg(8040);
    else
      krmicd.writeMsg(8041);
    end if;
    return;
  end if;

  <<retry>>  -- retry on inconsistent read

begin
  
  select value into sort_area_size
  from v$parameter
  where name='sort_area_size';

  select dbid, name, resetlogs_change#, resetlogs_time, controlfile_created,
         controlfile_sequence#, controlfile_change#, controlfile_time,
         version_time
  into   db_id, db_name, reset_scn, reset_time, cf_create_time, ckp_cf_seq,
         ckp_scn, ckp_time, cf_version
  from v$database;

  kccdivts := date2stamp(cf_version);	-- used by circular record queries
 
  sys.dbms_backup_restore.getCkpt(getckptscn
                                 ,high_cp_recid
                                 ,high_rt_recid
                                 ,high_le_recid
                                 ,high_fe_recid
                                 ,high_fn_recid
                                 ,high_ts_recid
                                 ,high_r1_recid
                                 ,high_r2_recid
                                 ,high_lh_recid
                                 ,high_or_recid
                                 ,high_al_recid
                                 ,high_bs_recid
                                 ,high_bp_recid
                                 ,high_bf_recid
                                 ,high_bl_recid
                                 ,high_dc_recid
                                 ,high_fc_recid
                                 ,high_cc_recid
                                 ,high_dl_recid
                                 ,high_pc_recid
                                 ,high_r4_recid
                                 );

  if (not full_resync) then
    ckp_scn := getckptscn;
    ckp_time := NULL;
  end if;  

  dbms_rcvcat.setDatabase
    (db_name, reset_scn, reset_time, db_id);

  begin
    if (full_resync) then 
      dbms_rcvcat.beginCkpt
        (ckp_scn, ckp_cf_seq, cf_version, ckp_time, 'FULL', mount_status,
         high_fe_recid);
    else
      dbms_rcvcat.beginCkpt
        (ckp_scn, ckp_cf_seq, cf_version, ckp_time, 'PARTIAL',
         mount_status, high_fe_recid);
    end if;
  exception
    when resync_not_needed then
      if implicit then
        krmicd.clearErrors;
        return;
      else
        raise;
      end if;
    when others then
      raise;
  end;
  
  if (full_resync) then
    krmicd.writeMsg(8002);      -- full resync
  else
    krmicd.writeMsg(8003);      -- partial resync
  end if;

  if (full_resync) then

    -- because we cannot detect if rollback segments have been created or
    -- dropped since the previous full open database resync, we always
    -- resync tablespace information when the database is open.
    if (mount_status = 'OPEN') then
      high_ts_recid := NULL;                 -- force tablespace resync
    end if;
    if dbms_rcvcat.beginTableSpaceResync(high_ts_recid) then
      for tsrec in ts loop
        if (mount_status = 'OPEN') then
          select count(us#) into rbs_count
          from undo$ u
          where u.ts# = tsrec.ts# and u.status$ != 1;
        end if;
        dbms_rcvcat.checkTableSpace
          (tsrec.name, tsrec.ts#, tsrec.create_scn, tsrec.create_time, 
           rbs_count);
      end loop;
 
      dbms_rcvcat.endTableSpaceResync;
    end if;
 
    IF dbms_rcvcat.beginDataFileResync(high_fe_recid) THEN
      rec_size := 758 + 75;
      rec_per_chunk := sort_area_size / rec_size;

      -- total number of records
      select dindf into total_recs
      from x$kccdi;

      found := 0;
      low := 0;
      loop
        high := low + rec_per_chunk - 1;
        for dfrec in df(low, high) LOOP
          IF (dfrec.fefdb = 0) THEN
            IF dfrec.clean_flag <> 0 THEN -- the clean bit is on
               IF dfrec.read_enabled_flag <> 0 THEN
                  read_only := 1;
               ELSE
                  read_only := 0;         -- offline clean
               END IF;
            ELSE -- the file is not clean
               dfrec.stop_scn := NULL;
               dfrec.stop_time := NULL;
               read_only := 0;
            END IF;

            IF dfrec.fsize = 0 THEN
               -- We don't have the filesize, so pass NULL so that we don't
               -- update the value in the catalog.
               dfrec.fsize := NULL;
            END IF;

            IF dfrec.missing_file_flag <> 0 THEN
               -- The file is missing, so the controlfile does not
               -- have the real filename.  Pass NULL to checkDatafile so
               -- that it does not change the filename in the catalog.
               dfrec.fname := NULL;
            END IF;

            dbms_rcvcat.checkDataFile(
              dfrec.fileno, dfrec.fname, dfrec.create_scn,
              dfrec.create_time, dfrec.fsize, dfrec.block_size, dfrec.tsnum,
              dfrec.stop_scn, read_only, dfrec.stop_time);

            IF dfrec.offline_scn <> 0 then
               dbms_rcvcat.checkOfflineRange(
                 null, null, dfrec.fileno, dfrec.create_scn,
                 dfrec.offline_scn, dfrec.online_scn, dfrec.online_time,
                 cf_create_time);
            END IF;
          END IF;
          found := found + 1;
          IF (found = total_recs) THEN
            goto all_found;
          END IF;
        end loop;
        low := low + rec_per_chunk;
      end loop;

      <<all_found>>

       dbms_rcvcat.endDataFileResync;
    end if;
 
    if dbms_rcvcat.beginThreadResync(high_rt_recid) then
      for rtrec in rt loop
        dbms_rcvcat.checkThread
         (rtrec.thread#, rtrec.last_sequence#, rtrec.enable_scn,
          rtrec.enable_time, rtrec.disable_scn, rtrec.disable_time,
          rtrec.status);
      end loop;

      dbms_rcvcat.endThreadResync;
    end if;
 
    if dbms_rcvcat.beginOnlineRedoLogResync(high_le_recid) then
      for orlrec in orl loop
        dbms_rcvcat.checkOnlineRedoLog
         (orlrec.thread#, orlrec.group#, orlrec.fname);
      end loop;

      dbms_rcvcat.endOnlineRedoLogResync;
    end if;
 
  end if; -- full_resync

  -- If you add any more circular record types, and if the cursor
  -- to get the records does a join, be sure to do an outer join so that
  -- we see all the circular records, regardless of the joined table
  -- state.

  recid := dbms_rcvcat.beginLogHistoryResync;
  if (high_lh_recid > recid) then
    for rlhrec in rlh(recid, high_lh_recid) loop
      dbms_rcvcat.checkLogHistory(
        rlhrec.recid, rlhrec.stamp, rlhrec.thread#,
        rlhrec.sequence#, rlhrec.low_scn, rlhrec.low_time,
        rlhrec.next_scn);
    end loop;
  end if;
  dbms_rcvcat.endLogHistoryResync;

  recid := dbms_rcvcat.beginArchivedLogResync;
  if (high_al_recid > recid) then
    select record_size into rec_size
    from v$controlfile_record_section
    where type='ARCHIVED LOG';

    rec_per_chunk := sort_area_size / rec_size;

    total_recs := high_al_recid - recid + 1;

    low  := recid;
    high := low;
    while (high < high_al_recid) loop
      high := least(low + rec_per_chunk -1, high_al_recid);
      for alrec in al(low, high) loop
        dbms_rcvcat.checkArchivedLog(
          alrec.recid, alrec.stamp, alrec.thread#,
          alrec.sequence#, alrec.resetlogs_change#, alrec.resetlogs_time,
          alrec.first_change#, alrec.first_time, alrec.next_change#,
          alrec.next_time, alrec.blocks, alrec.block_size, alrec.name,
          alrec.archived, alrec.completion_time, alrec.status);
      end loop;
      low := low + rec_per_chunk;
    end loop;
  end if;
  dbms_rcvcat.endArchivedLogResync;

  recid := dbms_rcvcat.beginOfflineRangeResync;
  if (high_or_recid > recid) then
     for offrrec in offr(recid, high_or_recid) LOOP
        dbms_rcvcat.checkOfflineRange(
          offrrec.recid, offrrec.stamp, offrrec.file#,
          offrrec.creation_change#,
          offrrec.offline_change#, offrrec.online_change#, offrrec.online_time,
          cf_create_time);
     end loop;
  end if;
  dbms_rcvcat.endOfflineRangeResync;

  recid := dbms_rcvcat.beginBackupSetResync;
  if (high_bs_recid > recid) then
    for bsrec in bs(recid, high_bs_recid) loop
      dbms_rcvcat.checkBackupSet(
        bsrec.recid, bsrec.stamp, bsrec.set_stamp, bsrec.set_count,
        bsrec.backup_type, bsrec.incremental_level, bsrec.pieces,
        bsrec.start_time, bsrec.completion_time);
    end loop;
  end if;
  dbms_rcvcat.endBackupSetResync;

  recid := dbms_rcvcat.beginBackupPieceResync;
  if (high_bp_recid > recid) then
    select record_size into rec_size
    from v$controlfile_record_section
    where type='BACKUP PIECE';

    rec_per_chunk := sort_area_size / rec_size;

    total_recs := high_bp_recid - recid + 1;

    low  := recid;
    high := low;
    while (high < high_bp_recid) loop
      high := least(low + rec_per_chunk -1, high_bp_recid);
      for bprec in bp(low, high) loop
        dbms_rcvcat.checkBackupPiece(
          bprec.recid, bprec.stamp, bprec.set_stamp, bprec.set_count,
          bprec.piece#, bprec.tag, bprec.device_type, bprec.handle,
          bprec.comments, bprec.media, bprec.concur,
          bprec.start_time, bprec.completion_time, bprec.status, bprec.copy#,
          bprec.media_pool);
      end loop;
      low := low + rec_per_chunk;
    end loop;
  end if;
  dbms_rcvcat.endBackupPieceResync;

  recid := dbms_rcvcat.beginBackupDataFileResync;
  if (high_bf_recid > recid) then
    select record_size into rec_size
    from v$controlfile_record_section
    where type='BACKUP DATAFILE';

    rec_per_chunk := sort_area_size / rec_size;

    total_recs := high_bf_recid - recid + 1;

    low  := recid;
    high := low;
    while (high < high_bf_recid) loop
      high := least(low + rec_per_chunk -1, high_bf_recid);
      for bdfrec in bdf(low, high) loop
        dbms_rcvcat.checkBackupDataFile(
          bdfrec.recid, bdfrec.stamp, bdfrec.set_stamp, bdfrec.set_count,
          bdfrec.file#, bdfrec.creation_change#, bdfrec.creation_time,
          bdfrec.resetlogs_change#, bdfrec.resetlogs_time,
          bdfrec.incremental_level, bdfrec.incremental_change#,
          bdfrec.checkpoint_change#, bdfrec.checkpoint_time,
          bdfrec.absolute_fuzzy_change#, bdfrec.datafile_blocks,
          bdfrec.blocks, bdfrec.block_size, bdfrec.oldest_offline_range,
          bdfrec.completion_time);
      end loop;
      low := low + rec_per_chunk;
    end loop;
  end if;
  dbms_rcvcat.endBackupDataFileResync;

  recid := dbms_rcvcat.beginBackupCorruptionResync;
  if (high_fc_recid > recid) then
    for bcbrec in bcb(recid, high_fc_recid) loop
      dbms_rcvcat.checkBackupCorruption(
        bcbrec.recid, bcbrec.stamp, bcbrec.set_stamp, bcbrec.set_count,
        bcbrec.piece#, bcbrec.file#, bcbrec.block#, bcbrec.blocks, 
        bcbrec.corruption_change#, bcbrec.marked_corrupt);
    end loop;
  end if;
  dbms_rcvcat.endBackupCorruptionResync;

  recid := dbms_rcvcat.beginBackupRedoLogResync;
  if (high_bl_recid > recid) then
    for brlrec in brl(recid, high_bl_recid) loop
      dbms_rcvcat.checkBackupRedoLog(
        brlrec.recid, brlrec.stamp, brlrec.set_stamp, brlrec.set_count,
        brlrec.thread#,
        brlrec.sequence#, brlrec.resetlogs_change#, brlrec.resetlogs_time, 
        brlrec.first_change#, brlrec.first_time, brlrec.next_change#, 
        brlrec.next_time, brlrec.blocks, brlrec.block_size);
    end loop;
  end if;
  dbms_rcvcat.endBackupRedoLogResync;

  recid := dbms_rcvcat.beginDataFileCopyResync;
  if (high_dc_recid > recid) then
    for cdfrec in cdf(recid, high_dc_recid) loop
      dbms_rcvcat.checkDataFileCopy(
        cdfrec.recid, cdfrec.stamp, cdfrec.fname, cdfrec.tag,
        cdfrec.file#, cdfrec.create_scn, cdfrec.create_time, cdfrec.reset_scn,
        cdfrec.reset_time, cdfrec.incr_level, cdfrec.ckp_scn, cdfrec.ckp_time, 
        cdfrec.online_fuzzy, cdfrec.backup_fuzzy, cdfrec.abs_fuzzy_scn,
        cdfrec.rcv_fuzzy_scn, cdfrec.rcv_fuzzy_time, 
        cdfrec.blocks, cdfrec.block_size, cdfrec.oldest_offline_range,
        cdfrec.completion_time,cdfrec.status);
    end loop;
  end if;
  dbms_rcvcat.endDataFileCopyResync;

  recid := dbms_rcvcat.beginCopyCorruptionResync;
  if (high_cc_recid > recid) then
    for ccbrec in ccb(recid, high_cc_recid) loop
      dbms_rcvcat.checkCopyCorruption(
        ccbrec.recid, ccbrec.stamp, ccbrec.copy_recid, ccbrec.copy_stamp,
        ccbrec.file#, ccbrec.block#, ccbrec.blocks, 
        ccbrec.corruption_change#, ccbrec.marked_corrupt);
    end loop;
  end if;
  dbms_rcvcat.endCopyCorruptionResync;

  recid := dbms_rcvcat.beginDeletedObjectResync;
  if (high_dl_recid > recid) then
    for dorec in dl(recid, high_dl_recid) loop
      dbms_rcvcat.checkDeletedObject(
        dorec.recid, dorec.stamp, 
        dorec.object_type, dorec.object_recid, dorec.object_stamp);
    end loop;
  end if;
  dbms_rcvcat.endDeletedObjectResync;

  recid := dbms_rcvcat.beginProxyResync;
  if (high_pc_recid > recid) then
    select record_size into rec_size
    from v$controlfile_record_section
    where type='PROXY COPY';

    rec_per_chunk := sort_area_size / rec_size;

    total_recs := high_pc_recid - recid + 1;

    low  := recid;
    high := low;
    while (high < high_pc_recid) loop
      high := least(low + rec_per_chunk - 1, high_pc_recid);
      for xdfrec in xdf(low, high) loop
        dbms_rcvcat.checkProxyDataFile(xdfrec.recid,
                                       xdfrec.stamp,
                                       xdfrec.tag,
                                       xdfrec.file#,
                                       xdfrec.create_scn,
                                       xdfrec.create_time,
                                       xdfrec.reset_scn,
                                       xdfrec.reset_time,
                                       xdfrec.incr_level,
                                       xdfrec.ckp_scn,
                                       xdfrec.ckp_time,
                                       xdfrec.online_fuzzy,
                                       xdfrec.backup_fuzzy,
                                       xdfrec.abs_fuzzy_scn,
                                       xdfrec.rcv_fuzzy_scn,
                                       xdfrec.rcv_fuzzy_time,
                                       xdfrec.blocks,
                                       xdfrec.block_size,
                                       xdfrec.oldest_offline_range,
                                       xdfrec.device_type,
                                       xdfrec.handle,
                                       xdfrec.comments,
                                       xdfrec.media,
                                       xdfrec.media_pool,
                                       xdfrec.start_time,
                                       xdfrec.completion_time,
                                       xdfrec.status);
      end loop;
      low := low + rec_per_chunk;
    end loop;
    -- put the checkProxyArchivedLog resync loop here
    -- Archivelog proxy backup support is complete in the dbms_backup_restore
    -- and all the layers in the kernel, but rman does not exploit it yet.
    -- That is why there are no recovery catalog tables or procedures for
    -- handling proxy archivelogs.
  end if;
  dbms_rcvcat.endProxyResync;

  dbms_rcvcat.endCkpt;

  if (full_resync) then
    -- release snapshot enqueue
    sys.dbms_backup_restore.cfileUseCurrent;
  end if;

  if (full_resync) then
    krmicd.writeMsg(8004);
  else
    krmicd.writeMsg(8005);
  end if;  

exception 
  when sort_area_too_small then
    dbms_rcvcat.cancelCkpt;
    if (sort_retries < 5) then
      sort_retries := sort_retries + 1;
      krmicd.writeMsg(1005, 
        'resync got ORA'||to_char(sqlcode)||
        ', retrying with sort_area_size = '||to_char(sort_retries)||'MB');
      krmicd.execSql(
        'alter session set sort_area_size = '||to_char(sort_retries*1024000));
      krmicd.clearErrors;
      goto retry;
    else
      krmicd.writeMsg(1005, 'resync got ORA-'||to_char(sqlcode)||
                          ', giving up');
      raise;
    end if;
  when sys.dbms_backup_restore.inconsistant_read then
    -- retry up to 5 times if resync gets the inconsistent_read exception
    dbms_rcvcat.cancelCkpt;
    if (read_retries < 5) then
      read_retries := read_retries + 1;
      krmicd.writeMsg(1005,'resync got ORA-235, retry '||to_char(read_retries));
      krmicd.clearErrors;
      goto retry;
    else
      krmicd.writeMsg(1005, 'resync got ORA-235, giving up');
      raise;
    end if;
  when others then
    dbms_rcvcat.cancelCkpt;
    if (full_resync) then
      sys.dbms_backup_restore.cfileUseCurrent;
    end if;
    raise;
end;
end;
>>>

#
# devalloc: allocate a device
#
define devalloc
<<<
-- devalloc
declare
  devtype varchar2(255);
  chid    varchar2(255);
  debug   number := null;
  options number := null;
  node    varchar2(255);
  maxsize binary_integer;
  vendor  varchar2(256);
begin
  &object&
  if debug is not null then 
    krmicd.execSql(
           'alter session set events ''immediate trace name krb_trace level '
           ||debug||'''');
  end if;
  if options is not null then 
    krmicd.execSql(
           'alter session set events ''immediate trace name krb_options level '
           ||options||'''');
  end if;
  devtype := sys.dbms_backup_restore.deviceAllocate( ident => chid,
     node => node, &args& );
  maxsize := sys.dbms_backup_restore.deviceQuery
    (sys.dbms_backup_restore.DEVICEQUERY_MAXSIZE);
  if maxsize > 0 then
     sys.dbms_backup_restore.setlimit
       (sys.dbms_backup_restore.kbytes, maxsize);
  end if;
  krmicd.setChannelInfo
    (devtype,
     node,
     maxsize,
     sys.dbms_backup_restore.deviceQuery
       (sys.dbms_backup_restore.DEVICEQUERY_PROXY),
     sys.dbms_backup_restore.deviceQuery
       (sys.dbms_backup_restore.DEVICEQUERY_MAXPROXY));
  krmicd.writeMsg(8030, chid);
  krmicd.writeMsg(8500, chid, to_char(krmicd.getSid), devtype);
  vendor := sys.dbms_backup_restore.deviceQuery
    (sys.dbms_backup_restore.DEVICEQUERY_VENDOR);
  if vendor is not null then
     krmicd.writemsg(8526, chid, vendor);
  end if;
end;
>>>

#
# devrel: release a device
#
define devrel
<<<
-- devrel
begin
  sys.dbms_backup_restore.deviceDeallocate;
  sys.dbms_backup_restore.set_client_info('');
  krmicd.writeMsg(8031, krmicd.getChid);
  krmicd.clearChannelInfo;                      -- tell krmq no device here now
end;
>>>

#
# setlm: set a limit on a device channel
#
define setlm
<<<
begin
>>>

define 'kbytes'
<<<
  sys.dbms_backup_restore.setLimit(
    sys.dbms_backup_restore.kbytes,&args&);
>>>

define 'readrate'
<<<
  sys.dbms_backup_restore.setLimit(
    sys.dbms_backup_restore.readrate,&args&);
>>>

define 'parallel'
<<<
  sys.dbms_backup_restore.setLimit(
    sys.dbms_backup_restore.parallel,&args&);
>>>

define setlmend
<<<
end;
>>>

#
# copydf:  copy a datafile
#
define copydf
<<<
-- copydf
declare
  full_name varchar2(512);
  recid     number;
  stamp     number;
  dfnumber  binary_integer;
begin
  &object& 
  sys.dbms_backup_restore.copyDataFile(full_name => full_name,
    recid => recid, stamp => stamp, dfnumber => dfnumber, &args&);
  krmicd.writeMsg(8000, krmicd.getChid, to_char(dfnumber));
  krmicd.writeMsg(8501, full_name, to_char(recid), to_char(stamp));
end;
>>>

#
# copydfc: copy a datafilecopy
#
define copydfc
<<<
-- copydfc
declare
  recid     number;
  stamp     number;
  copy_recid number;
  copy_stamp number;
  dfnumber          number;
  full_name varchar2(512);
begin
  &object&
  sys.dbms_backup_restore.copyDataFileCopy(full_name => full_name,
    recid => recid, stamp => stamp,
    copy_recid => copy_recid, copy_stamp => copy_stamp, &args&);
  krmicd.writeMsg(8007, krmicd.getChid, to_char(dfnumber));
  krmicd.writeMsg(8501, full_name, to_char(recid), to_char(stamp));
end;
>>>

#
# copycf: copy a controlfile
#
define copycf
<<<
-- copycf
declare
  src_name   varchar2(512);
  dest_name  varchar2(512);
  full_name  varchar2(512);
  recid      number;
  stamp      number;
begin
  &object&
  &args&
  sys.dbms_backup_restore.copyControlFile(src_name => src_name,
    dest_name => dest_name,
    recid => recid, stamp => stamp,
    full_name => full_name);
  krmicd.writeMsg(8025, krmicd.getChid, src_name, full_name);
  krmicd.writeMsg(8506, src_name);
  krmicd.writeMsg(8501, full_name, to_char(recid), to_char(stamp));
end;
>>>

#
# copycurrentcf: copy current controlfile
#
define copycurrentcf
<<<
-- copycurrentcf
declare
   dest_name    varchar2(512);
begin
   &args&
   krmicd.execSql(
      'alter database backup controlfile to '''||dest_name||''' reuse');
   krmicd.writeMsg(8027, krmicd.getChid);
   krmicd.writemsg(8505, dest_name);
exception
   when others then
   krmicd.writeMsg(8028, krmicd.getChid);
   raise;
end;
>>>

#
# copyalc: copy an archived log
#
define copyalc
<<<
-- copyalc
declare
  recid            number;
  stamp            number;
  arch_recid       number;
  arch_stamp       number;
  full_name        varchar2(512);
  thread           number;
  sequence         number;
begin
  &object&
  sys.dbms_backup_restore.copyArchivedLog(arch_recid => arch_recid,
    arch_stamp => arch_stamp, full_name => full_name, recid => recid,
    stamp => stamp, &args&);
  krmicd.writeMsg(8026, krmicd.getChid);
  krmicd.writeMsg(8504, to_char(thread), to_char(sequence),
                  to_char(arch_recid), to_char(arch_stamp));
  krmicd.writeMsg(8501, full_name, to_char(recid), to_char(stamp));
end;
>>>


# catdfc:  catalog a datafilecopy
#
define catdfc
<<<
-- catdfc
declare
  fname varchar2(512);
  full_name varchar2(512);
  recid     number;
  stamp     number;
begin
  &object& 
  sys.dbms_backup_restore.inspectDataFileCopy(fname => fname, 
    full_name => full_name, recid => recid, stamp => stamp, &args&);
  krmicd.writeMsg(8050);
  krmicd.writeMsg(8513, full_name, to_char(recid), to_char(stamp));
end;
>>>

# catalc:  catalog an archivelog copy
#
define catalc
<<<
-- catalc
declare
  fname varchar2(512);
  full_name varchar2(512);
  recid     number;
  stamp     number;
begin
  &object& 
  sys.dbms_backup_restore.inspectArchivedLog(fname => fname, 
    full_name => full_name, recid => recid, stamp => stamp);
  krmicd.writeMsg(8051);
  krmicd.writeMsg(8514, full_name, to_char(recid), to_char(stamp));
end;
>>>

# catbcf:  catalog a backup controlfile
#
define catbcf
<<<
-- catbcf
declare
  fname varchar2(512);
  full_name varchar2(512);
  recid     number;
  stamp     number;
begin
  &object& 
  sys.dbms_backup_restore.inspectControlFile(fname => fname, 
    full_name => full_name, recid => recid, stamp => stamp);
  krmicd.writeMsg(8052);
  krmicd.writeMsg(8516, full_name, to_char(recid), to_char(stamp));
end;
>>>

#
# clean: cleanup our session.  This is called for each allocated channel
#        when all other commands/steps have terminated normally.  
#        It is also called when a step terminates abnormally and the error
#        is not retry-able or the user does not want to retry.
#
# The first 'select count(*) from x$dual' statement here is a
# workaround for bug 756629.  When an error occurs on a
# channel, and multiple channels are allocated, we call upibrk/upirst on
# all the running channels.  Although upirst is supposed to reset the
# connection state so that the next call doesn't see the ORA-03111 error
# when indicates that upibrk was called, this isn't happening, and the next
# call (which was the below call to backupCancel) would get the ORA-03111.
# This then results in a hung connection, because the client ttc layer
# attempts to re-try the call that got ORA-03111, but the PL/SQL RPC
# callback isn't designed to re-try; because the callback was already called
# once, it doesn't think it needs to send the IN parameters again, so they
# are not placed on the network.  The server expects to see them, however,
# and hangs in read() when it tries to get them from the data packet and
# they are not there.  A 'select' statement, on the other hand does not
# have any problems when retried, so we use an innocuous statement to 
# absorb the 3111 before the next RPC.  Note that x$dual is a valid table
# when the database is started, mounted, or open.  We use krmicd.execSql
# instead of just executing the statement directly here because the
# delete channel doesn't call krmkllc, so the compilation of this step
# will fail when the delete channel is released when the database is
# not open.
define clean
<<<
-- clean
declare
  /* device status variables */
  state       binary_integer;
  devtype     varchar2(512);
  name        varchar2(512);
  bufsz       binary_integer;
  bufcnt      binary_integer;
  kbytes      binary_integer;
  readrate    binary_integer;
  parallel    binary_integer;
  thread      number;
  kcrmx_recs  number;

  mr_not_started exception;
  pragma exception_init(mr_not_started, -1112);
  db_not_mounted exception;
  pragma exception_init(db_not_mounted, -1507);
begin
  begin
    krmicd.execSql('select count(*) from x$dual');
  exception
    when others then
      krmicd.clearErrors;
  end;
  sys.dbms_backup_restore.backupCancel;
  sys.dbms_backup_restore.restoreCancel;
  sys.dbms_backup_restore.cfileUseCurrent;              -- release enqueue
  sys.dbms_backup_restore.deviceStatus(state, devtype, name, bufsz, bufcnt,
                                         kbytes, readrate, parallel);
  begin
    if krmicd.mrCheck > 0 then
      krmicd.execSql('alter database recover cancel');
    end if;
  exception
    when others then
      krmicd.clearErrors;
  end;

  if (state > sys.dbms_backup_restore.NO_DEVICE) then
    sys.dbms_backup_restore.deviceDeallocate;
    krmicd.writeMsg(8031, krmicd.getChid);
    krmicd.clearChannelInfo;                    -- tell krmq no device here now
    -- clear the client_info field on channels which have no device allocated.
    -- this has the effect of leaving the client_info field present on the
    -- default channel.
    sys.dbms_backup_restore.set_client_info('');
  end if;
end;
>>>

#
# budf_start: backup datafiles.  This is the first skeleton in the step.
#
define budf_start
<<<
-- budf_start
/* This must be retriable, which means a backup conversation may already
 * be in progress when this step (re)executes.
 */
declare
  /* backup conversation status variables */
  state       binary_integer;
  setid       number;
  stamp       number;
  pieceno     binary_integer;
  files       binary_integer;
  datafiles   boolean;
  incremental boolean;
  nochecksum  boolean;
  device      boolean;
  /* piece creation variables */
  done        boolean;
  concur      boolean;
  /* Miscellaneous */
  memnum      number;
  dfnumber    number;
  cfname      varchar2(512);
  copy_recid  number;
  copy_stamp  number;
  busy_retries number := 0;
  resetlogs_change  number;
  creation_change   number;
  checkpoint_change number;
  blksize           number;
  fname             varchar2(1024);
  no_delete         binary_integer;
  copy         number;
  ncopies      number := 0;
  handle       varchar2(512);
  comment      varchar2(80);
  media        varchar2(64);
  wrong_format      exception;
  first_time        boolean := TRUE;
  backup_level      number;
  set_type          varchar2(30);
  elapsed	    number;
  stampd	    date;
  hours             number;
  mins              number;
  secs              number;
begin
  &1&

  if backup_level is not null then
     set_type := 'incremental level ' || to_char(backup_level);
  else
     set_type := 'full';
  end if;

  sys.dbms_backup_restore.backupStatus(state, setid, stamp, pieceno, files,
                                       datafiles, incremental, nochecksum,
                                       device);
  if state = sys.dbms_backup_restore.BACKUP_NO_CONVERSATION then
    goto start_convo;
  elsif state = sys.dbms_backup_restore.BACKUP_NAMING_FILES then
    goto name_files;
  else
    goto create_piece;
  end if;

<<start_convo>>
  sys.dbms_backup_restore.backupSetDatafile(stamp, setid, &args&);
  krmicd.writeMsg(8008, krmicd.getChid, set_type);
  krmicd.writeMsg(8502, to_char(setid), to_char(stamp),
                  to_char(stamp2date(stamp)));
<<name_files>>
>>>
 
#
# bual_start: backup archivelogs.  This is the first skeleton in the step.
#
define bual_start
<<<
-- bual_start
/* This must be retriable, which means a backup conversation may already
 * be in progress when this step (re)executes.
 */
declare
  /* backup conversation status variables */
  state       binary_integer;
  setid       number;
  stamp       number;
  pieceno     binary_integer;
  files       binary_integer;
  datafiles   boolean;
  incremental boolean;
  nochecksum  boolean;
  device      boolean;
  /* piece creation variables */
  done        boolean;
  concur      boolean;
  /* Miscellaneous */
  memnum      number;
  arch_recid  number;
  arch_stamp  number;
  fname       varchar2(512);
  thread           number;
  sequence         number;
  resetlogs_change number;
  first_change     number;
  blksize          number;
  copy         number;
  ncopies      number := 0;
  handle       varchar2(512);
  comment      varchar2(80);
  media        varchar2(64);
  wrong_format     exception;
  first_time       boolean := TRUE;
  elapsed          number;
  stampd           date;
  hours            number;
  mins             number;
  secs             number;
begin
  sys.dbms_backup_restore.backupStatus(state, setid, stamp, pieceno, files,
                                         datafiles, incremental, nochecksum,
                                         device);

  if state = sys.dbms_backup_restore.BACKUP_NO_CONVERSATION then
    goto start_convo;
  elsif state = sys.dbms_backup_restore.BACKUP_NAMING_FILES then
    goto name_files;
  else
    goto create_piece;
  end if;

<<start_convo>>
  sys.dbms_backup_restore.backupSetArchivedLog(stamp, setid, &args&);
  krmicd.writeMsg(8009, krmicd.getChid);
  krmicd.writeMsg(8502, to_char(setid), to_char(stamp),
                  to_char(stamp2date(stamp)));
<<name_files>>
>>>
 
#
# budf_name: backup datafile name.  This skeleton appears 1 or more
# times in the step.  It adds 1 datafile to the backup set.
#
define budf_name
<<<
-- budf_name
  &memnum&
  &object&
  if (first_time) then
     krmicd.writeMsg(8010, krmicd.getChid);
     first_time := FALSE;
  end if;
  if files < memnum then
    begin
       sys.dbms_backup_restore.backupDataFile(dfnumber => dfnumber, &args&);
       krmicd.writeMsg(8522, to_char(dfnumber, 'FM09999'), fname);
    exception
       when sys.dbms_backup_restore.inc_scn_matches_df_scn then
          krmicd.writeMsg(8522, to_char(dfnumber, 'FM09999'), fname);
          krmicd.writeMsg(8056, to_char(dfnumber, 'FM09999'));
          krmicd.clearErrors;
    end;
    files := files + 1;
  end if;
>>>

#
# budc_name: backup datafilecopy name.
#
define budc_name
<<<
-- budc_name
  &memnum&
  &object&
  if files < memnum then
    sys.dbms_backup_restore.backupDataFileCopy(copy_recid => copy_recid,
      copy_stamp => copy_stamp, &args&);
    files := files + 1;
    krmicd.writeMsg(8033, krmicd.getChid, to_char(dfnumber, 'FM09999'));
    krmicd.writeMsg(8506, fname);
  end if;
>>>

#
# bucf_name: backup controlfile name.  Appears at most 1 time in a step.
# It adds a specified backup controlfile name to the backup set, or 
# the snapshot controlfile if cfname is null.
#
define bucf_name
<<<
-- bucf_name
  &memnum&
  &object&
  if (first_time) then
     krmicd.writeMsg(8010, krmicd.getChid);
     first_time := FALSE;
  end if;
  if files < memnum then
      -- Refresh the snapshot controlfile so that it is reasonably current
      -- before backing it up.  This is necessary because it is possible
      -- that the snapshot controlfile SCN is zero, indicating that its
      -- contents are not valid.
      <<snapshot>>  -- retry on failure to get snapshot enqueue

      begin
        if cfname is null then -- backup current controlfile
          sys.dbms_backup_restore.cfileMakeAndUseSnapshot;
          sys.dbms_backup_restore.cfileUseCurrent;
        end if;
        sys.dbms_backup_restore.backupControlFile(cfname => cfname);
      exception
        when sys.dbms_backup_restore.snapshot_enqueue_busy then
          -- retry up to 5 times, waiting 10 seconds between retries
          if busy_retries = 5 then
            krmicd.writeMsg(20029, 'cannot make a snapshot controlfile');
            raise;
          end if;

          busy_retries := busy_retries + 1;
          krmicd.writeMsg(8512);
          krmicd.sleep(10*busy_retries);
          krmicd.clearErrors;
          goto snapshot;
      end;      -- snapshot controlfile stuff

    files := files + 1;
    if cfname is null then
      krmicd.writeMsg(8011);
    else
      krmicd.writeMsg(8524, cfname);
    end if;
  end if;
>>>

#
# bual_name: backup archived log name.
#
define bual_name
<<<
-- bual_name
  name_log(&memnum&, &args&, files=>files, first_time => first_time);
>>>

#
# bu_create: backup piece create.  This skeleton appears 
# exactly once.  It loops until all pieces have been created.
#
define bu_create
<<<
-- bu_create
<<create_piece>>
  sys.dbms_backup_restore.backupStatus(state, setid, stamp, pieceno, files,
                                         datafiles, incremental, nochecksum,
                                         device);
  -- If we get here and the files count is zero, then all files in this
  -- backup set were skipped.

  if (files = 0) then
      sys.dbms_backup_restore.backupCancel;
      krmicd.writeMsg(8057, krmicd.getChid);
  else
      loop
          declare
              type names is table of varchar2(512) index by binary_integer;
              copyaux      number;
              fnames       names;
              busy_retries number := 0;
              fmt          varchar2(512) := NULL;
          begin
          <<snapshot>>
              begin
              -- format will substituted for fmt := '<format>'
              &format&

              -- ncopies will substituted for ncopies := <number>
              &ncopies&

              -- Call genPieceName as many times as ncopies, stored 
              -- generated names into array of fnames.  Check in the 
              -- array for duplicated names
              copy := 0;
              while copy < ncopies loop
                  begin
                  fnames(copy) :=
                      krmicd.genPieceName(pno => pieceno+1,
                                          set_count => setid,
                                          set_stamp => stamp,
                                          format => fmt, 
                                          dupflg => copy+1);
    
                  -- check previous names for collision
                  copyaux := 0;
                  while copyaux < copy loop
                      if fnames(copy) = fnames(copyaux) then
                          raise wrong_format;
                      end if; 
                      copyaux := copyaux + 1;
                  end loop;
                  -- Store the names of the second copy onwards
                  if (copy > 0) then
                      sys.dbms_backup_restore.backupPieceCrtDupSet(copy,
                                                                  fnames(copy));
                  end if;
                  exception
                      when wrong_format then
                          krmicd.writeMsg(11004, 
                                          'format needs %c when duplexing');
                          raise;
                  end;
                  copy := copy + 1;
              end loop;
              -- Ok, now do the actual piece creation, we use the 'old'
              -- interface
              copy := 0;
              sys.dbms_backup_restore.backupPieceCreate(fnames(copy),
                                                        pieceno, 
                                                        done, 
                                                        handle,
                                                        comment,
                                                        media,
                                                        concur, 
                                                        &args&);
              exception
                  when sys.dbms_backup_restore.snapshot_enqueue_busy then
                  -- retry up to 5 times, waiting 10 seconds between retries
                      if busy_retries = 5 then
                          krmicd.writeMsg(20029, 
                                          'cannot make a snapshot controlfile');
                          raise;
                      end if;
    
                      busy_retries := busy_retries + 1;
                      krmicd.writeMsg(8512);
                      krmicd.sleep(10*busy_retries);
                      krmicd.clearErrors;
                      goto snapshot;
              end;
          end; -- snapshot controlfile stuff
          if ncopies = 1 then
              krmicd.writeMsg(8013, krmicd.getChid, to_char(pieceno));
          else
              krmicd.writeMsg(8095, krmicd.getChid, to_char(pieceno), 
                              to_char(ncopies));
          end if;
          copy := 0;
          while copy < ncopies loop
              if (copy > 0) then
                  -- Retrieve output parameters from server structures
                  sys.dbms_backup_restore.backupPieceCrtDupGet(copy, 
                                                               handle,
                                                               comment,
                                                               media);
              end if;
              if comment is null then comment := 'NONE'; end if;
              krmicd.writeMsg(8503, handle, comment);
              copy := copy + 1;
          end loop;
          if done then
             stampd := stamp2date(stamp);
             select sysdate-stampd into elapsed from x$dual;
	     dur2time(elapsed, hours, mins, secs);
             krmicd.writemsg(8525, to_char(hours, 'FM09') || ':' ||
                              to_char(mins,  'FM09') || ':' ||
                              to_char(secs,  'FM09'));
             exit;
          end if;
      end loop;
  end if;
  first_time := TRUE;  -- in case we will be deleting what we backed up
>>>

define budc_del
<<<
-- budc_del
  &object&
  sys.dbms_backup_restore.deleteDataFileCopy(recid => copy_recid,
                                                   stamp => copy_stamp,
                                                   fname => fname,
                                                   dfnumber => dfnumber,
                                                   resetlogs_change =>
                                                     resetlogs_change,
                                                   creation_change =>
                                                     creation_change,
                                                   checkpoint_change =>
                                                     checkpoint_change,
                                                   blksize => blksize,
                                                   no_delete => no_delete);
  if (dfnumber = 0) then
    krmicd.writeMsg(8072);
    krmicd.writeMsg(8516, fname, to_char(copy_recid), to_char(copy_stamp));
  else
    krmicd.writeMsg(8070);
    krmicd.writeMsg(8513, fname, to_char(copy_recid), to_char(copy_stamp));
  end if;
>>>


define bual_del
<<<
-- bual_del
  &object&
  sys.dbms_backup_restore.deleteArchivedLog(recid => arch_recid,
                                                  stamp => arch_stamp,
                                                  fname => fname,
                                                  thread => thread,
                                                  sequence => sequence,
                                                  resetlogs_change =>
                                                    resetlogs_change,
                                                  first_change =>
                                                    first_change,
                                                  blksize => blksize);
  if (first_time) then
     krmicd.writeMsg(8071, krmicd.getChid);
  end if;
  first_time := FALSE;
  krmicd.writeMsg(8514, fname, to_char(arch_recid), to_char(arch_stamp));
>>>

define bu_end
<<<
end;
>>>

#
# restoredfc: restore a datafile from a datafile copy
#
define restoredfc
<<<
-- restoredfc
declare
  recid             number;
  stamp             number;
  copy_recid        number;
  copy_stamp        number;
  dfnumber          number;
  fname             varchar2(512) := null;      -- restore dest
  max_corrupt       number := 0;
  full_name         varchar2(512);              -- output filename
  copy              varchar2(512);              -- input filename
  check_logical     boolean := false;
begin
  &object&
  krmicd.writeMsg(8019, krmicd.getChid, to_char(dfnumber, 'FM09999'));
  krmicd.writeMsg(8507, to_char(copy_recid), to_char(copy_stamp), copy);
  if fname is not null then
    krmicd.writeMsg(8509, to_char(dfnumber, 'FM09999'), fname);
  end if;
  sys.dbms_backup_restore.copyDataFileCopy(full_name => full_name,
                                             recid => recid,
                                             stamp => stamp,
                                             fname => fname,
                                             copy_recid => copy_recid,
                                             copy_stamp => copy_stamp,
                                             max_corrupt => max_corrupt,
                                             check_logical => check_logical);
  krmicd.writeMsg(8007, krmicd.getChid, to_char(dfnumber, 'FM09999'));
  krmicd.writeMsg(8501, full_name, to_char(recid),to_char(stamp));
end;
>>>

#
# restorecfc: restore a controlfile from a controlfile copy
#
define restorecfc
<<<
-- restorecfc
declare
  src_name   varchar2(512);
  dest_name  varchar2(512);
  full_name  varchar2(512);
  recid      number;
  stamp      number;
  cfrepl     boolean;
begin
  &object&
  krmicd.writeMsg(8021, krmicd.getChid);
  krmicd.writeMsg(8505, dest_name);
  sys.dbms_backup_restore.copyControlFile(full_name => full_name,
    recid => recid, stamp => stamp, src_name => src_name,
    dest_name => dest_name);
  krmicd.writeMsg(8025, krmicd.getChid);
  krmicd.writeMsg(8506, src_name);
  krmicd.writeMsg(8501, full_name, to_char(recid), to_char(stamp));
  if (cfrepl) then
     replicate_controlfile(cfname => dest_name);
  end if;     
end;
>>>

#
# rsdf_start: restore datafiles/controlfiles.  This is the first skeleton
# in the step.
#
define rsdf_start
<<<
-- rsdf_start
declare
  /* restoreStatus */
  state       binary_integer;
  pieces_done binary_integer;
  files       binary_integer;
  datafiles   boolean;
  incremental boolean;
  device      boolean;
  /* restorebackuppiece */
  done        boolean;
  cfrepl      boolean;
  handle      varchar2(512);
  params      varchar2(512);
  /* Miscellaneous */
  memnum      number;
  piecenum    number;
  dfnumber    number;
  toname      varchar2(512);
  cfname      varchar2(512);
  set_count   number;
  set_stamp   number;
  restore_not_complete exception;
  first_time  boolean := TRUE;
  validate    boolean := FALSE;
  max_corrupt  binary_integer := 0;
  check_logical boolean := FALSE;
  tag         varchar2(31);
begin
  &1&
  sys.dbms_backup_restore.restoreStatus(state, pieces_done, files, datafiles,
                       incremental, device);
  if state = sys.dbms_backup_restore.restore_no_conversation then
    goto start_convo;
  elsif state = sys.dbms_backup_restore.restore_naming_files then
    goto name_files;
  else
    goto restore_piece;
  end if;

<<start_convo>>
  sys.dbms_backup_restore.restoreSetDatafile(check_logical=>check_logical);
  if validate then
    krmicd.writeMsg(8096, krmicd.getChid);
  else
    krmicd.writeMsg(8016, krmicd.getChid);
  end if;
  krmicd.writeMsg(8502, to_char(set_count), to_char(set_stamp),
                  to_char(stamp2date(set_stamp)));
<<name_files>>
>>>

#
# ridf_start: restore incremental datafiles backup set start
#
define ridf_start
<<<
-- ridf_start
declare
  /* restoreStatus */
  state       binary_integer;
  pieces_done binary_integer;
  files       binary_integer;
  datafiles   boolean;
  incremental boolean;
  device      boolean;
  /* restorebackuppiece */
  done        boolean;
  cfrepl      boolean;
  handle      varchar2(512);
  params      varchar2(512);
  /* Miscellaneous */
  memnum      number;
  piecenum    number;
  dfnumber    number;
  toname      varchar2(512);
  cfname      varchar2(512);
  fuzzy_hint  number;
  set_count   number;
  set_stamp   number;
  restore_not_complete exception;
  first_time  boolean := TRUE;
  validate    boolean := FALSE;
  max_corrupt  binary_integer := 0;
  check_logical boolean := FALSE;
  tag           varchar2(31);
begin
  &1&
  sys.dbms_backup_restore.restoreStatus(state, pieces_done, files, datafiles,
                       incremental, device);
  if state = sys.dbms_backup_restore.restore_no_conversation then
    goto start_convo;
  elsif state = sys.dbms_backup_restore.restore_naming_files then
    goto name_files;
  else
    goto restore_piece;
  end if;

<<start_convo>>
  sys.dbms_backup_restore.applySetDatafile(check_logical=>check_logical);
  krmicd.writeMsg(8039, krmicd.getChid);
  krmicd.writeMsg(8502, to_char(set_count), to_char(set_stamp),
                  to_char(stamp2date(set_stamp)));
<<name_files>>
>>>

#
# rsdf_name - name a datafile to be restored from a backup set
#
define rsdf_name
<<<
-- rsdf_name
  toname := null;
  max_corrupt := 0;
  &memnum&
  &object&
  if files < memnum then
    sys.dbms_backup_restore.restoreDataFileTo(dfnumber => dfnumber,
                                                toname => toname,
                                                max_corrupt => max_corrupt);
    if first_time then
       krmicd.writeMsg(8089, krmicd.getChid);
       first_time := FALSE;
    end if;
    if toname is not null then
      krmicd.writeMsg(8523, to_char(dfnumber, 'FM09999'), toname);
    else
      krmicd.writeMsg(8523, to_char(dfnumber, 'FM09999'), 'default location');
    end if;
  end if;
>>>

#
# ridf_name - name a datafile to be restored from an incremental backup set
#
define ridf_name
<<<
-- ridf_name
  toname := null;
  max_corrupt := 0;
  &memnum&
  &object&
  if files < memnum then
    sys.dbms_backup_restore.applyDataFileTo(dfnumber => dfnumber,
                                              toname => toname,
                                              fuzziness_hint => fuzzy_hint,
                                              max_corrupt => max_corrupt);
    if first_time then
       krmicd.writeMsg(8089, krmicd.getChid);
       first_time := FALSE;
    end if;

    if toname is not null then
      krmicd.writeMsg(8509, to_char(dfnumber, 'FM09999'), toname);
    end if;
  end if;
>>>

#
# rscf_name - restore the controlfile in a datafile restore conversation
#
define rscf_name
<<<
-- rscf_name
  &object&
  &memnum&
  if files < memnum then
    sys.dbms_backup_restore.restoreControlfileTo(cfname => cfname);
    krmicd.writeMsg(8021, krmicd.getChid);
    krmicd.writeMsg(8505, cfname);
  end if;
>>>

#
# restore_piece_label - the beginning of the piece restore section
#
define restore_piece_label
<<<
-- restore_piece_label
<<restore_piece>>
>>>

#
# restore_piece - restore one backup piece.  Applies to all types of full
# restore conversations.
#
define restore_piece
<<<
-- restore_piece
  params := null;
  &piecenum&
  &object&
  if (pieces_done+1) = piecenum then
    sys.dbms_backup_restore.restoreBackupPiece(done => done,
                                                 handle => handle,
                                                 params => params);
    krmicd.writeMsg(8023, krmicd.getChid, to_char(piecenum));
    krmicd.writeMsg(8511, handle, nvl(tag, 'NULL'), params);
    if done then
      if (validate) then
        krmicd.writeMsg(8098, krmicd.getChid);
      else        
        krmicd.writeMsg(8024, krmicd.getChid);
      end if;
      -- replicate controlfile if default cf restore
      if cfrepl then
         replicate_controlfile(cfname => cfname);
      end if;
      return;
    end if;
    pieces_done := pieces_done + 1;
  end if;
>>>

define restore_end
<<<
-- restore_end
  krmicd.writeMsg(8001);
  raise restore_not_complete;
end;
>>>

#
# rsal_start: restore archived logs.  This is the first skeleton in the step.
#
define rsal_start
<<<
-- rsal_start
declare
  /* restoreStatus */
  state       binary_integer;
  pieces_done binary_integer;
  files       binary_integer;
  datafiles   boolean;
  incremental boolean;
  device      boolean;
  /* restorebackuppiece */
  done        boolean;
  cfrepl      boolean;      -- unused here
  cfname      varchar2(512);
  handle      varchar2(512);
  params      varchar2(512);
  /* Miscellaneous */
  memnum      number;
  piecenum    number;
  thread      number;
  sequence    number;
  destination varchar2(512) := null;
  restore_not_complete exception;
  validate    boolean := FALSE;
  tag         varchar2(31);
begin
  sys.dbms_backup_restore.restoreStatus(state, pieces_done, files, datafiles,
                       incremental, device);
  if state = sys.dbms_backup_restore.restore_no_conversation then
    goto start_convo;
  elsif state = sys.dbms_backup_restore.restore_naming_files then
    goto name_files;
  else
    goto restore_piece;
  end if;

<<start_convo>>
  &object&
  sys.dbms_backup_restore.restoreSetArchivedLog(destination=>destination);
  if validate then
    krmicd.writeMsg(8097, krmicd.getChid);
  elsif destination is null then
    krmicd.writeMsg(8017, krmicd.getChid);
  else
    krmicd.writeMsg(8018, krmicd.getChid);
    krmicd.writeMsg(8508, destination);
  end if;
<<name_files>>
>>>

#
# rsal_name - name an archived log to be restored from a backup set
#
define rsal_name
<<<
-- rsal_name
  &memnum&
  &object&
  if files < memnum then
    sys.dbms_backup_restore.restoreArchivedLog(thread => thread,
                                                 sequence => sequence);
    krmicd.writeMsg(8022, krmicd.getChid);
    krmicd.writeMsg(8510, to_char(thread), to_char(sequence));
  end if;
>>>

#
# switch:
#
define 'switch'
<<<
-- switch
declare
  copy_recid number;
  copy_stamp number;
  dfnumber   number;
  fname      varchar2(512);
begin
  &object&
  sys.dbms_backup_restore.switchToCopy(copy_recid=>copy_recid,
                                         copy_stamp=>copy_stamp);
  krmicd.writeMsg(8015, to_char(dfnumber));
  krmicd.writeMsg(8507, to_char(copy_recid), to_char(copy_stamp), fname);
end;
>>>


#
# sql: arbitrary sql 
#
define 'sql' 
<<<
-- sql
begin
  krmicd.execSql(&args&);
end;
>>>
#
#
# TESTING Only!
#
define testkrm
<<<
begin
  krmicd.writeMsg(1005, null);
end;
>>>

# smr is divided into three parts, because the 'alter database recover'
# statement may need to be repeated when ALTER DATABASE RECOVER DATAFILE LIST
# is used, as part of RECOVER DATABASE.
#
# smr1: start media recovery -  part 1 (always used) - start of block
#
define 'smr1'
<<<
-- smr1
declare
   mr_cancelled exception;
   pragma exception_init(mr_cancelled, -283);

   mr_need_log  exception;
   pragma exception_init(mr_need_log, -279);

   mr_not_needed  exception;
   pragma exception_init(mr_not_needed, -264);

   thread           number;
   sequence         number;
   scn              number;
   dellog           binary_integer;
   recid            binary_integer;
   stamp            binary_integer;
   seq              binary_integer;
   resetlogs_change number;
   first_change     number;
   blksize          binary_integer;
   logname          varchar2(512);
   stopthd          number;
   stopseq          varchar2(50);
begin
   begin
      krmicd.writeMsg(8054);            -- starting media recovery
>>>

#
# smr2: start media recovery - part 2 (optional) - RECOVER DATAFILE LIST
#
define 'smr2'
<<<
-- smr2
      krmicd.execSql('alter database recover datafile list' ||
        '&args&');
>>>

#
# smr3: start media recovery - part 3 (always used) - remainder of block
#
define 'smr3'
<<<
-- smr3
      krmicd.execSql('alter database recover if needed' || '&args&');
      krmicd.writeMsg(8055);            -- media recovery complete
      return;    
   exception
      when mr_cancelled then
        krmicd.writeMsg(8059);
        raise;
      when mr_need_log then
        krmicd.clearErrors;
      when mr_not_needed then
        krmicd.clearErrors;
        krmicd.writeMsg(8055);          -- media recovery complete
        return;
      when others then
        krmicd.writeMsg(8059);
        raise;
   end;

   krmicd.checkUntil(stopthd, stopseq);

   select thr, seq, scn into thread, sequence, scn from x$kcrmx;

   if stopthd = thread and sequence >= to_number(stopseq) then
      krmicd.execSql('alter database recover cancel');
      krmicd.writeMsg(8055);               -- media recovery complete
      return;
   end if;

end;
>>>

#
# log_apply:  apply some logs during media recovery
#
define 'log_apply'
<<<
-- log_apply
declare
  mr_cancelled exception;
  pragma exception_init(mr_cancelled, -283);

  mr_need_log  exception;
  pragma exception_init(mr_need_log, -279);

  mr_aborted   exception;
  pragma exception_init(mr_aborted, -20500);

  sequence         number;
  thd              number;
  scn              number;
  dellog           binary_integer;
  recid            binary_integer;
  recid_num        number;
  stamp            binary_integer;
  stamp_num        number;
  seq              binary_integer;
  seq_num          number;
  resetlogs_change number;
  first_change     number;
  blksize          binary_integer;
  blksize_num      number;
  logname          varchar2(512) := NULL;
  stopthd          number;
  stopseq          varchar2(50);

begin

  krmicd.checkUntil(stopthd, stopseq);

  <<get_log>>

  begin
    select thr, seq, scn into thd, sequence, scn from x$kcrmx;
  exception
    when no_data_found then
      -- presumably the media recovery has completed, so just exit
      return;
  end;

  if stopthd = thd and sequence >= to_number(stopseq) then
     krmicd.execSql('alter database recover cancel');
     krmicd.writeMsg(8055);             -- media recovery complete
     return;
  end if;

  begin
     -- krmkclog
     logname := krmicd.getLog(thd, scn, dellog, recid, stamp, seq,
                              resetlogs_change, first_change, blksize);
  exception
     when others then
        krmicd.writeMsg(8060);          -- unable to find log
        krmicd.writeMsg(8510, to_char(thd), to_char(sequence));
        raise;
  end;

  if recid >= 0 then                                           /* sb4 -> ub4 */
     recid_num := recid;
  else
     recid_num := 2**32 + recid;
  end if;
  if stamp >= 0 then                                           /* sb4 -> ub4 */
     stamp_num := stamp;
  else
     stamp_num := 2**32 + stamp;
  end if;
  if seq >= 0 then                                             /* sb4 -> ub4 */
     seq_num := seq;
  else
     seq_num := 2**32 + seq;
  end if;
  if blksize >=0 then                                          /* sb4 -> ub4 */
     blksize_num := blksize;
  else
     blksize_num := 2**32 + blksize;
  end if;

  begin
    if logname is not NULL then
      krmicd.writeMsg(8515, logname, to_char(thd), to_char(sequence));
      krmicd.execSql('alter database recover logfile ''' || logname || '''');
      if dellog = 1 then
         sys.dbms_backup_restore.deleteArchivedLog(recid => recid_num,
                                                   stamp => stamp_num,
                                                   fname => logname,
                                                   thread => thd,
                                                   sequence => seq_num,
                                                   resetlogs_change =>
                                                     resetlogs_change,
                                                   first_change =>
                                                     first_change,
                                                   blksize => blksize_num);
         krmicd.writeMsg(8071, krmicd.getChid);
         krmicd.writeMsg(8514, logname, to_char(recid_num),
                         to_char(stamp_num));
      end if;
      krmicd.writeMsg(8055);                    -- media recovery complete
      return;
    else
      return;
    end if;
  exception
    when mr_need_log then
       if dellog = 1 then
          sys.dbms_backup_restore.deleteArchivedLog(recid => recid_num,
                                                   stamp => stamp_num,
                                                   fname => logname,
                                                   thread => thd,
                                                   sequence => seq_num,
                                                   resetlogs_change =>
                                                     resetlogs_change,
                                                   first_change =>
                                                     first_change,
                                                   blksize => blksize_num);
          krmicd.writeMsg(8071, krmicd.getChid);
          krmicd.writeMsg(8514, logname, to_char(recid_num),
                          to_char(stamp_num));
       end if;
       krmicd.clearErrors;
       goto get_log;
  end;
end;
>>>

#
# scr_cre_rep: create or replace a script
#  
define 'scr_cre_rep'
<<<
-- scr_cre_rep
declare
  creat  boolean;
  line   varchar2(1024);
  name   varchar2(100);
begin
  &args&

  if creat then
    dbms_rcvcat.createScript(name);
  else
    dbms_rcvcat.replaceScript(name);
  end if;

  loop
    line := krmicd.getLine;
    if line is null then
      exit;
    end if;
    dbms_rcvcat.putLine(line);
  end loop;

  dbms_rcvcat.commitChanges;
  if creat then
    krmicd.writeMsg(8085, name);
  else
    krmicd.writeMsg(8086, name);
  end if;
end;
>>>

#
# replicate_controlfile: multiplex a controlfile from one source file to all
# of the locations specified in the target database's init.ora file.
#
define replicate_controlfile
<<<
declare
  cfname      varchar2(512);
begin
  &object&
  replicate_controlfile(cfname => cfname);
end;
>>>

define 'x$replicate_controlfile'
<<<
procedure replicate_controlfile(cfname IN varchar2) IS
  src_name   varchar2(512);
  dest_name  varchar2(512);
  full_name  varchar2(512);
  recid      number;
  stamp      number;
begin
  src_name := sys.dbms_backup_restore.normalizefilename(cfname);
  krmicd.writeMsg(8058);
  krmicd.writeMsg(8506, src_name);
  for i in 1..9999 loop
    dest_name := sys.dbms_backup_restore.getparm(
              sys.dbms_backup_restore.control_file, i);
    if dest_name is null then exit; end if;
    if src_name <> dest_name then
      krmicd.writeMsg(8505, dest_name);
      sys.dbms_backup_restore.copyControlFile(src_name  => src_name,
                                              dest_name => dest_name,
                                              recid     => recid,
                                              stamp     => stamp,
                                              full_name => full_name);
    end if;
  end loop;
end;
>>>

# rpctest: test async RPCs
#
define "rpctest"
<<<
-- rpctest
begin
  sys.dbms_backup_restore.sleep(60);            -- 1 minutes
end;
>>>

#
# tspitr_0: pre_tspitr script.  This member is used once.
#
define tspitr_0
<<<
# set the until clause
set until &1&;

# restore the controlfile
restore clone controlfile to clone_cf;

# replicate the controlfile
replicate clone controlfile from clone_cf;

# mount the controlfile
sql clone 'alter database mount clone database';

# archive current online log for tspitr to a resent until time
sql 'alter system archive log current';
>>>

#
# tspitr_sy_ct: resync catalog
#
define tspitr_sy_ct
<<<
# resync catalog after controlfile restore
resync catalog;
>>>

#
# tspitr_1: set until phase of tspitr script.  
#
define tspitr_1
<<<
{
# generated tablespace point-in-time recovery script

# set the until clause
set until &1&;
>>>

#
# tspitr_2: offline the tablespaces we'll import.  
#
define tspitr_2
<<<
plsql
&begin&
-- tspitr_2
declare
  sqlstatement       varchar2(512);
  offline_not_needed exception;
  pragma exception_init(offline_not_needed, -01539);
begin
  sqlstatement := 'alter tablespace '||'&1&'||' offline for recover';
  krmicd.writeMsg(6162, sqlstatement);
  krmicd.execSql(sqlstatement);
exception
  when offline_not_needed then
    null;
end;
&end&
;
>>>

#
# tspitr_3: switch a datafile that we are FLIPing.  This member is used
#           once for each datafile we flip.
#
define tspitr_3
<<<
# do a FLIP
switch clone datafile &1& to datafilecopy &2&;
>>>

#
# tspitr_4: set restore destinations for each datafile.  This member is used
#           once for each datafile we restore.
#
define tspitr_4
<<<
# set a destination filename for restore
set newname for datafile &1& to &2&;
>>>

#
# tspitr_5: do the restores.  This member is used once.
#
define tspitr_5
<<<
# restore the tablespaces in the recovery set plus the auxilliary tablespaces
restore clone datafile &1&;

switch clone datafile all;
>>>

#
# tspitr_6: online the datafiles we restored.   This member is used once for
#           datafile we restored.
#
define tspitr_6
<<<
#online the datafiles restored or flipped
sql clone "alter database datafile &1& online";
>>>

#
# tsiptr_7: do the incomplete recovery and resetlogs.  This member is used once.
#
define tspitr_7
<<<
# make the controlfile point at the restored datafiles, then recover them

recover clone database tablespace &1&;

alter clone database open resetlogs;
# PLUG HERE the creation of a temporary tablespace if export fails due to lack
# of temporary space.
# For example in Unix these two lines would do that:
#sql clone "create tablespace aux_tspitr_tmp
#           datafile ''/tmp/aux_tspitr_tmp.dbf'' size 500K";
}
>>>

#
# tspitr_8: online the tablespaces will be exported.  
#
define tspitr_8
<<<
#online the tablespace will be exported
sql clone "alter tablespace &1& online";
>>>

#
# tspitr_9: export the tablespaces in the recovery set.  
#
define tspitr_9
<<<
# export the tablespaces in the recovery set
host 'exp userid =&1& point_in_time_recover=y tablespaces=&2& file=&3&';
>>>

#
#
# tspitr_10: change datafilecopy uncatalog
#
define tspitr_10
<<<
#change datafilecopy uncatalog
change datafilecopy &1& uncatalog;
>>>

# tspitr_11: rename datafile in recovery set
#
define tspitr_11
<<<
#rename a datafile in both recovery set and setname/setclonename list
sql "alter database rename file &1& to &2&";
>>>

#
# tspitr_12: import the tablespaces in the recovery set.
#
define tspitr_12
<<<
# shutdown clone before import
shutdown clone immediate
# import the tablespaces in the recovery set
host 'imp userid =&1& point_in_time_recover=y file=&2&';
>>>

#
# tspitr_13: online/offline the tablespaces imported.  
#           (for getting the correct resetlogs SCN)
#
define tspitr_13
<<<
# online/offline the tablespace imported
sql "alter tablespace &1& online";
sql "alter tablespace &2& offline";
>>>

#
# tspitr_14: resync catalog
#
define tspitr_14
<<<
# resync catalog after tspitr finished
resync catalog;
>>>

#
# validateset: validate backupset
#
define validateset
<<<
-- validateset
  sys.dbms_backup_restore.restorevalidate;
>>>

#
# scandfc: scan datafilecopy
#
define scandfc
<<<
-- scandfc
declare
  recid    number;
  stamp    number;
  fname    varchar2(512);
  dfnumber number;
  scn      number;
  check_logical boolean := false;
begin
  &object&
  if dfnumber = 0 then
    krmicd.writeMsg(8518, krmicd.getChid, fname);
  else
    krmicd.writeMsg(8519, krmicd.getChid, fname);
  end if;
  scn := sys.dbms_backup_restore.scandatafilecopy(recid,stamp,
                                                  update_fuzziness=>false,
                                              check_logical => check_logical);
end;
>>>

#
# scanal: scan archived log
#
define scanal
<<<
-- scanal
declare
  recid number;
  stamp number;
  fname varchar2(512);
begin
  &object&
  krmicd.writeMsg(8520, krmicd.getChid, fname);
  sys.dbms_backup_restore.scanarchivedlog(recid,stamp);
end;
>>>

#
# setcmdid: set command id
#
define setcmdid
<<<
-- setcmdid
declare
  cmdid  varchar2(512);
begin
  &object&
  if krmicd.getDevType is null then
    return;
  end if;
  sys.dbms_backup_restore.set_client_info(
    'id='||cmdid||',ch='||krmicd.getchid);
end;
>>>

#
# aor: apply offline range
#
define aor
<<<
-- aor
declare
  cfname   varchar2(512) := null;
  recid    number;
  stamp    number;
  fname    varchar2(512) := null;
  dfnumber binary_integer := null;
  blksize  number := null;
begin
  &1&                   -- set variables

  sys.dbms_backup_restore.applyOfflineRange(cfname  => cfname,
                                            dfname  => fname,
                                            blksize => blksize,
                                            recid   => recid,
                                            stamp   => stamp,
                                            fno     => dfnumber);

  if (fname is not null) then
    krmicd.writeMsg(8088, fname);
  else
    krmicd.writeMsg(8088, to_char(dfnumber, 'FM09999'));
  end if;
  krmicd.writeMsg(8521, to_char(recid), to_char(stamp));
end;
>>>

#
# proxy_backup_start: proxy backup start
#
define proxy_backup_start
<<<
-- proxy_backup_start
declare
  dfnumber          number;
  copy_recid        number;
  copy_stamp        number;
  busy_retries      number;
  resetlogs_change  number;
  creation_change   number;
  checkpoint_change number;
  blksize           number;
  fname             varchar2(1024);
  fmt               varchar2(512);
  no_delete         binary_integer;
  wrong_format      exception;
  first_time        boolean := TRUE;
  incremental       boolean := FALSE;
  media_pool        binary_integer := 0;
  set_type          varchar2(30);
  tag               varchar2(32) := NULL;
  elapsed           number;
  start_time        date;
  hours             number;
  mins              number;
  secs              number;
  pieceno           number := 0;
  set_stamp         number;
  set_count         number;
  cfname            varchar2(1);      -- not used for backup
  cfrepl            boolean := FALSE; -- not used for backup
  validate          boolean := FALSE; -- not used for backup
begin
  &1&

  if incremental then
     set_type := 'incremental level 0';
  else
     set_type := 'full';
  end if;

  sys.dbms_backup_restore.proxycancel;
  sys.dbms_backup_restore.proxyBeginBackup(tag => tag,
                                           incremental => incremental,
                                           media_pool => media_pool,
                                           set_stamp => set_stamp,
                                           set_count => set_count);
  krmicd.writeMsg(8527, krmicd.getChid, set_type,
                  to_char(stamp2date(set_stamp)));
  start_time := stamp2date(set_stamp);
>>>

#
# proxy_budf: proxy backup datafile name.  This skeleton appears 1 or more
# times in the step.  It adds 1 datafile to the list of files to be
# proxy backed up.
#
define proxy_budf
<<<
-- proxy_budf
  fmt := NULL;
  &1& -- dfnumber, fmt, fname
  pieceno := pieceno + 1;
  if (first_time) then
     krmicd.writeMsg(8091, krmicd.getChid);
     first_time := FALSE;
  end if;
  sys.dbms_backup_restore.proxyBackupDataFile
    (file# => dfnumber,
     handle => krmicd.genPieceName(pno => pieceno,
                                   set_count => set_count,
                                   set_stamp => set_stamp,
                                   format => fmt, 
                                   dupflg => 0));
  krmicd.writeMsg(8522, to_char(dfnumber, 'FM09999'), fname);
>>>

#
# proxy_budc: proxy backup datafilecopy name
#
define proxy_budc
<<<
-- proxy_budc
  fmt := NULL;
  &1& -- dfnumber, fmt, fname, copy_recid, copy_stamp
  pieceno := pieceno + 1;
  if (first_time) then
     krmicd.writeMsg(8091, krmicd.getChid);
     first_time := FALSE;
  end if;
  sys.dbms_backup_restore.proxyBackupDataFileCopy
    (copy_recid => copy_recid,
     copy_stamp => copy_stamp,
     handle => krmicd.genPieceName(pno => pieceno,
                                   set_count => set_count,
                                   set_stamp => set_stamp,
                                   format => fmt, 
                                   dupflg => 0));
  krmicd.writeMsg(8033, krmicd.getChid, to_char(dfnumber, 'FM09999'));
  krmicd.writeMsg(8506, fname);
>>>

#
# proxy_bucf: proxy backup controlfile name.  Appears at most 1 time in
# a step.  It adds a specified backup controlfile name to the proxy
# conversation, or the snapshot controlfile if cfname is null.
#
define proxy_bucf
<<<
-- proxy_bucf
  fname := NULL;
  fmt := NULL;
  &1& -- fname, fmt, 
  pieceno := pieceno + 1;
  if (first_time) then
     krmicd.writeMsg(8091, krmicd.getChid);
     first_time := FALSE;
  end if;
  -- Refresh the snapshot controlfile so that it is reasonably current
  -- before backing it up.  This is necessary because it is possible
  -- that the snapshot controlfile SCN is zero, indicating that its
  -- contents are not valid.
  busy_retries := 0;
  <<snapshot>>  -- retry on failure to get snapshot enqueue

  begin
    if fname is null then -- backup current controlfile
       sys.dbms_backup_restore.cfileMakeAndUseSnapshot;
       sys.dbms_backup_restore.cfileUseCurrent;
    end if;
    sys.dbms_backup_restore.proxyBackupControlFile
     (name => fname,
      handle => krmicd.genPieceName(pno => pieceno,
                                    set_count => set_count,
                                    set_stamp => set_stamp,
                                    format => fmt, 
                                    dupflg => 0));
  exception
     when sys.dbms_backup_restore.snapshot_enqueue_busy then
       -- retry up to 5 times, waiting an additional 10 seconds between retries
       if busy_retries = 5 then
          krmicd.writeMsg(20029, 'cannot make a snapshot controlfile');
          raise;
       end if;

       busy_retries := busy_retries + 1;
       krmicd.writeMsg(8512);
       krmicd.sleep(10*busy_retries);
       krmicd.clearErrors;
       goto snapshot;
    end;      -- snapshot controlfile stuff

  if fname is null then
     krmicd.writeMsg(8093);
  else
     krmicd.writeMsg(8524, fname);
  end if;
>>>

#
# proxy_restore_start: restore datafiles/controlfiles.  This is the first
# skeleton in the step.
#
define proxy_restore_start
<<<
-- proxy_restore_start
declare
  cfrepl      boolean;
  handle      varchar2(512);
  dfnumber    number;
  toname      varchar2(512);
  cfname      varchar2(512);
  destination varchar2(512) := null;
  first_time  boolean := TRUE;
  start_time  date;
  elapsed     number;
  hours       number;
  mins        number;
  secs        number;
  recid       number;
  stamp       number;
  validate    boolean;
  vrc         binary_integer;
  validation_failure exception;
begin
  &1&
  sys.dbms_backup_restore.proxyBeginRestore(destination);
  select sysdate into start_time from x$dual;
  if (validate) then
    krmicd.writeMsg(8100, krmicd.getChid);
  else
    krmicd.writeMsg(8090, krmicd.getChid);
  end if;
>>>

#
# proxy_rsdf - name a datafile to be proxy restored
#
define proxy_rsdf
<<<
-- proxy_rsdf
  &1& -- handle, dfnumber, toname
  sys.dbms_backup_restore.proxyRestoreDatafile(handle => handle,
                                               file# => dfnumber,
                                               toname => toname);
  if first_time then
     krmicd.writeMsg(8094, krmicd.getChid);
     first_time := FALSE;
  end if;
  krmicd.writeMsg(8523, to_char(dfnumber, 'FM09999'), toname);
  krmicd.writeMsg(8529, handle);
>>>

#
# proxy_rscf - restore the controlfile in a datafile proxy restore conversation
#
define proxy_rscf
<<<
-- proxy_rscf
  &1& -- handle, cfname, cfrepl
  sys.dbms_backup_restore.proxyRestoreControlfile(handle => handle,
                                                  toname => cfname);
  if first_time then
     krmicd.writeMsg(8094, krmicd.getChid);
     first_time := FALSE;
  end if;
  krmicd.writeMsg(8021, krmicd.getChid);
  krmicd.writeMsg(8505, cfname);
  krmicd.writeMsg(8529, handle);
>>>

#
# proxy_val - validate a proxy backup
#
define proxy_val
<<<
-- proxy_val
  &1& -- handle, recid, stamp
  vrc := sys.dbms_backup_restore.proxyValidate(recid, stamp, handle);
  if vrc < sys.dbms_backup_restore.validate_file_different then
    krmicd.writeMsg(8531, krmicd.getChid, handle, 'FOUND');
  else
    krmicd.writeMsg(8531, krmicd.getChid, handle, 'NOT FOUND');
    raise validation_failure;
  end if;
>>>

#
# proxy_go: do the proxy backup.  Appears exactly once.
#
define proxy_go
<<<
-- proxy_go
  if not validate then
    sys.dbms_backup_restore.proxyGo;
    -- replicate controlfile if default cf restore
    if cfrepl then
       replicate_controlfile(cfname => cfname);
    end if;
    select sysdate - start_time into elapsed from x$dual;
    dur2time(elapsed, hours, mins, secs);
    krmicd.writemsg(8528, krmicd.getChid,
                    to_char(hours, 'FM09') || ':' ||
                    to_char(mins,  'FM09') || ':' ||
                    to_char(secs,  'FM09'));
  end if;
>>>

#
# proxy_end: end the proxy conversation
#
define proxy_end
<<<
  if validate then
    krmicd.writeMsg(8101, krmicd.getChid);
  end if;
end;
>>>

#
# Clone database skeletons
#
#
# General use skeletons
#   Used in any of the 4 phases of the clone database
#

# Begin of script
define cl_beg
<<<
{
>>>

# Execute arbitrary sql
# &1& indicates the sql statement.  Double quotes inside the sql should be
#     escaped with ^.
define cl_sql
<<<
   sql clone "&1&";
>>>

# Set until clause for restore and for recover
# &1& indicates the scn to which to restore or recover
define cl_unt
<<<
   set until scn &1&;
>>>

# Catalog a datafile into the controlfile
# &1& is the filename of the datafilecopy to catalog
define cl_cat
<<<
   catalog clone datafilecopy &1&;
>>>

# End of command
define cl_cend
<<<
   ;
>>>

# End of script
define cl_end
<<<
}
>>>

# Restore phase skeletons
#   Used only by the restore phase of the clone database
#

# Restore of clone, skip readonly datafiles
define res_cl_res
<<<
   restore clone database
>>>

# Restore of clone, check if readonly datafiles need to be restored
define res_cl_ron
<<<
   restore check readonly clone database
>>>

# Skip clause for restore, used for offline and readonly
# &1& indicates the comma separated list of tablespaces to skip
define res_cl_skp
<<<
   skip tablespace &1&
>>>

# Set newname for datafile
# &1& is the number of the datafile
# &2& is the new name for the datafile
define res_cl_set
<<<
   set newname for datafile &1& to &2&;
>>>

# Switch all datafiles
define res_cl_swi
<<<
   switch clone datafile all;
>>>

# Recover phase skeletons
#   Used only by the recover phase of the clone database
#
# Recover database, skip readonlys
define rec_cl_rec
<<<
   recover clone database
>>>

# Recover database, check readonlys
define rec_cl_ron
<<<
   recover clone database check readonly
>>>

# With noredo
define rec_cl_noredo_comma
<<<
   , noredo
>>>

# With noredo
define rec_cl_noredo
<<<
   noredo
>>>

# End recover
define rec_cl_end
<<<
   ;
>>>

# Open database phase skeletons
#   Used only by the open database phase of the clone database
#
# Dismount database
define opn_cl_beg
<<<
   sql clone 'Alter database dismount';
>>>

# Switch manually each datafile
# &1& is the number of the datafile
# &2& is the name of the datafile to which we are switching
define opn_cl_swi
<<<
   switch clone datafile &1& to datafilecopy &2&;
>>>

# Open database
define opn_cl_end
<<<
   Alter clone database open resetlogs;
>>>

# backup piece  validation for AUTOLOCATE mode
define val_piece
<<<
-- val_piece
declare
   handle	varchar2(512);
   recid        number;
   stamp        number;
   setCount     number;
   setStamp     number;
   pieceNum     number;
   valrc        binary_integer;
begin
   -- We get the args for the dbms_backup_restore call from the environment
   -- via krmicd instead of having krmk compile them into this step.  It is
   -- done this way to save memory by avoiding having lots of copies
   -- of the piece handle in the compilation heap.  We can also easily
   -- change it to validate all pieces in the backup set instead of
   -- just the first one.
   krmicd.getValidatePieceArgs(handle, recid, stamp, setCount, setStamp,
                               pieceNum);
   valrc := sys.dbms_backup_restore.validateBackupPiece(
      recid     => recid,
      stamp     => stamp,
      handle    => handle,
      set_stamp => setStamp,
      set_count => setCount,
      pieceno   => pieceNum,
      params    => NULL);
   valrc := bitand(valrc, sys.dbms_backup_restore.validate_file_different);
   if (valrc = 0) then
      -- Don't print the 8102 message because it is just noise.
      -- krmicd.writeMsg(8102, krmicd.getChid, handle);
      krmicd.setValidateResult;
   else
      -- Don't print the 8103 message because krmqexe() will show the
      -- pieces that no channel could find.
      -- krmicd.writeMsg(8103, krmicd.getChid, handle);
      NULL;
   end if;
end;
>>>

#STOPSTOP <--- this is where krmk.pc stops parsing during initialization

# #########################################################################
#                     Create Recovery Catalog                             #
#                                                                         #
#  If new members are added here, the static array krmkcre_libunits must  #
#  be updated with the new member names.                                  #
#                                                                         #
#  The reason that the views here are created with 'create or replace'    #
#  rather than just 'create' is that some of these view libmems are also  #
#  used to upgrade the recovery catalog, and we always want to create the #
#  most current view.                                                     #
#                                                                         #
# #########################################################################
#    NAME
#      catrman.sql
#    DESCRIPTION
#      Create Recovery Catalog tables and views
#
#      Recovery Catalog MUST NOT be created under SYS (INTERNAL).
#      The owner must be granted RECOVERY_CATALOG_OWNER role or corresponding
#      privileges and sufficient quota on the default tablespace.
#
#      create user rman identified by xxxxxx 
#        default tablespace tbsnnn quota unlimited on tbsnnn;
#      grant recovery_catalog_owner to rman;
#      connect rman/xxxxxx
#      @catrman 
#
#      Accessing the recovery catalog tables directly is not supported.
#
#    NOTES
#
#      Remember to update catnormn.sql when adding new tables or views
#
#    MODIFIED   (MM/DD/YY)
#    swerthei    06/03/98 - add media_pool to physical media tables
#    swerthei    05/16/98 - add proxy copy tables and views
#    fsanchez    03/28/98 - Duplexed backup sets
#    dbeusee     04/06/98 - xcheck enh.
#    dbeusee     03/13/98 - Fix bug 624305 (don't harm existing catalog).
#    gpongrac    01/20/98 - allow nulls in dfatt for fname and blocks
#    swerthei    11/11/97 - bug 585165: make df.read_only NOT NULL
#    swerthei    08/18/97 - add stop_change#, read_only columns to rc_datafil
#    gpongrac    07/01/97 - track offline clean and read only scn
#    tpystyne    04/26/97 - add ts_u2 constraint
#    dalpern     04/16/97 - renamed prvtrman to prvtrmnu - "user" v. "sys"
#    gpongrac    03/31/97 - fix typo
#    gpongrac    03/31/97 - change comments about ckp.cf_create_time
#    gpongrac    03/31/97 - change comments about dbinc.cf_create_time
#    gpongrac    03/31/97 - change index on offr
#    gpongrac    03/31/97 - add cf_create_time to offr
#    tpystyne    03/20/97 - bug 465098: remove comments that choke sqlplus
#    gpongrac    03/05/97 - add backup set recid and stamp to rc_backup_dataf
#    gpongrac    02/20/97 - add completion time to bdf and rc_backup_datafile
#    tpystyne    01/10/97 - allow null next_time in al
#    swerthei    12/10/96 - add backup piece/set time columns
#    tpystyne    12/16/96 - move clone_fname to df
#    tpystyne    12/13/96 - add parent_dbinc_key
#    tpystyne    12/09/96 - code review changes
#    gpongrac    11/15/96 - add comments about ckp_u1 constraint
#    gpongrac    11/12/96 - add ckp_type to ckp_u1 constraint
#    gpongrac    11/08/96 - change ckp_u1
#    swerthei    11/08/96 - remove NOT NULL constraint from ts.create_time
#    tpystyne    11/11/96 - add stored script views
#    gpongrac    11/04/96 - add high_df_recid to ckp table
#    gpongrac    11/01/96 - keep low and next times in al
#    swerthei    10/31/96 - remove df.unrecoverable_scn
#    gpongrac    10/24/96 - remove bloblocksize from  rc_log_history
#    swerthei    10/23/96 - add cdf.completion_time, al.completion_time
#    gpongrac    10/23/96 - move blocksize to al table
#    tpystyne    10/21/96 - change JOB to PARTIAL
#    tpystyne    10/20/96 - make device_type not null
#    tpystyne    10/13/96 - fix rc_ views
#    gpongrac    10/07/96 - have rc_datafile return blocksize
#    tpystyne    10/03/96 - add next_scn to al
#    tpystyne    09/05/96 - add online redo log support
#    gpongrac    08/22/96 - use @@ not @
#    tpystyne    08/26/96 - add ckp_time to ccf and bcf
#    gpongrac    08/16/96 - add unique constraint to scr table
#    gpongrac    08/13/96 - stored script support
#    tpystyne    07/24/96 - add incr_level to cdf
#    bhomsi      07/16/96 - A new ref log needed for tkrmrman.tsc if update
#    gpongrac    07/03/96 - install the packages too
#    tpystyne    07/05/96 - remove bcf_offr and ccf_offr
#    tpystyne    07/01/96 - implement offline range resync
#    tpystyne    06/19/96 - remove unused columns from rc_tablespace
#    tpystyne    05/30/96 - allow null dates
#    tpystyne    05/20/96 - add indexes to tune dbms_rcvman
#    tpystyne    04/26/96 - allow duplicate thread# and sequence# numbers
#    tpystyne    03/14/96 - restructure primary and foreign keys
#    tpystyne    02/20/96 - fix views again
#    tpystyne    02/12/96 - add fname_hash columns
#    tpystyne    01/31/96 - update for set_stamp and set_count
#    tpystyne    01/18/96 - fix view definitions
#    gpongrac    01/10/96 - drop domain col from dbinc table
#    tpystyne    12/06/95 - more fixes
#    tpystyne    12/01/95 - remove incremental backup SCNs
#    tpystyne    11/14/95 - add new tables
#    tpystyne    11/01/95 - incorporate design changes
#    tpystyne    10/18/95 - add rt and rlh tables
#    tpystyne    10/13/95 - add rcvcat views
#    tpystyne    10/09/95 - name constraints
#    tpystyne    10/03/95 - fix backup set tables
#    tpystyne    10/02/95 - replace rbs with tsatt
#    gpongrac    09/26/95 - move device_type from bs to bp
#    tpystyne    09/26/95 - add db_key, recid and stamp columns to bs
#                         - add unique keys
#    gpongrac    09/25/95 - add block count columns to bdf and brl
#    gpongrac    09/25/95 - add devtype column to bs table
#    gpongrac    09/22/95 - change col type for ts.ts_name from char(30) to v
#    gpongrac    09/21/95 - add creation SCN and recovery SCN to table ts
#                         - change offr and bdf tables too
#    tpystyne    09/20/95 - incorporate design changes
#    gpongrac    08/08/95 - add rbs (rollback segments and stuff)
#    gpongrac    07/17/95 - merge
#    gpongrac    07/17/95 - deal with offline clean and readonly correctly
#    gpongrac    07/17/95 - merge
#    gpongrac    06/07/95 - recovery catalog DDL
#    gpongrac    04/14/95 - Created

# character set of the recovery catalog database should be a superset
# all target database character sets. This is important if tablespace,
# or tag names contain non-ASCII characters.

# DB table contains all target databases that have been registered in
# recovery catalog. The unique key constraint on db_id prevents clients
# registering the same database twice. 

define db
<<<
CREATE TABLE db
(
db_key          NUMBER NOT NULL,        -- sequence generated primary key
db_id           NUMBER NOT NULL,        -- kccfhdbi from controlfile
-- updatable columns
curr_dbinc_key  NUMBER,                 -- current incarnation
CONSTRAINT db_p  PRIMARY KEY (db_key),
CONSTRAINT db_u1 UNIQUE(db_id)          -- ensure that db_id is unique
) &tablespace&
>>>

# primary key generator
define rman_seq
<<<
CREATE SEQUENCE rman_seq
>>>


# the DBINC table contains all incarnations of the target databases registered
# in this recovery catalog. The unique key constraint on db_key, reset_scn and
# reset_time prevents clients from registering the same incarnation twice.
# 
# The high recid columns for circular-reuse type records keep track of the
# records propagated from controlfile to recovery catalog. Only new records
# with recids higher than the corresponding high recid needs to be propagated
# by the next resync.

define dbinc
<<<
CREATE TABLE dbinc
(
dbinc_key       NUMBER NOT NULL,        -- sequence generated primary key
db_key          NUMBER NOT NULL,        -- database to which this incarnation
                                        -- belongs
db_name         VARCHAR2(8) NOT NULL,   -- current db_name
reset_scn       NUMBER NOT NULL,        -- SCN of last resetlogs
reset_time      DATE NOT NULL,          -- timestamp of last resetlogs
parent_dbinc_key NUMBER,                -- parent incarnation
-- updatable columns
cf_create_time  DATE,                      -- cf version_time at last resync
                                           -- This was controlfile_created in
                                           -- 8.0.2, now is version_time.
ckp_scn         NUMBER DEFAULT 0 NOT NULL, -- cf ckp scn at last full resync
full_ckp_cf_seq NUMBER DEFAULT 0 NOT NULL, -- cf seq at last full resync
job_ckp_cf_seq  NUMBER DEFAULT 0 NOT NULL, -- cf seq at last partial resync
high_ts_recid   NUMBER,                    -- tablespace recid
high_df_recid   NUMBER,                    -- datafile recid
high_rt_recid   NUMBER,                    -- redo thread recid
high_orl_recid  NUMBER,                    -- online redo log recid
high_offr_recid NUMBER DEFAULT 0 NOT NULL, -- offline range (kkor) recid
high_rlh_recid  NUMBER DEFAULT 0 NOT NULL, -- log history (kcclh) recid
high_al_recid   NUMBER DEFAULT 0 NOT NULL, -- archived log (kccal) recid
high_bs_recid   NUMBER DEFAULT 0 NOT NULL, -- backup set (kccbs) recid
high_bp_recid   NUMBER DEFAULT 0 NOT NULL, -- backup piece (kccbp) recid
high_bdf_recid  NUMBER DEFAULT 0 NOT NULL, -- backup datafile (kccbf) recid
high_cdf_recid  NUMBER DEFAULT 0 NOT NULL, -- datafile copy (kccdc) recid
high_brl_recid  NUMBER DEFAULT 0 NOT NULL, -- backup redo log (kccbl) recid
high_bcb_recid  NUMBER DEFAULT 0 NOT NULL, -- backup datafile corruption recid
high_ccb_recid  NUMBER DEFAULT 0 NOT NULL, -- datafile copy corruption recid
high_do_recid   NUMBER DEFAULT 0 NOT NULL, -- deleted object recid
high_pc_recid   NUMBER DEFAULT 0 NOT NULL, -- proxy copy (kccpc) recid
CONSTRAINT dbinc_p  PRIMARY KEY(dbinc_key),
CONSTRAINT dbinc_u1 UNIQUE (db_key, reset_scn, reset_time),
CONSTRAINT dbinc_f1 FOREIGN KEY(db_key)
  REFERENCES db ON DELETE CASCADE
) &tablespace&
>>>


# ensure that current database incarnation exists in dbinc
define db_f1
<<<
ALTER TABLE db ADD 
CONSTRAINT db_f1 FOREIGN KEY(curr_dbinc_key) REFERENCES dbinc
>>>

# the CKP table records all recovery catalog checkpoints.
# Every complete recovery catalog resync will take snapshot of the target
# database controlfile and resync the recovery catalog from the snapshot
# control file. A checkpoint record is insert into the CKP to indicate that
# a full resync was done. The unique constraint ckp_u1
# ensures that the controlfile changes between two resyncs.

define ckp
<<<
CREATE TABLE ckp
(
ckp_key         NUMBER NOT NULL,        -- primary key
ckp_scn         NUMBER NOT NULL,        -- controlfile checkpoint scn
ckp_time        DATE,                   -- controlfile checkpoint timestamp
ckp_cf_seq      NUMBER NOT NULL,        -- controlfile sequence
cf_create_time  DATE NOT NULL,          -- controlfile version_time
                                        -- This was controlfile_created in
                                        -- 8.0.2, now is version_time.
dbinc_key       NUMBER NOT NULL,        -- database incarnation
ckp_type        VARCHAR2(7) NOT NULL,   -- resync type, 'FULL' or 'PARTIAL'
ckp_db_status   VARCHAR2(7),            -- 'OPEN' or 'MOUNTED'
resync_time     DATE NOT NULL,          -- resync time
prev_ckp_key    NUMBER,                 -- previous checkpoint
high_df_recid   NUMBER NOT NULL,        -- datafile recid at ckp_scn
CONSTRAINT ckp_p  PRIMARY KEY (ckp_key),
-- If you change ckp_u1, then the query in dbms_rcvman.getCheckpoint
-- may need revisions to its where clause.  That query must always
-- be a single-row query.
CONSTRAINT ckp_u1 UNIQUE (dbinc_key, ckp_scn, ckp_type, ckp_cf_seq, 
                          cf_create_time),
CONSTRAINT ckp_f1 FOREIGN KEY (dbinc_key) -- checkpoint belongs to a dbinc
  REFERENCES dbinc ON DELETE CASCADE,
CONSTRAINT ckp_f2 FOREIGN KEY (prev_ckp_key) -- previous checkpoint for dbinc
  REFERENCES ckp,
CONSTRAINT ckp_c_type CHECK (ckp_type in ('FULL', 'PARTIAL'))
) &tablespace&
>>>

# the TS table contains all tablespaces of all database incarnations.
# Note that the same tablespace ts# and name may exist multiple times in
# a database incarnation. This will happen if a tablespace is dropped and
# recreated. The unique key constraint on dbinc_key, ts# and drop_scn
# ensures that only one of them is current (drop_scn is null).

# The drop_scn is calculated when a resync notices that the tablespace is
# is dropped. If a new tablespace with the same ts# is seen then the drop_scn
# is set to the create_scn of the tablespace. Otherwise the drop_scn
# is set to the ckp_scn. Note that the tablespace never exists at drop_scn,
# it is always dropped before the drop_scn and drop_time.

# Note that the create_time is null for offline tablespaces after
# creating the controlfile.

define ts
<<<
CREATE TABLE ts
(
dbinc_key       NUMBER NOT NULL,        -- database incarnation
ts#             NUMBER NOT NULL,        -- tablespace id in target db
ts_name         VARCHAR2(30) NOT NULL,  -- tablespace name
create_scn      NUMBER NOT NULL,        -- creation SCN (from 1st datafile)
create_time     DATE,                   -- creation time
-- updatable columns
drop_scn        NUMBER,                 -- drop SCN (as calculated)
                                        -- NULL if tablespace exists
drop_time       DATE,                   -- drop time
CONSTRAINT ts_p  PRIMARY KEY (dbinc_key, ts#, create_scn),
CONSTRAINT ts_u1 UNIQUE (dbinc_key, ts#, drop_scn),
CONSTRAINT ts_u2 UNIQUE (dbinc_key, ts_name, create_scn),
CONSTRAINT ts_f1 FOREIGN KEY (dbinc_key)
  REFERENCES dbinc ON DELETE CASCADE
) &tablespace&
>>>

# tablespace attributes that change over time
define tsatt
<<<
CREATE TABLE tsatt
(
dbinc_key       NUMBER NOT NULL,        -- database incarnation
ts#             NUMBER NOT NULL,        -- tablespace id in target db
create_scn      NUMBER NOT NULL,        -- creation SCN
start_ckp_key   NUMBER NOT NULL,        -- ckp when first observed
end_ckp_key     NUMBER,                 -- ckp when changed; NULL->current
rbs_count       NUMBER,                 -- number of rollback segs in this ts
                                        -- NULL -> unknown
CONSTRAINT tsatt_u1 UNIQUE (dbinc_key, ts#, create_scn, end_ckp_key),
CONSTRAINT tsatt_f1 FOREIGN KEY (dbinc_key, ts#, create_scn)
  REFERENCES ts ON DELETE CASCADE,
CONSTRAINT tsatt_f2 FOREIGN KEY (start_ckp_key)
  REFERENCES ckp,
CONSTRAINT tsatt_f3 FOREIGN KEY (end_ckp_key)
  REFERENCES ckp
) &tablespace&
>>>

# the DF table contains all datafiles of all database incarnations.
# Note that the same datafile file# may exists multiple times in a database
# incarnation. This may happen if the datafile is dropped (with its 
# tablespace) and a new datafile gets the old file#. The unique key on
# ts_key and file# ensures that the same file# is not used for the same
# tablespace (since datafiles can only be dropped with their tablespaces)
# The file# and create_scn combination must also be unique for a database
# incarnation.

# The drop_scn is calculated when a resync notices that a datafile has
# disappeared from the controlfile of the target database. If a new file
# with the same file# (and higher create_scn) is seen then the drop_scn is
# set to the create_scn of the new file. Otherwise the drop_scn
# is set to the ckp_scn. This calculation guarantees that two datafiles
# with the same file# never appear to have existed at the same point-in-time
# (or SCN). This is important since the query "which datafiles existed at 
# SCN X" should never return two files with the same file#.

define df
<<<
CREATE TABLE df
(
dbinc_key       NUMBER NOT NULL,        -- database incarnation
file#           NUMBER NOT NULL,        -- database file number
create_scn      NUMBER NOT NULL,        -- creation SCN
create_time     DATE,                   -- creation timestamp
ts#             NUMBER NOT NULL,        -- tablespace id in target db
ts_create_scn   NUMBER NOT NULL,        -- tablespace creation SCN
block_size      NUMBER NOT NULL,        -- blocksize 
-- updatable columns
clone_fname     VARCHAR2(1024),         -- clone datafile name
drop_scn        NUMBER,                 -- drop SCN (as calculated)
                                        -- NULL if datafile exists
drop_time       DATE,                   -- drop time
stop_scn        NUMBER,                 -- offline clean or read only scn
stop_time       DATE,			-- timestamp for above SCN
read_only       NUMBER NOT NULL,        -- 1 if stop_scn is read only, else 0  
CONSTRAINT df_p  PRIMARY KEY (dbinc_key, file#, create_scn),
CONSTRAINT df_u1 UNIQUE (dbinc_key, file#, drop_scn),
CONSTRAINT df_u2 UNIQUE (dbinc_key, ts#, ts_create_scn, file#),
-- file belongs to a tablespace
CONSTRAINT df_f1 FOREIGN KEY (dbinc_key, ts#, ts_create_scn)   
  REFERENCES ts ON DELETE CASCADE
) &tablespace&
>>>

# datafile attributes that change over time
define dfatt
<<<
CREATE TABLE dfatt
(
dbinc_key       NUMBER NOT NULL,        -- database incarnation
file#           NUMBER NOT NULL,        -- database file number
create_scn      NUMBER NOT NULL,        -- creation SCN
start_ckp_key   NUMBER NOT NULL,        -- ckp when first observed
end_ckp_key     NUMBER,                 -- ckp when changed; NULL->current
fname           VARCHAR2(1024),         -- datafile name
blocks          NUMBER,                 -- datafile size
CONSTRAINT dfatt_u1 UNIQUE (dbinc_key, file#, create_scn, end_ckp_key),
CONSTRAINT dfatt_f1 FOREIGN KEY (dbinc_key, file#, create_scn)
  REFERENCES df ON DELETE CASCADE,
CONSTRAINT dfatt_f2 FOREIGN KEY (start_ckp_key)
  REFERENCES ckp,
CONSTRAINT dfatt_f3 FOREIGN KEY (end_ckp_key)
  REFERENCES ckp
) &tablespace&
>>>

# The OFFR table stores datafile offline ranges
# Note that datafile ranges are stored in two places in the control file.
# The most recent offline range (if any) of each datafile is kept in the
# datafile record itself and previous ranges are stored in the offline range
# records. In the recovery catalog all offline ranges are recorded in the
# OFFR table

# The resync will first read datafile records and resync the most 
# recent offline ranges. Note that the offr_recid and offr_stamp are null
# until the offline range is moved to the offline range record.
# Then it will read all offline range records with recid higher than the 
# highwater mark and resync them. If the offline range is already in the
# recovery catalog, its recid and stamp are updated in case they are null.

# The unique key on dbinc_key, file# and offline_scn ensure that the same
# offline range can't be entered twice. An offline range range belongs to
# the database incarnation that was current at the end (online scn)
# of the range.
# In order to allow multiple tspitr to the same pit creation_scn is added
# to the unique index, otherwise duplicated value on index and RMAN-20087
# (bug 607271)

define offr
<<<
CREATE TABLE offr
(
offr_key        NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
offr_recid      NUMBER,                 -- offline range recid
offr_stamp      NUMBER,                 -- offline range stamp
file#           NUMBER NOT NULL,        -- datafile number
create_scn      NUMBER NOT NULL,        -- datafile incarnation
offline_scn     NUMBER NOT NULL,        -- scn at datafile was taken offline
online_scn      NUMBER NOT NULL,        -- online checkpoint SCN
online_time     DATE NOT NULL,          -- online checkpoint time
cf_create_time  DATE,                   -- controlfile creation time
CONSTRAINT offr_p  PRIMARY KEY (offr_key),
CONSTRAINT offr_u1 UNIQUE (dbinc_key, offr_recid, offr_stamp),
CONSTRAINT offr_u2 UNIQUE (dbinc_key, file#, create_scn, offline_scn, 
                           cf_create_time),
CONSTRAINT offr_f1 FOREIGN KEY (dbinc_key)
  REFERENCES dbinc ON DELETE CASCADE
) &tablespace&
>>>

# The RR table contains redo ranges for all database incarnations
# A redo range is a continuous sequence of redo log history records
# propagated to the recovery catalog. A new redo range is started when
# recovery catalog resync detects a gap in redo log history. This will
# happen when the control file is recreated or when a redo history record
# is overwritten in the controlfile before it is propagated to the
# recovery catalog.  

define rr
<<<
CREATE TABLE rr
(
rr_key          NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
low_scn         NUMBER NOT NULL,        -- low SCN of the range
high_scn        NUMBER NOT NULL,        -- high SCN of the range
CONSTRAINT rr_p  PRIMARY KEY (rr_key),
CONSTRAINT rr_f1 FOREIGN KEY (dbinc_key)
  REFERENCES dbinc ON DELETE CASCADE
) &tablespace&
>>>

# the RT table contains all redo threads for all database incarnations.
# Although threads cannot be dropped they may appear dropped if the
# controlfile of the target database is recreated.

define rt
<<<
CREATE TABLE rt
(
dbinc_key       NUMBER NOT NULL,        -- database incarnation
thread#         NUMBER NOT NULL,        -- thread number
-- updatable columns
sequence#       NUMBER NOT NULL,        -- last log sequence number allocated
enable_scn      NUMBER,                 -- SCN of last enable
enable_time     DATE,                   -- timestamp of last enable
disable_scn     NUMBER,                 -- SCN of last disable
disable_time    DATE,                   -- timestamp of last disable
status          VARCHAR2(1) NOT NULL,   -- 'D' -> disabled
                                        -- 'E' -> enabled
                                        -- 'O' -> open
CONSTRAINT rt_p  PRIMARY KEY (dbinc_key, thread#),
CONSTRAINT rt_f1 FOREIGN KEY (dbinc_key)
  REFERENCES dbinc ON DELETE CASCADE,
CONSTRAINT rt_c_status CHECK (status in ('D','E','O'))
) &tablespace&
>>>

# the ORL table contains all redo logfiles for all database incarnations.
define orl
<<<
CREATE TABLE orl
(
dbinc_key       NUMBER NOT NULL,        -- database incarnation
thread#         NUMBER NOT NULL,        -- thread number
group#          NUMBER NOT NULL,        -- group number
fname           VARCHAR2(1024) NOT NULL, -- datafile name
--CONSTRAINT orl_p  PRIMARY KEY (dbinc_key, fname), -- key is too long
CONSTRAINT orl_f1 FOREIGN KEY (dbinc_key, thread#)
  REFERENCES rt ON DELETE CASCADE
) &tablespace&
>>>

define orl_i_1
<<<
CREATE INDEX orl_i_1 on orl(dbinc_key, thread#, group#) &tablespace&
>>>

# the RLH table records all redo log history for all threads.
# The redo log history entries in the control file are circularly reused,
# so it is important to resync often enough to propagate records to
# recovery catalog before they are reused in the controlfile

define rlh
<<<
CREATE TABLE rlh
(
rlh_key         NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
rlh_recid       NUMBER NOT NULL,        -- log history recid from control file
rlh_stamp       NUMBER NOT NULL,        -- log history stamp from control file
thread#         NUMBER NOT NULL,        -- thread number
sequence#       NUMBER NOT NULL,        -- log sequence number
low_scn         NUMBER NOT NULL,        -- scn generated when switching in
low_time        DATE NOT NULL,
next_scn        NUMBER NOT NULL,        -- scn generated when switching out
status          VARCHAR2(1),            -- 'C' -> cleared 
CONSTRAINT rlh_p  PRIMARY KEY (rlh_key),
CONSTRAINT rlh_u1 UNIQUE (dbinc_key, thread#, sequence#, low_scn),
CONSTRAINT rlh_u2 UNIQUE (dbinc_key, rlh_recid, rlh_stamp),
CONSTRAINT rlh_f1 FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE,
CONSTRAINT rlh_c_status CHECK (status in ('C'))
) &tablespace&
>>>

# The AL table contains archived logs
# It corresponds to the V$ARCHIVED_LOG fixed view in
# in the control file. The archived logs are uniquely identified by dbinc_key,
# recid and stamp.

# Note that it is possible to have an archived log without the corresponding
# log history record. This can happen if the log history record in the
# controlfile is written over or after resetlogs which clears the log history.
# Therefore the constraint referencing rlh is commented out. 

define al
<<<
CREATE TABLE al
(
al_key          NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
al_recid        NUMBER NOT NULL,        -- archive log recid from control file
al_stamp        NUMBER NOT NULL,        -- archive log stamp from control file
thread#         NUMBER NOT NULL,        -- thread number
sequence#       NUMBER NOT NULL,        -- log sequence number
low_scn         NUMBER NOT NULL,        -- scn generated when switching in
low_time        DATE   NOT NULL,        -- time low SCN allocated
next_scn        NUMBER NOT NULL,        -- scn generated when switching out
next_time       DATE,                   -- time when next SCN allocated
fname           VARCHAR2(1024),         -- archive log file name,
                                        -- NULL -> log was cleared
fname_hashkey   VARCHAR2(20),           -- hashed fname for indexing
archived        VARCHAR2(1) NOT NULL,   -- 'Y' -> archived log
                                        -- 'N' -> inspected online log
blocks          NUMBER NOT NULL,        -- number of blocks written
block_size      NUMBER NOT NULL,        -- size of a block in bytes
completion_time DATE NOT NULL,          -- time the log was archived or copied
-- updatable columns
status          VARCHAR2(1) NOT NULL,   -- 'A' -> available, 'U' -> unavailable
CONSTRAINT al_p  PRIMARY KEY (al_key),
CONSTRAINT al_u1 UNIQUE (dbinc_key, al_recid, al_stamp),
CONSTRAINT al_f1 FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE,
CONSTRAINT al_c_archived CHECK (archived in ('Y','N')),
CONSTRAINT al_c_status CHECK (status in ('A','U','D'))
) &tablespace&
>>>

define al_i_fname_status
<<<
CREATE INDEX al_i_fname_status on al(fname_hashkey, status) &tablespace&
>>>

define al_i_2
<<<
CREATE INDEX al_i_2 on al(dbinc_key, thread#, sequence#, low_scn) &tablespace&
>>>

# the BS table contains all backup sets for all database incarnations
# The unique key on db_key, set_stamp and set_count ensures that a backup
# set can only be inserted once into the recovery catalog.
# A "place-holder" bs record is created when checkBackupPiece finds a kccbp
# record whose parent kccbs record has already aged out of the controlfile.
# In this case, the following colums will be null:
# bs_recid, bs_stamp, bck_type, incr_level, start_time, completion_time,
# status.

define bs
<<<
CREATE TABLE bs
(
bs_key          NUMBER NOT NULL,        -- sequence generated primary key
db_key          NUMBER NOT NULL,        -- database
bs_recid        NUMBER,                 -- backup set recid from control file
bs_stamp        NUMBER,                 -- backup set stamp from control file
set_stamp       NUMBER NOT NULL,        -- set_stamp from control file
set_count       NUMBER NOT NULL,        -- set_count from control file
bck_type        VARCHAR2(1),            -- 'D' -> full datafile
                                        -- 'I' -> incremental datafile
                                        -- 'L' -> archivelog
incr_level      NUMBER,                 -- incremental backup level (0 - 4)
					-- null if this a FULL backupset
pieces          NUMBER NOT NULL,        -- number of backup pieces in the set
start_time      DATE,                   -- time when this backup started
completion_time DATE,                   -- time when this backup completed
status          VARCHAR2(1),            -- 'A' -> complete set of pieces avail
					-- 'D' -> no pieces or all deleted
					-- 'O' -> neither of above
CONSTRAINT bs_p  PRIMARY KEY (bs_key),
CONSTRAINT bs_u2 UNIQUE (db_key, set_stamp, set_count),
CONSTRAINT bs_f1 FOREIGN KEY(db_key)    -- backup set belongs to a db
  REFERENCES db ON DELETE CASCADE,
CONSTRAINT bs_c_bck_type CHECK (bck_type in ('D','I','L')),
CONSTRAINT bs_c_incr_level CHECK (incr_level in (0,1,2,3,4))
-- note:  incr_level can be null.  CHECK constraints allow this.
) &tablespace&
>>>

# the BP table contains all backup pieces of backup sets.
# The unique key on bs_key, bp_recid and bp_stamp ensures that the same piece
# can't be entered twice in the recovery catalog. Note that there may be
# multiple copies of of the same piece, but each copy will have its own row
# in the BP table (and its own backup piece record in the control file).
# Do not create a unique key constraint on bs_key and piece#.
 
define bp
<<<
CREATE TABLE bp
(
bp_key          NUMBER NOT NULL,        -- sequence generated primary key
bs_key          NUMBER NOT NULL,        -- backup set
db_key		NUMBER NOT NULL,
bp_recid	NUMBER NOT NULL,	-- backup piece recid from control file
bp_stamp	NUMBER NOT NULL,	-- backup piece stamp from control file
piece#		NUMBER NOT NULL,	-- first piece is 1
copy#           NUMBER NOT NULL,        -- copy number, 1 if no copies
tag		VARCHAR2(32),		-- user specified tag
device_type	VARCHAR2(255) NOT NULL,	-- 'DISK' -> on disk rather than seq
handle		VARCHAR2(1024) NOT NULL, --backup piece handle
handle_hashkey  VARCHAR2(30) NOT NULL, 	-- indexed hashkey on handle
comments	VARCHAR2(255),		-- 
media		VARCHAR2(80),		-- media handle
media_pool      NUMBER,                 -- media pool
concur		VARCHAR2(1) NOT NULL,	-- 'Y' media supports concurrent access
start_time      DATE NOT NULL,          -- time when this piece started
completion_time DATE NOT NULL,          -- time when this piece completed
-- updatable columns
status          VARCHAR2(1) NOT NULL,   -- 'A' -> available, 'U' -> unavailable
CONSTRAINT bp_p  PRIMARY KEY (bp_key),
CONSTRAINT bp_u1 UNIQUE (bs_key, bp_recid, bp_stamp),
CONSTRAINT bp_f1 FOREIGN KEY (bs_key)
  REFERENCES bs ON DELETE CASCADE,
CONSTRAINT bp_c_status CHECK (status in ('A','U','D','X')),
CONSTRAINT bp_c_concur CHECK (concur in ('Y','N'))
) &tablespace&
>>>

define bp_i_device_handle_status
<<<
CREATE INDEX bp_i_device_handle_status on bp(handle_hashkey, status)
&tablespace&
>>>

# The BCF table contains control file backups (in backup sets)
# Note that a backup datafile record with file# 0 is used to represent the 
# backup control file in the V$BACKUP_DATAFILE view.

define bcf
<<<
CREATE TABLE bcf
(
bcf_key         NUMBER NOT NULL,
bs_key          NUMBER NOT NULL,        -- backup set
dbinc_key       NUMBER NOT NULL,        -- database incarnation
bcf_recid       NUMBER NOT NULL,        -- recid from control file
bcf_stamp       NUMBER NOT NULL,        -- stamp from control file
ckp_scn         NUMBER NOT NULL,        -- controlfile checkpoint SCN
ckp_time        DATE NOT NULL,          -- controlfile checkpoint time
create_time     DATE NOT NULL,          -- controlfile creation time
min_offr_recid  NUMBER NOT NULL,        -- recid of the oldest offline range
block_size      NUMBER NOT NULL,        -- blocksize
CONSTRAINT bcf_p  PRIMARY KEY (bcf_key),
CONSTRAINT bcf_u1 UNIQUE (dbinc_key, bcf_recid, bcf_stamp),
CONSTRAINT bcf_u2 UNIQUE (bs_key),
CONSTRAINT bcf_f1 FOREIGN KEY (bs_key)
  REFERENCES bs ON DELETE CASCADE
) &tablespace&
>>>

# The CCF table contains control file copies
# Note that a datafile copy record with file# 0 is used to represent the 
# control file copy in the V$DATAFILE_COPY view.

define ccf
<<<
CREATE TABLE ccf
(
ccf_key         NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
ccf_recid       NUMBER NOT NULL,        -- recid from control file
ccf_stamp       NUMBER NOT NULL,        -- stamp from control file
fname           VARCHAR2(1024) NOT NULL, -- cf copy file name
fname_hashkey   VARCHAR2(20) NOT NULL,  -- hashed fname for indexing
tag             VARCHAR2(32),           -- cf copy tag
ckp_scn         NUMBER NOT NULL,        -- controlfile checkpoint SCN
ckp_time        DATE NOT NULL,          -- controlfile checkpoint time
create_time     DATE NOT NULL,          -- controlfile creation time
min_offr_recid  NUMBER NOT NULL,        -- recid of the oldest offline range
block_size      NUMBER NOT NULL,        -- blocksize
completion_time DATE NOT NULL,          -- time that the copy was taken
-- updatable columns
status          VARCHAR2(1) NOT NULL,   -- 'A' -> available, 'U' -> unavailable
CONSTRAINT ccf_p  PRIMARY KEY (ccf_key),
CONSTRAINT ccf_f1 FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE,
CONSTRAINT ccf_u1 UNIQUE (dbinc_key, ccf_recid, ccf_stamp),
CONSTRAINT ccf_c_status CHECK (status in ('A','U','D'))
) &tablespace&
>>>

define ccf_i_fname_status
<<<
CREATE INDEX ccf_i_fname_status on ccf(fname_hashkey, status) &tablespace&
>>>

# The XCF table contains control file proxy copies Note that a datafile
# proxy copy record with file# 0 is used to represent the control file
# copy in the V$PROXY_DATAFILE view.

define xcf
<<<
CREATE TABLE xcf
(
xcf_key         NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
xcf_recid       NUMBER NOT NULL,        -- recid from control file
xcf_stamp       NUMBER NOT NULL,        -- stamp from control file
tag             VARCHAR2(32),           -- cf copy tag
ckp_scn         NUMBER NOT NULL,        -- controlfile checkpoint SCN
ckp_time        DATE NOT NULL,          -- controlfile checkpoint time
create_time     DATE NOT NULL,          -- controlfile creation time
min_offr_recid  NUMBER NOT NULL,        -- recid of the oldest offline range
block_size      NUMBER NOT NULL,        -- blocksize
device_type     VARCHAR2(255) NOT NULL, -- 'DISK' -> on disk rather than seq
handle          VARCHAR2(1024) NOT NULL, -- backup piece handle
handle_hashkey  VARCHAR2(30) NOT NULL,  -- indexed hashkey on handle
comments        VARCHAR2(255),          -- 
media           VARCHAR2(80),           -- media handle
media_pool      NUMBER,                 -- media pool
start_time      DATE NOT NULL,          -- time when this piece started
completion_time DATE NOT NULL,          -- time when this piece completed
-- updatable columns
status          VARCHAR2(1) NOT NULL,
CONSTRAINT xcf_p  PRIMARY KEY (xcf_key),
CONSTRAINT xcf_f1 FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE,
CONSTRAINT xcf_u1 UNIQUE (dbinc_key, xcf_recid, xcf_stamp),
CONSTRAINT xcf_c_status CHECK (status in ('A','U','D','X'))
) &tablespace&
>>>

define xcf_i_handle_status
<<<
CREATE INDEX xcf_i_handle_status on xcf(handle_hashkey, status) &tablespace&
>>>

# the BDF table contains all datafile backups (in backup sets). 
# A backup datafile is uniquely identified by bs_key, bdf_recid and bdf_stamp.

define bdf
<<<
CREATE TABLE bdf
(
bdf_key         NUMBER NOT NULL,        -- primary key
dbinc_key       NUMBER NOT NULL,        -- database incarnation
bdf_recid       NUMBER NOT NULL,        -- bdf recid from control file
bdf_stamp       NUMBER NOT NULL,        -- bdf stamp from control file
bs_key          NUMBER NOT NULL,        -- backup set, null if copy
file#           NUMBER NOT NULL,        -- database file number
create_scn      NUMBER NOT NULL,        -- creation SCN
incr_level      NUMBER,                 -- incremental backup level (null, 0-4)
incr_scn        NUMBER NOT NULL,        -- scn since backup contains changes
ckp_scn         NUMBER NOT NULL,        -- scn of the last datafile ckpt
ckp_time        DATE NOT NULL,          -- time of the last datafile ckpt
abs_fuzzy_scn   NUMBER,                 -- absolute fuzzy scn
rcv_fuzzy_scn   NUMBER,                 -- media recovery fuzzy scn
rcv_fuzzy_time  DATE,                   -- timestamp for media rcv fuzzy scn
datafile_blocks NUMBER NOT NULL,        -- number of blocks in datafile
blocks          NUMBER NOT NULL,        -- number of blocks written to backup
block_size      NUMBER NOT NULL,        -- blocksize
completion_time DATE,                   -- completion time (null for 8.0.2)
CONSTRAINT bdf_p  PRIMARY KEY (bdf_key),
CONSTRAINT bdf_u1 UNIQUE (dbinc_key, bdf_recid, bdf_stamp),
--CONSTRAINT bdf_u2 UNIQUE (bs_key, file#),
CONSTRAINT bdf_f1 FOREIGN KEY (bs_key)
  REFERENCES bs ON DELETE CASCADE
--CONSTRAINT bdf_f2 FOREIGN KEY (dbinc_key, file#, create_scn)
--  REFERENCES df ON DELETE CASCADE
) &tablespace&
>>>

# CREATE INDEX bdf_i_df_key on bdf(bs_key) &tablespace&

define bdf_i_df_key
<<<
CREATE INDEX bdf_i_df_key on bdf(dbinc_key, file#, create_scn) &tablespace&
>>>

# the CDF table contains all datafile copies. 
# A datafile copy is uniquely identified by dbinc_key, recid and stamp columns

define cdf
<<<
CREATE TABLE cdf
(
cdf_key         NUMBER NOT NULL,        -- primary key
dbinc_key       NUMBER NOT NULL,        -- database incarnation
cdf_recid       NUMBER NOT NULL,        -- df copy recid from control file
cdf_stamp       NUMBER NOT NULL,        -- df copy stamp from control file
file#           NUMBER NOT NULL,        -- database file number
create_scn      NUMBER NOT NULL,        -- creation SCN
fname           VARCHAR2(1024) NOT NULL, -- df copy file name
fname_hashkey   VARCHAR2(20) NOT NULL,  -- hashed fname for indexing
tag             VARCHAR2(32),           -- df copy tag
incr_level      NUMBER,                 -- incremental backup level (null or 0)
ckp_scn         NUMBER NOT NULL,        -- scn of the last datafile ckpt
ckp_time        DATE NOT NULL,          -- time of the last datafile ckpt
onl_fuzzy       VARCHAR2(1) NOT NULL,   -- 'Y' -> online fuzzy
bck_fuzzy       VARCHAR2(1) NOT NULL,   -- 'Y' -> backup fuzzy
abs_fuzzy_scn   NUMBER,                 -- absolute fuzzy scn, if known.
                                        -- null if not known.
rcv_fuzzy_scn   NUMBER,                 -- media recovery fuzzy scn
rcv_fuzzy_time  DATE,                   -- timestamp for media rcv fuzzy scn
blocks          NUMBER NOT NULL,        -- number of blocks
block_size      NUMBER NOT NULL,        -- blocksize
completion_time DATE NOT NULL,          -- time when this copy completed
-- updatable columns
status          VARCHAR2(1) NOT NULL,   -- 'A' -> available, 'U' -> unavailable
CONSTRAINT cdf_p  PRIMARY KEY (cdf_key),
CONSTRAINT cdf_u1 UNIQUE (dbinc_key, cdf_recid, cdf_stamp),
CONSTRAINT cdf_f1 FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE,
-- this constraint can't be enforced since datafile copies are sometimes
-- inserted before the datafiles. 
--CONSTRAINT cdf_f2 FOREIGN KEY (dbinc_key, file#, create_scn)
--  REFERENCES df ON DELETE CASCADE,
CONSTRAINT cdf_c_status CHECK (status in ('A','U','D')),
CONSTRAINT cdf_c_onl_fuzzy CHECK (onl_fuzzy in ('Y','N')),
CONSTRAINT cdf_c_bck_fuzzy CHECK (bck_fuzzy in ('Y','N'))
) &tablespace&
>>>

define cdf_i_df_key
<<<
CREATE INDEX cdf_i_df_key on cdf(dbinc_key, file#, create_scn) &tablespace&
>>>

define cdf_i_fname_status
<<<
CREATE INDEX cdf_i_fname_status on cdf(fname_hashkey, status) &tablespace&
>>>

# the XDF table contains all proxy datafile backups
# A proxy datafile backup uniquely identified by dbinc_key, recid and stamp.
# xdf_key is also a unique primary key

define xdf
<<<
CREATE TABLE xdf
(
xdf_key         NUMBER NOT NULL,        -- primary key
dbinc_key       NUMBER NOT NULL,        -- database incarnation
xdf_recid       NUMBER NOT NULL,        -- x$kccpd recid from control file
xdf_stamp       NUMBER NOT NULL,        -- x$kccpd stamp from control file
file#           NUMBER NOT NULL,        -- database file number
create_scn      NUMBER NOT NULL,        -- creation SCN
tag             VARCHAR2(32),           -- df copy tag
incr_level      NUMBER,                 -- incremental backup level (null or 0)
ckp_scn         NUMBER NOT NULL,        -- scn of the last datafile ckpt
ckp_time        DATE NOT NULL,          -- time of the last datafile ckpt
onl_fuzzy       VARCHAR2(1) NOT NULL,   -- 'Y' -> online fuzzy
bck_fuzzy       VARCHAR2(1) NOT NULL,   -- 'Y' -> backup fuzzy
abs_fuzzy_scn   NUMBER,                 -- absolute fuzzy scn, if known.
                                        -- null if not known.
rcv_fuzzy_scn   NUMBER,                 -- media recovery fuzzy scn
rcv_fuzzy_time  DATE,                   -- timestamp for media rcv fuzzy scn
blocks          NUMBER NOT NULL,        -- number of blocks
block_size      NUMBER NOT NULL,        -- blocksize
device_type     VARCHAR2(255) NOT NULL, -- 'DISK' -> on disk rather than seq
handle          VARCHAR2(1024) NOT NULL, --backup piece handle
handle_hashkey  VARCHAR2(30) NOT NULL,  -- indexed hashkey on handle
comments        VARCHAR2(255),          -- 
media           VARCHAR2(80),           -- media handle
media_pool      NUMBER,                 -- media pool
start_time      DATE NOT NULL,          -- time when this piece started
completion_time DATE NOT NULL,          -- time when this piece completed
-- updatable columns
status          VARCHAR2(1) NOT NULL,
CONSTRAINT xdf_p  PRIMARY KEY (xdf_key),
CONSTRAINT xdf_u1 UNIQUE (dbinc_key, xdf_recid, xdf_stamp),
CONSTRAINT xdf_f1 FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE,
-- this constraint can't be enforced since proxy copies might be resynced
-- before their parent datafiles
--CONSTRAINT xdf_f2 FOREIGN KEY (dbinc_key, file#, create_scn)
--  REFERENCES df ON DELETE CASCADE,
CONSTRAINT xdf_c_status CHECK (status in ('A','U','D','X')),
CONSTRAINT xdf_c_onl_fuzzy CHECK (onl_fuzzy in ('Y','N')),
CONSTRAINT xdf_c_bck_fuzzy CHECK (bck_fuzzy in ('Y','N'))
) &tablespace&
>>>

define xdf_i_df_key
<<<
CREATE INDEX xdf_i_df_key on xdf(dbinc_key, file#, create_scn) &tablespace&
>>>

define xdf_i_handle_status
<<<
CREATE INDEX xdf_i_handle_status on xdf(handle_hashkey, status) &tablespace&
>>>

# The BRL table contains backup redo logs (in backup sets)
# It corresponds to the V$REDOLOG_BACKUP fixed view in in the control file. 
# The redo log backups are uniquely identified by bs_key, brl_recid and
# brl_stamp.

define brl
<<<
CREATE TABLE brl
(
brl_key         NUMBER NOT NULL,
dbinc_key       NUMBER NOT NULL,        -- database incarnation
brl_recid       NUMBER NOT NULL,        -- recid from control file
brl_stamp       NUMBER NOT NULL,        -- stamp from control file
bs_key          NUMBER NOT NULL,        -- backup set key
thread#         NUMBER NOT NULL,        -- thread number
sequence#       NUMBER NOT NULL,        -- log sequence number
low_scn         NUMBER NOT NULL,        -- scn generated when switching in
low_time        DATE   NOT NULL,        -- time low SCN allocated
next_scn        NUMBER NOT NULL,        -- scn generated when switching out
next_time       DATE NOT NULL,          -- time when next SCN allocated
blocks          NUMBER NOT NULL,        -- number of blocks written to backup
block_size      NUMBER NOT NULL,        -- size of a block in bytes
CONSTRAINT brl_p  PRIMARY KEY (brl_key),
CONSTRAINT brl_u1 UNIQUE (dbinc_key, brl_recid, brl_stamp),
CONSTRAINT brl_f1 FOREIGN KEY (bs_key)
  REFERENCES bs ON DELETE CASCADE
--CONSTRAINT brl_f2 FOREIGN KEY (dbinc_key, thread#, sequence#, low_scn)
--  REFERENCES rlh ON DELETE CASCADE
) &tablespace&
>>>

define brl_i_bs_key
<<<
CREATE INDEX brl_i_bs_key on brl(bs_key) &tablespace&
>>>

# The CB table contains corrupt block ranges in datafile backups.
# It corresponds to the V$BACKUP_CORRUPTION fixed view in the control file.

define bcb
<<<
CREATE TABLE bcb
(
bdf_key         NUMBER NOT NULL,        -- datafile backup or copy
bcb_recid       NUMBER NOT NULL,        -- recid from control file
bcb_stamp       NUMBER NOT NULL,        -- stamp from control file
piece#          NUMBER NOT NULL,        -- backup piece to which block belongs
block#          NUMBER NOT NULL,        -- starting block number in the file
blocks          NUMBER NOT NULL,        -- block count in corrupt range
corrupt_scn     NUMBER,                 -- scn at which corruption was detected
marked_corrupt  VARCHAR2(1) NOT NULL,   -- 'Y' -> could not read from disk
CONSTRAINT bcb_u1 UNIQUE (bdf_key, bcb_recid, bcb_stamp),
CONSTRAINT bcb_f1 FOREIGN KEY (bdf_key)
  REFERENCES bdf ON DELETE CASCADE
) &tablespace&
>>>

# The CCB table contains corrupt block ranges in datafile copies
# It corresponds to the V$COPY_CORRUPTION fixed view in the control file.

define ccb
<<<
CREATE TABLE ccb
(
cdf_key         NUMBER NOT NULL,        -- datafile copy
ccb_recid       NUMBER NOT NULL,        -- recid from control file
ccb_stamp       NUMBER NOT NULL,        -- stamp from control file
block#          NUMBER NOT NULL,        -- block number in the file
blocks          NUMBER NOT NULL,        -- block count in corrupt range
corrupt_scn     NUMBER,                 -- scn at which corruption was detected
marked_corrupt  VARCHAR2(1) NOT NULL,   -- 'Y' -> could not read from disk
CONSTRAINT ccb_u1 UNIQUE (cdf_key, ccb_recid, ccb_stamp),
CONSTRAINT ccb_f1 FOREIGN KEY (cdf_key)
  REFERENCES cdf ON DELETE CASCADE
) &tablespace&
>>>

# The SCR table contains 1 row for each stored script.

define scr
<<<
CREATE TABLE scr
(
scr_key         NUMBER NOT NULL,        -- sequence generated primary key
db_key          NUMBER NOT NULL,        -- database that owns this script
scr_name        VARCHAR2(100) NOT NULL, -- script name
CONSTRAINT scr_p  PRIMARY KEY(scr_key),
CONSTRAINT scr_f1 FOREIGN KEY(db_key)
  REFERENCES db ON DELETE CASCADE,
CONSTRAINT scr_u1 UNIQUE(db_key, scr_name)
) &tablespace&
>>>

# The SCRL table contains 1 row for each line of each stored script.

define scrl
<<<
CREATE TABLE scrl
(
scr_key         NUMBER NOT NULL,        -- script that owns this line
linenum         NUMBER NOT NULL,        -- line number
text            VARCHAR2(1024) NOT NULL,-- text of the line
CONSTRAINT scrl_u1 UNIQUE(scr_key, linenum),
CONSTRAINT scrl_f1 FOREIGN KEY(scr_key)
  REFERENCES scr ON DELETE CASCADE
) &tablespace&
>>>

define config
<<<
CREATE TABLE config
(
name		VARCHAR2(30) NOT NULL,	-- name of configuration option
value		VARCHAR2(100)		-- value of configuration option
) &tablespace&
>>>

define config_init_upgrade
<<<
DECLARE
   compatible number;
BEGIN
   SELECT to_number(value) INTO compatible
     FROM config
    WHERE name = 'compatible';
   IF (compatible IS NULL) THEN
      INSERT INTO config VALUES('compatible', '080004');
   END IF;
END;
>>>

define config_init_create
<<<
INSERT INTO config VALUES('compatible', '080106')
>>>

#
# Recovery Catalog views
#

# databases registered in recovery catalog
# information about databases and their current incarnations
define rc_database
<<<
create or replace view rc_database as
select db.db_key,
       db.curr_dbinc_key dbinc_key,
       db.db_id dbid,
       dbinc.db_name name,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time
from db, dbinc
where db.curr_dbinc_key = dbinc.dbinc_key
>>>

# database incarnations
# information about all incarnations registered in recovery catalog 
define rc_database_incarnation
<<<
create or replace view rc_database_incarnation as
select db.db_key,
       db.db_id dbid,
       dbinc.dbinc_key,
       dbinc.db_name name,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       decode(dbinc.dbinc_key, db.curr_dbinc_key, 'YES', 'NO') 
         current_incarnation,
       dbinc.parent_dbinc_key
from db, dbinc
where db.db_key = dbinc.db_key
>>>

# recovery catalog resyncs (checkpoints)
# information about recovery catalog resyncs
define rc_resync
<<<
create or replace view rc_resync as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       ckp.ckp_key resync_key,
       ckp.ckp_scn controlfile_change#,
       ckp.ckp_time controlfile_time,
       ckp.ckp_cf_seq controlfile_sequence#,
       ckp.cf_create_time controlfile_version,
       ckp.ckp_type resync_type,
       ckp.ckp_db_status db_status,
       ckp.resync_time
from ckp, dbinc
where ckp.dbinc_key = dbinc.dbinc_key
>>>

# rc_checkpoint is replaced by rc_resync, but is still used by some tests
define rc_checkpoint
<<<
create or replace view rc_checkpoint as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       ckp.ckp_key,
       ckp.ckp_scn,
       ckp.ckp_cf_seq,
       ckp.ckp_time,
       ckp.ckp_type
from ckp, dbinc
where ckp.dbinc_key = dbinc.dbinc_key
>>>

# tablespaces
# information about all tablespaces registered in recovery catalog
# show also dropped tablespaces and tablespaces that belong to old
# database incarnations
# current value is showed for tablespace attributes
define rc_tablespace
<<<
create or replace view rc_tablespace as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       ts.ts#,
       ts.ts_name name,
       ts.create_scn creation_change#,
       ts.create_time creation_time,
       ts.drop_scn drop_change#,
       ts.drop_time
from ts, dbinc
where dbinc.dbinc_key = ts.dbinc_key
>>>

# datafiles
# information about all datafiles registered in recovery catalog
# the datafiles are showed dropped if their tablespace is dropped
# similar to rc_tablespace view

define rc_datafile
<<<
create or replace view rc_datafile as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       ts.ts#,
       ts.ts_name tablespace_name,
       df.file#,
       df.create_scn creation_change#,
       df.create_time creation_time,
       df.drop_scn drop_change#,
       df.drop_time,
       dfatt.blocks*df.block_size bytes,
       dfatt.blocks,
       df.block_size,
       dfatt.fname name,
       df.stop_scn stop_change#,
       df.stop_time,
       df.read_only read_only
from dbinc, ts, df, dfatt
where dbinc.dbinc_key = ts.dbinc_key
and   ts.dbinc_key = df.dbinc_key
and   ts.ts# = df.ts#
and   ts.create_scn = df.ts_create_scn
and   df.dbinc_key = dfatt.dbinc_key
and   df.file# = dfatt.file#
and   df.create_scn = dfatt.create_scn
and   dfatt.end_ckp_key is null
>>>

# information about redo threads

define rc_redo_thread
<<<
create or replace view rc_redo_thread as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       rt.thread#,
       rt.status,
       rt.sequence#,
       rt.enable_scn enable_change#,
       rt.enable_time,
       rt.disable_scn disable_change#,
       rt.disable_time
from dbinc, rt
where dbinc.dbinc_key = rt.dbinc_key
>>>

# information about online redo logs

define rc_redo_log
<<<
create or replace view rc_redo_log as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       orl.thread#,
       orl.group#,
       orl.fname name
from dbinc, orl
where dbinc.dbinc_key = orl.dbinc_key
>>>

# information about redo log history

define rc_log_history
<<<
create or replace view rc_log_history as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       rlh.rlh_recid recid,
       rlh.rlh_stamp stamp,
       rlh.thread#,
       rlh.sequence#,
       rlh.low_scn first_change#,
       rlh.low_time first_time,
       rlh.next_scn next_change#,
       decode(rlh.status, 'N', 'NO', 'Y', 'YES', NULL,  NULL, '?') cleared
--       rlh.next_time,
--       rlh.blocks
from dbinc, rlh
where dbinc.dbinc_key = rlh.dbinc_key
>>>

# archived logs
# information about all archivelogs

define rc_archived_log
<<<
create or replace view rc_archived_log as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       al.al_key,
       al.al_recid recid,
       al.al_stamp stamp,
       al.fname name,
       al.thread#,
       al.sequence#,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       al.low_scn first_change#,
       al.low_time first_time,
       al.next_scn next_change#,
       al.next_time,
       al.blocks,
       al.block_size,
       al.completion_time,
       decode(al.archived, 'N', 'NO', 'Y', 'YES', '?') archived,
       al.status
from dbinc, al
where dbinc.dbinc_key = al.dbinc_key
>>>

# backup sets

define rc_backup_set
<<<
create or replace view rc_backup_set as
select db.db_key,
       db.db_id,
       bs.bs_key,
       bs.bs_recid recid,
       bs.bs_stamp stamp,
       bs.set_stamp,
       bs.set_count,
       bs.bck_type backup_type,
       bs.incr_level incremental_level,
       bs.pieces,
       bs.start_time,
       bs.completion_time,
       ((bs.completion_time - bs.start_time) * 86400) elapsed_seconds,
       bs.status
from db, bs
where db.db_key = bs.db_key
>>>

# backup pieces

define rc_backup_piece
<<<
create or replace view rc_backup_piece as
select db.db_key,
       db.db_id,
       bp.bp_key,
       bp.bp_recid recid,
       bp.bp_stamp stamp,
       bs.bs_key,
       bs.set_stamp,
       bs.set_count,
       bs.bck_type backup_type,
       bs.incr_level incremental_level,
       bp.piece#,
       bp.copy#,
       bp.device_type,
       bp.handle,
       bp.comments,
       bp.media,
       bp.media_pool,
       decode(bp.concur, 'N', 'NO', 'Y', 'YES', '?') concur,
       bp.tag,
       bp.start_time,
       bp.completion_time,
       ((bp.completion_time - bp.start_time) * 86400) elapsed_seconds,
       bp.status
from db, bs, bp
where db.db_key = bs.db_key
and   bs.bs_key = bp.bs_key
>>>

# datafile backups (in backup sets)

define rc_backup_datafile
<<<
create or replace view rc_backup_datafile as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       bdf.bdf_key,
       bdf.bdf_recid recid,
       bdf.bdf_stamp stamp,
       bs.bs_key,
       bs.set_stamp,
       bs.set_count,
       bs.bs_recid, 
       bs.bs_stamp,
       bs.bck_type backup_type,
       bdf.incr_level incremental_level,
       bdf.completion_time,
       bdf.file#,
       bdf.create_scn creation_change#,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       bdf.incr_scn incremental_change#,
       bdf.ckp_scn checkpoint_change#,
       bdf.ckp_time checkpoint_time,
       bdf.abs_fuzzy_scn absolute_fuzzy_change#,
       bdf.datafile_blocks,
       bdf.blocks,
       bdf.block_size,
       bs.status,
       bs.incr_level bs_level,
       bs.pieces
from dbinc, bs, bdf
where dbinc.dbinc_key = bdf.dbinc_key
and   bs.bs_key = bdf.bs_key
>>>

# backup control files in backup sets

define rc_backup_controlfile
<<<
create or replace view rc_backup_controlfile as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       bcf.bcf_key,
       bcf.bcf_recid recid,
       bcf.bcf_stamp stamp,
       bs.bs_key,
       bs.set_stamp,
       bs.set_count,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       bcf.ckp_scn checkpoint_change#,
       bcf.ckp_time checkpoint_time,
       bcf.create_time creation_time,
       bcf.block_size,
       bcf.min_offr_recid oldest_offline_range,
       bs.status,
       bs_recid,
       bs_stamp,
       bs.incr_level bs_level,
       bs.completion_time
from dbinc, bs, bcf
where dbinc.dbinc_key = bcf.dbinc_key
and   bs.bs_key = bcf.bs_key
>>>

# datafile copies (on disk)

define rc_datafile_copy
<<<
create or replace view rc_datafile_copy as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       cdf.cdf_key,
       cdf.cdf_recid recid,
       cdf.cdf_stamp stamp,
       cdf.fname name,
       cdf.tag,
       cdf.file#,
       cdf.create_scn creation_change#,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       cdf.incr_level incremental_level,
       cdf.ckp_scn checkpoint_change#,
       cdf.ckp_time checkpoint_time,
       cdf.abs_fuzzy_scn absolute_fuzzy_change#,
       cdf.rcv_fuzzy_scn recovery_fuzzy_change#,
       cdf.rcv_fuzzy_time recovery_fuzzy_time,
       decode(cdf.onl_fuzzy,'N', 'NO', 'Y', 'YES', '?') online_fuzzy,
       decode(cdf.bck_fuzzy,'N', 'NO', 'Y', 'YES', '?') backup_fuzzy,
       cdf.blocks,
       cdf.block_size,
       cdf.completion_time,
       cdf.status
from dbinc, cdf
where dbinc.dbinc_key = cdf.dbinc_key
>>>

define rc_controlfile_copy
<<<
create or replace view rc_controlfile_copy as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       ccf.ccf_key,
       ccf.ccf_recid recid,
       ccf.ccf_stamp stamp,
       ccf.fname name,
       ccf.tag,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       ccf.ckp_scn checkpoint_change#,
       ccf.ckp_time checkpoint_time,
       ccf.create_time creation_time,
--       ccf.ckp_time checkpoint_time,
--       ccf.blocks,
       ccf.block_size,
       ccf.min_offr_recid,
       ccf.min_offr_recid oldest_offline_range,
       ccf.completion_time,
       ccf.status
from dbinc, ccf
where dbinc.dbinc_key = ccf.dbinc_key
>>>

# redo log backups (in backup sets)

define rc_backup_redolog
<<<
create or replace view rc_backup_redolog as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       brl.brl_key,
       brl.brl_recid recid,
       brl.brl_stamp stamp,
       bs.bs_key,
       bs.set_stamp,
       bs.set_count,
       bs.bck_type backup_type,
       bs.completion_time,
       brl.thread#,
       brl.sequence#,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       brl.low_scn first_change#,
       brl.low_time first_time,
       brl.next_scn next_change#,
       brl.next_time,
       brl.blocks,
       brl.block_size,
       bs.status,
       bs_recid,
       bs_stamp,
       bs.pieces
from dbinc, bs, brl
where dbinc.dbinc_key = brl.dbinc_key
and   bs.bs_key = brl.bs_key
>>>

# corrupt blocks in datafile backups and copies

define rc_backup_corruption
<<<
create or replace view rc_backup_corruption as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       bcb.bcb_recid recid,
       bcb.bcb_stamp stamp,
       bs.bs_key,
       bs.set_stamp,
       bs.set_count,
       bcb.piece#,
       bdf.bdf_key,
       bdf.bdf_recid,
       bdf.bdf_stamp,
       bdf.file#,
       bdf.create_scn creation_change#,
       bcb.block#,
       bcb.blocks,
       bcb.corrupt_scn corruption_change#,
       decode(bcb.marked_corrupt,'N', 'NO', 'Y', 'YES', '?') marked_corrupt
from dbinc, bs, bdf, bcb
where dbinc.dbinc_key = bdf.dbinc_key
and   bs.bs_key = bdf.bs_key
and   bdf.bdf_key = bcb.bdf_key
>>>

define rc_copy_corruption
<<<
create or replace view rc_copy_corruption as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       ccb.ccb_recid recid,
       ccb.ccb_stamp stamp,
       cdf.cdf_key,
       cdf.cdf_recid copy_recid,
       cdf.cdf_stamp copy_stamp,
       cdf.file#,
       cdf.create_scn creation_change#,
       ccb.block#,
       ccb.blocks,
       ccb.corrupt_scn corruption_change#,
       decode(ccb.marked_corrupt,'N', 'NO', 'Y', 'YES', '?') marked_corrupt
from  dbinc, cdf, ccb
where dbinc.dbinc_key = cdf.dbinc_key
and   cdf.cdf_key = ccb.cdf_key
and   cdf.status = 'A'
>>>

# offline ranges for datafiles

define rc_offline_range
<<<
create or replace view rc_offline_range as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       offr.offr_recid recid,
       offr.offr_stamp stamp,
       offr.file#,
       offr.create_scn creation_change#,
       offr.offline_scn offline_change#,
       offr.online_scn online_change#,
       offr.online_time,
       offr.cf_create_time
from dbinc, offr
where dbinc.dbinc_key = offr.dbinc_key
>>>

# stored scripts 

define rc_stored_script
<<<
create or replace view rc_stored_script as
select db.db_key,
       dbinc.db_name,
       scr.scr_name script_name
from db, dbinc, scr
where dbinc.dbinc_key = db.curr_dbinc_key
and   db.db_key = scr.db_key
>>>

define rc_stored_script_line
<<<
create or replace view rc_stored_script_line as
select db.db_key,
       scr.scr_name script_name,
       scrl.linenum line,
       scrl.text
from db, scr, scrl
where db.db_key = scr.db_key
and   scr.scr_key = scrl.scr_key
>>>

# proxy copies

define rc_proxy_datafile
<<<
create or replace view rc_proxy_datafile as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       xdf.xdf_key,
       xdf.xdf_recid recid,
       xdf.xdf_stamp stamp,
       xdf.tag,
       xdf.file#,
       xdf.create_scn creation_change#,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       xdf.incr_level incremental_level,
       xdf.ckp_scn checkpoint_change#,
       xdf.ckp_time checkpoint_time,
       xdf.abs_fuzzy_scn absolute_fuzzy_change#,
       xdf.rcv_fuzzy_scn recovery_fuzzy_change#,
       xdf.rcv_fuzzy_time recovery_fuzzy_time,
       decode(xdf.onl_fuzzy,'N', 'NO', 'Y', 'YES', '?') online_fuzzy,
       decode(xdf.bck_fuzzy,'N', 'NO', 'Y', 'YES', '?') backup_fuzzy,
       xdf.blocks,
       xdf.block_size,
       xdf.device_type,
       xdf.handle,
       xdf.comments,
       xdf.media,
       xdf.media_pool,
       xdf.start_time,
       xdf.completion_time,
       ((xdf.completion_time - xdf.start_time) * 86400) elapsed_seconds,
       xdf.status
from dbinc, xdf
where dbinc.dbinc_key = xdf.dbinc_key
>>>

define rc_proxy_controlfile
<<<
create or replace view rc_proxy_controlfile as
select dbinc.db_key,
       dbinc.dbinc_key,
       dbinc.db_name,
       xcf.xcf_key,
       xcf.xcf_recid recid,
       xcf.xcf_stamp stamp,
       xcf.tag,
       dbinc.reset_scn resetlogs_change#,
       dbinc.reset_time resetlogs_time,
       xcf.ckp_scn checkpoint_change#,
       xcf.ckp_time checkpoint_time,
       xcf.create_time creation_time,
       xcf.block_size,
       xcf.min_offr_recid,
       xcf.min_offr_recid oldest_offline_range,
       xcf.device_type,
       xcf.handle,
       xcf.comments,
       xcf.media,
       xcf.media_pool,
       xcf.start_time,
       xcf.completion_time,
       ((xcf.completion_time - xcf.start_time) * 86400) elapsed_seconds,
       xcf.status
from dbinc, xcf
where dbinc.dbinc_key = xcf.dbinc_key
>>>

# dbmsrvct.sql
# 
#    NAME
#      dbmsrvct.sql - recovery catalog package spec
#
#    DESCRIPTION
#      This package contains procedures for maintaining
#      the recovery catalog.
#
#    NOTES
#      This is an internal package used only by the Recovery Manager.
#      It is the only interface for updating the recovery catalog.
#      It must be created in the same database and schema as the
#      recovery catalog tables.
#
#    MODIFIED   (MM/DD/YY)
#    swerthei    06/02/98 - add media_pool
#    swerthei    06/01/98 - add changeProxyCopy
#    swerthei    05/18/98 - resync proxy copy records
#    dbeusee     04/20/98 - dbeusee_rpt_redundancy_enh
#    gpongrac    05/06/98 - add bsStatusRecalc
#    dbeusee     04/08/98 - xcheck enh.
#    fsanchez    03/30/98 - Duplexed Backup sets
#    gpongrac    09/02/97 - add setDatafileSize
#    gpongrac    06/30/97 - keep offline clean and read-only scn in df table
#    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    02/27/97 - add ckptNeeded
#    gpongrac    02/20/97 - add completion_time to bdf
#    swerthei    01/14/97 - add setclonename
#    swerthei    12/10/96 - add backup piece/set time columns
#    tpystyne    12/13/96 - maintain parent dbinc
#    tpystyne    12/12/96 - add rbs_count to checkTableSpace
#    gpongrac    11/20/96 - make this_dbinc_key public;
#    gpongrac    11/05/96 - add high_df_recid to beginCkpt
#    swerthei    10/23/96 - add checkDataFileCopy.completion_time
#    swerthei    10/23/96 - add checkArchivedLog.completion_time
#    tpystyne    10/05/96 - add changeControlfileCopy
#    tpystyne    09/12/96 - add archived parameter to checkArchivedLog
#    tpystyne    09/06/96 - add online redo log support
#    gpongrac    08/23/96 - add commit function
#    tpystyne    08/26/96 - add min_offr_recid
#    gpongrac    08/13/96 - add stored script support
#    tpystyne    07/24/96 - add incr_level to cdf
#    tpystyne    06/24/96 - implement offline range resync
#    tpystyne    06/22/96 - remove unused columns from ts
#    tpystyne    06/04/96 - add create_time to checkBackup/CopyControlFile
#    asurpur     04/09/96 - Dictionary Protection Implementation
#    tpystyne    03/28/96 - use raise_application_error
#    tpystyne    03/21/96 - update function prototypes
#    tpystyne    01/31/96 - update for set_stamp and set_count
#    gpongrac    01/10/96 - drop domain from registerDataase and setDatabase
#    tpystyne    12/07/95 - update comments
#    tpystyne    12/01/95 - add deleted object resync
#    tpystyne    11/27/95 - split out dbms_rman package
#    tpystyne    10/31/95 - incorporate design changes
#    tpystyne    10/23/95 - add archive log procedures
#    gpongrac    10/11/95 - add restore and recovery procedures
#    tpystyne    10/09/95 - fix procedure prototypes
#    tpystyne    10/03/95 - fix backup set procedure prototypes
#    tpystyne    10/02/95 - remove rbs procedure calls
#    tpystyne    09/27/95 - update parameter names to match with column names
#    gpongrac    09/25/95 - add blocksize args to addBackupDatafile, add addB
#    gpongrac    09/25/95 - add procedures for backup job steps
#    gpongrac    09/21/95 - add creation SCN as arg to checkTs
#    gpongrac    08/08/95 - recovery catalog manager package spec
#    gpongrac    08/08/95 - Created



# The dbms_rcvcat package is used by the Recovery Manager to maintain the 
# recovery catalog. The Recovery Manager will periodically query the
# controlfile of the target database and propagate the information to
# the recovery catalog using dbms_rcvcat. The Recovery Manager is the only
# intended client for this package. All PL/SQL code that access this
# packages is in recover.bsq.

# The errors raised by dbms_rcvcat are in the 20000 - 20999 range and
# described in rmanus.msg. Recovery Manager will display the error numbers
# with text from its error message file.

# The package must be created in the same schema as the recovery catalog
# tables and views. 

# the recovery catalog can store information about many target databases.
#
# a target database must be registered before any information about it can
# be recorded in the recovery catalog.
#
# registerDatabase;
#
# if the target database is opened using the resetlogs option, old backups 
# and archived logs cannot be used anymore and the database is said to 
# have a new incarnation. The new incarnation must be registered and
# made the current incarnation of target database by calling resetDatabase. 
#
# resetDatabase;
#
# to resync the recovery catalog with the target database the following
# sequence of calls must be executed (in the given order). The procedures
# will do minimal checking to enforce that calls are executed in right order,
# but it is the responsility of the client to ensure that the protocol is
# not violated.
#
# the resync operation is one transaction; it is either committed by endCkpt
# or rolled back by cancelCkpt. The client must not directly commit or 
# rollback.

# the full resync operation will first make a snapshot of the control file
# and read all information from the snapshot. The partial resync operation
# used by backup job will read from the current controlfile, but only the
# circular-reuse type records. 

# beginCkpt locks the database incarnation to ensure that only one resync
# can progress at a time (concurrent resyncs wouldn't make sense and
# would be hard to coordinate). However, the resync operation tolerates
# Recovery Manager commands that update the recovery catalog directly.  
#
# begin
#   setDatabase;
#   beginCkpt;
#     beginTableSpaceResync;
# 	 checkTableSpace;
# 	 ...
#     endTableSpaceResync;
#     beginDataFileResync;
# 	 checkDataFile;
# 	 ...
#     endDataFileResync;
#     beginThreadResync;
# 	 checkThread;
# 	 ...
#     endThreadResync;
#     beginLogHistoryResync;
# 	 checkLogHistory;
# 	 ...
#     endLogHistoryResync;
#     beginArchivedLogResync;
# 	 checkArchivedLog;
# 	 ...
#     endArchivedLogResync;
#     beginBackupSetResync;
# 	 checkBackupSet;
# 	 ...
#     endBackupSetResync;
#     beginBackupPieceResync;
# 	 checkBackupPiece;
# 	 ...
#     endBackupPieceResync;
#     beginBackupDataFileResync;
# 	 checkBackupDataFile;
# 	 ...
#     endBackupDataFileResync;
#     beginBackupRedoLogResync;
# 	 checkBackupRedoLog;
# 	 ...
#     endBackupRedoLogResync;
#     beginDataFileCopyResync;
# 	 checkDataFileCopy;
# 	 ...
#     endDataFileCopyResync;
#   endCkpt;
# exception
#   when dbms_rcvcat.internal_error or others then
#     cancelCkpt;
# end

# the change command uses the following procedures. Each call is a transaction

# changeBackupPiece;
# changeDatafileCopy;
# changeArchivedLog;

define dbmsrvct_sql
<<<
create or replace package dbms_rcvcat is

-- resync types
RESYNC_FULL    CONSTANT number := 1;
RESYNC_PARTIAL CONSTANT number := 2;
RESYNC_NONE    CONSTANT number := 3;

-- 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;

/*-----------------------*
 * Database Registration *
 *-----------------------*/

PROCEDURE registerDatabase(
   db_id      IN number
  ,db_name    IN varchar2
  ,reset_scn  IN number
  ,reset_time IN date
);

-- registerDatabase registers a new target database in the recovery catalog.
-- All target databases must be registered exactly once.  The database
-- is inserted into the db table, and also one row is inserted into the dbinc 
-- table as the root and the current incarnation of this database.
-- Note that it is legal to register a database that has undergone a previous 
-- resetlogs, but recovery to a time prior to the resetlogs is not supported.
-- registerDatabase is atomic; all changes to recovery catalog are committed 
-- or rolled back if an exception is signalled

-- Input parameters:
--   db_id
--     the value of kccfhdbi from the controlfile of the target database
--   db_name
--     the name of the database
--   reset_scn
--     the resetlogs SCN of this database
--   reset_time
--     the resetlogs time
-- Exceptions:
--   DUPLICATE_DATABASE
--     a database with the same db_id has already been registered. This may
--     happen if the database was created by copying datafiles from an
--     existing datafile 
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before calling registerDatabase
--   BACKUP_IN_PROGRESS (internal)
--     a backup conversation is in progress. The current backup conversation
--     must be ended or canceled before calling registerDatabase


procedure resetDatabase(
  db_id      IN number
 ,db_name    IN varchar2
 ,reset_scn  IN number
 ,reset_time IN date
 ,parent_reset_scn  IN number
 ,parent_reset_time IN date
);

procedure resetDatabase(
  dbinc_key  IN number
 ,db_name    IN varchar2
);

-- There are two forms of resetDatabase procedure.
-- The first form registers a new incarnation of an registered database
-- and makes it the current incarnation. It must always be called after 
-- the target database is opened with resetlogs option.
-- The second form makes an old incarnation the current incarnation.
-- registerDatabase is atomic; all changes to recovery catalog are committed 
-- or rolled back if an exception is signalled

-- Input parameters:
--   dbinc_key
--     the primary key of an existing database incarnation in the recovery
--     catalog. Only used by the second form.
--   db_id
--     the value of kccfhdbi from the controlfile of the target database
--   db_name
--     the name of the database
--   reset_scn
--     the resetlogs SCN of this database
--   reset_time
--     the resetlogs time
-- Exceptions:
--   DB_ID_IS_NULL (internal)
--     a null db_id was given
--   DATABASE_NOT_FOUND
--     No database with the given db_id was found in the recovery catalog
--     Use registerDatabase procedure instead
--   RESET_SCN_TOO_LOW
--   RESET_TIME_TOO_LOW
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before calling resetDatabase
--   BACKUP_IN_PROGRESS (internal)
--     a backup conversation is in progress. The current backup conversation
--     must be ended or canceled before calling resetDatabase


procedure unregisterDatabase(
  db_key     IN number
 ,db_id      IN number
);

-- unRegisterDatabase removes a registered database from the recovery catalog

/*--------------------------*
 * Set Database Incarnation *
 *--------------------------*/

procedure setDatabase(
  db_name    IN varchar2
 ,reset_scn  IN number
 ,reset_time IN date
 ,db_id      IN number
);

-- 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
--   db_name
--     the name of the database
--   reset_scn
--     the resetlogs SCN of this database
--   reset_time
--     the resetlogs time
-- Exceptions:
--   DATABASE_NOT_FOUND
--     No database with the given db_id was found in the recovery catalog
--     The database must be registered using registerDatabase first
--   DATABASE_INCARNATION_NOT_FOUND
--     No database incarnation matches the given arguments
--     The database incarnation must be registered using resetDatabase first
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before calling setDatabase

/*-----------------------------*
 * Recovery Catalog Checkpoint *
 *-----------------------------*/

function ckptNeeded(
  ckp_scn         IN number
 ,ckp_cf_seq      IN number
 ,cf_version      IN date
 ,cf_type         IN number
 ,high_df_recid   IN number
 ,high_orl_recid  IN number
 ,high_cdf_recid  IN number
 ,high_al_recid   IN number
 ,high_bp_recid   IN number
 ,high_do_recid   IN number
 ,high_offr_recid IN number
 ,high_pc_recid   IN number DEFAULT NULL
) return number;

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
);

-- 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

-- Exceptions:
--   CHECKPOINT_IN_PROGRESS (internal)
--     a recovery catalog checkpoint is in progress. The current checkpoint
--     must be ended or canceled before beginning a new one
--   DATABASE_INCARNATION_NOT_SET
--     the database incarnation is not set. It must be set by calling
--     setDatabase before calling beginCkpt
--   INVALID_CKP_SCN
--     the backup controlfile ckp_scn is less than ckp_scn of the previous
--     checkpoint. This should not happen. However, it is conceivable that this
--     could happen if the user restores the database from an offline backup
--     and doesn't open the database with resetlogs.
--   INVALID_CKP_CF_SEQ
--     the backup controlfile ckp_cf_seq is less than ckp_cf_seq of the 
--     previous checkpoint. This should not happen. See the explanation of the
--     the previous exception.
--   CKP_ALREADY_RECORDED
--     the backup control file ckp_scn and ckp_cf_seq are equal to the previous
--     checkpoint. Nothing was changed in the control file between this
--     this checkpoint and the previous one, so the there is no reason to
--     resync
--   CHECKPOINT_TYPE_INVALID
--     the ckp_type is not valid ('ONLINE' or 'OFFLINE')

procedure endCkpt;

-- endCkpt must be called after all information is successfully resynced.
-- Note that endCkpt cannot verify that everything was successfully resynced,
-- the client must not call endCkpt if it encountered unrecoverable errors
-- during the checkpoint.
-- It will commit the checkpoint transaction and clear the package state
-- variables to indicate that no checkpoint is in progress

-- Exceptions:
--   CHECKPOINT_NOT_IN_PROGRESS (internal)
--     there is no checkpoint in progress to be ended. Maybe cancelCkpt was
--     called before this.

procedure cancelCkpt;

-- cancelCkpt must be called if unrecoverable error is encountered during 
-- the checkpoint. It will rollback the checkpoint transaction and clear
-- the package state variables to indicate that no checkpoint is in progress


/*-------------------*
 * Resync            *
 *-------------------*/

-- Resyncing all non-circular-reuse type records (tablespace, datafile and 
-- redo thread) from the controlfile use the following protocol. The
-- controlfile maintains a high_recid for each record type. The high_recid
-- is incremented every time a record of that type is created, updated or
-- deleted. The client gets the high_recid for the record type
-- from the controlfile and passes it to beginResync. The beginResync
-- compares the given high_recid with the high_recid stored in the recovery
-- catalog. If the high_recids are equal then nothing has changed in the
-- controlfile since the previous resync and beginResync returns false.
-- If the given high_recid is greater than the one stored in the recovery
-- catalog then beginResync will return true and the client will query
-- all records from the controlfile and call Check procedure for each
-- record.

-- Resyncing all circular-reuse type records from the controlfile use another
-- protocol. Since the circular-reuse type records are never updated
-- (significantly) after they have been created, only records created after
-- the previous resync need to be resynced. The client calls the beginResync
-- procedure which returns the high_recid stored in the recovery catalog.
-- The client compared the returned high_recid with the high_recid in the
-- controlfile. If the high_recids are equal then no new record have created
-- since the previous resync. If the high_recid in controlfile is greater
-- than the one returned by beginResync, then the client will query all
-- records with recid greater than the returned high_recid from the controlfile
-- and call Check procedure for each record.

-- The parameters for all Check procedures match the columns in the 
-- corresponding V$ views on the controlfile fixed tables. 


/*-------------------*
 * Tablespace Resync *
 *-------------------*/

function beginTableSpaceResync(
  high_ts_recid IN number
) 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
);

-- 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

-- Exceptions:
--   TS_RESYNC_NOT_STARTED
--   BAD_TS_ORDER
--     tablespaces were passed in wrong order
--   INVALID_CREATE_SCN
--     create_scn is higher than ckp_scn or create scn is lower than the
--     create scn of the previous incarnation
--   INVALID_CREATE_TIME
--   INVALID_TS_NAME

procedure endTableSpaceResync;

-- endTableSpaceResync must be called after the last call to checkTablespace.

-- Exceptions:
--    TS_RESYNC_NOT_STARTED


/*-----------------*
 * Datafile Resync *
 *-----------------*/

function beginDataFileResync(
  high_df_recid IN number
) return boolean;


-- beginDataFileResync must be called to start the process of resyncing 
-- information about datafiles. The tablespace information must be resync'd
-- before doing a datafile resync.

-- Exceptions:
--    TS_RESYNC_NOT_COMPLETE

procedure checkDataFile(file#       IN NUMBER,
			fname       IN VARCHAR2,
			create_scn  IN NUMBER,
			create_time IN DATE,
			blocks      IN NUMBER,
			block_size  IN NUMBER,
			ts#         IN NUMBER,
			stop_scn    IN NUMBER,
			read_only   IN NUMBER,
                        stop_time   IN DATE DEFAULT NULL);

-- 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

-- Exceptions:
--    DF_RESYNC_NOT_STARTED

procedure endDataFileResync;

-- endDataFileResync must be called after all datafiles have been passed in.

-- Exceptions:
--    DF_RESYNC_NOT_STARTED

procedure setDatafileSize(
  file#       IN number
 ,create_scn  IN number
 ,blocks      IN number
);


/*---------------------*
 * Redo Thread resync  *
 *---------------------*/

function beginThreadResync(
  high_rt_recid IN number
) return boolean;


-- beginThreadResync begins the redo thread resync.


procedure checkThread(
  thread#        IN number
 ,last_sequence# IN number
 ,enable_scn     IN number
 ,enable_time    IN date
 ,disable_scn    IN number
 ,disable_time   IN date
 ,status         IN varchar2
);

-- checkThread inserts or updates information about the given redo thread.
-- It must called once for each redo thread currently listed in the
-- control file in ascending thread# order. 

-- Input Parameters:
--   thread#
--     thread number
--   last_sequence#
--     last log sequence number allocated for this thread
--   enable_scn
--     SCN of the last thread enable or disable
--   create_time
--     timestamp of the last thread enable or disable
--   status
--     'E' -> enabled, 'D' -> disabled 

-- Exceptions:
--    RT_RESYNC_NOT_STARTED
--    BAD_RT_ORDER


procedure endThreadResync;

-- endThreadResync must be called after all redo threads have been checked



/*------------------------*
 * Online Redo Log resync *
 *------------------------*/

function beginOnlineRedoLogResync(
  high_orl_recid IN number
) return boolean;

-- beginOnlineRedoLogResync begins the redo log resync.

procedure checkOnlineRedoLog(
  thread#        IN number
 ,group#         IN number
 ,fname          IN varchar2
);

-- checkOnlineRedoLog inserts or updates information about the given redo log
-- It must called once for each redo log currently listed in the
-- control file in ascending fname order. 

procedure endOnlineRedoLogResync;

-- endOnlineRedoLogResync must be called after all logs have been checked



/*-----------------------------*
 * Redo Log History resync     *
 *-----------------------------*/

function beginLogHistoryResync return number;

-- beginLogHistoryResync begins the log history resync. It returns the 
-- highest recid of the log history entries recorded in the recovery catalog. 
-- The purpose of the returned recid is to avoid processing log history 
-- entries that are already in rcvcat.

procedure checkLogHistory(
  rlh_recid   IN number
 ,rlh_stamp   IN number
 ,thread#     IN number
 ,sequence#   IN number
 ,low_scn     IN number
 ,low_time    IN date
 ,next_scn    IN number
);
 
-- checkLogHistory inserts or checks the information about given log history
-- entry. If entry doesn't exist in rcvcat then it is inserted. If the entry 
-- already exists then it is compared with the given entry. If there
-- is a mismatch an internal error is signalled (the entries shouldn't change) 

-- checkLogHistory is called once for every redo log history entry  in the
-- controlfile with a recid greater than or equal to the one returned by 
-- beginLogHistoryResync. The idea is to check the last entry insert during the
-- previous checkpoint.  

-- Input Parameters:
--   rlh_recid
--     The controlfile recid of the redo log history entry
--   rlh_stamp
--     The controlfile stamp of the redo log history entry
--   thread#
--     Thread number.
--   sequence#
--     Log sequence number
--   low_scn
--     The low SCN of this log.
--   low_time
--     Timestamp associated with lowScn.
--   next_scn
--     The last SCN of this log.  All redo at this SCN or higher
--     is in a subsequent log sequence number.


procedure endLogHistoryResync;

-- endLogHistoryResync will complete redo log history resync and update the 
-- high_rlh_recid column in dbinc table.



/*-------------------------*
 * Archived Log resync     *
 *-------------------------*/

function beginArchivedLogResync return number;

-- beginArchivedLogResync begins archive log resync


procedure checkArchivedLog(
  al_recid    IN number
 ,al_stamp    IN number
 ,thread#     IN number
 ,sequence#   IN number
 ,reset_scn   IN number
 ,reset_time  IN date
 ,low_scn     IN number
 ,low_time    IN date
 ,next_scn    IN number
 ,next_time   IN date
 ,blocks      IN number
 ,block_size  IN number
 ,fname       IN varchar2
 ,archived    IN varchar2
 ,completion_time IN date
 ,status      IN varchar2
);

-- 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


procedure endArchivedLogResync;

-- endArchivedLogResync completes archived log resync and updates the 
-- high_al_recid column in dbinc table for the next resync.



/*-------------------------*
 * Offline range resync    *
 *-------------------------*/

function beginOfflineRangeResync return number;

-- beginOfflineRangeResync begins the offline range resync. It returns
-- the highest offline range recid that was recorded by the previous resync.


procedure checkOfflineRange(
  offr_recid     IN number
 ,offr_stamp     IN number
 ,file#          IN number
 ,create_scn     IN number
 ,offline_scn    IN number
 ,online_scn     IN number
 ,online_time    IN date
 ,cf_create_time IN date
);

-- checkOfflineRange inserts a offline range record in the rcvcat. 


-- Input Parameters:
--   offr_recid
--     recid of the offline range record in control file
--   offr_stamp
--     stamp of the offline range record in control file


procedure endOfflineRangeResync;

-- endOfflineRangeResync completes the offline range resync.



/*-------------------------*
 * Backup Set resync       *
 *-------------------------*/

PROCEDURE updateBackupSetRec(bs_key IN NUMBER);

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
);

-- 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


procedure endBackupSetResync;

-- endBackupSetResync completes the backup set resync.



/*-------------------------*
 * Backup piece resync     *
 *-------------------------*/

function beginBackupPieceResync return number;


procedure checkBackupPiece(
  bp_recid    IN number
 ,bp_stamp    IN number
 ,set_stamp   IN number
 ,set_count   IN number
 ,piece#      IN number
 ,tag         IN varchar2
 ,device_type IN varchar2
 ,handle      IN varchar2
 ,comments    IN varchar2
 ,media       IN varchar2
 ,concur      IN varchar2
 ,start_time  IN date
 ,completion_time IN date
 ,status      IN varchar2
 ,copy#       IN number default 1
 ,media_pool  IN number default 0
);

-- Input Parameters:
--
--   bp_recid
--     recid of backup piece record in control file
--   bp_stamp
--     stamp of backup piece record in control file
--   piece#
--     The ordinal number of this piece within the set.
--   tag
--     User-specified tag.  May be null.
--   device_type
--     Sequential device type on which this backup set resides.  'DISK'
--     specified if on disk rather than sequential media.
--   handle
--     The media handle on which this backup piece is stored.
--   comments
--     The comment associated with the backup piece.
--   media
--     The media id.  If multiple backup pieces reside on the same physical
--     piece of media, then they have the same mediaId.  Note this is not
--     the same as a handle, as the handle is specific to a single piece.
--   concur
--     TRUE if multiple job steps can access the physical media concurrently,
--     FALSE if not.


procedure endBackupPieceResync;



/*-------------------------*
 * Backup Datafile resync  *
 *-------------------------*/

function beginBackupDataFileResync return number;


procedure checkBackupDataFile(
  bdf_recid       IN number
 ,bdf_stamp       IN number
 ,set_stamp       IN number
 ,set_count       IN number
 ,file#           IN number
 ,create_scn      IN number
 ,create_time     IN date
 ,reset_scn       IN number
 ,reset_time      IN date
 ,incr_level      IN number
 ,incr_scn        IN number
 ,ckp_scn         IN number
 ,ckp_time        IN date
 ,abs_fuzzy_scn   IN number
 ,datafile_blocks IN number
 ,blocks          IN number
 ,block_size      IN number
 ,min_offr_recid  IN number
 ,completion_time IN date
);


-- 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.

procedure endBackupDataFileResync;



/*-------------------------*
 * Backup Redo Log resync  *
 *-------------------------*/

function beginBackupRedoLogResync return number;


procedure checkBackupRedoLog(
  brl_recid   IN number
 ,brl_stamp   IN number
 ,set_stamp   IN number
 ,set_count   IN number
 ,thread#     IN number
 ,sequence#   IN number
 ,reset_scn   IN number
 ,reset_time  IN date
 ,low_scn     IN number
 ,low_time    IN date
 ,next_scn    IN number
 ,next_time   IN date
 ,blocks      IN number
 ,block_size  IN number
);

-- Input Parameters:
--   recid
--     The controlfile recid of the archivelog entry.  Null if for a backup
--     set.
--   stamp
--     Timestamp associated with copyRecid.
--   thread#
--     Thread number.
--   sequence#
--     Log sequence number
--   reset_scn
--     The resetlogs SCN.
--   reset_time
--     Timestamp associated with resetScn.
--   low_scn
--     The low SCN of this log.
--   low_time
--     Timestamp associated with lowScn.
--   next_scn
--     The last SCN of this log.  All redo at this SCN or higher
--     is in a subsequent log sequence number.
--   next_time
--     Timestamep associated with nextScn.
--   blocks
--     Number of blocks in this archivelog.
--   block_size
--     Blocksize of the log.

procedure endBackupRedoLogResync;



/*----------------------------*
 * Copy Datafile resync       *
 *----------------------------*/

function beginDataFileCopyResync return number;


procedure checkDataFileCopy(
  cdf_recid       IN number
 ,cdf_stamp       IN number
 ,fname           IN varchar2
 ,tag             IN varchar2
 ,file#           IN number
 ,create_scn      IN number
 ,create_time     IN date
 ,reset_scn       IN number
 ,reset_time      IN date
 ,incr_level      IN number
 ,ckp_scn         IN number
 ,ckp_time        IN date
 ,onl_fuzzy       IN varchar2
 ,bck_fuzzy       IN varchar2
 ,abs_fuzzy_scn   IN number
 ,rcv_fuzzy_scn   IN number
 ,rcv_fuzzy_time  IN date
 ,blocks          IN number
 ,block_size      IN number
 ,min_offr_recid  IN number
 ,completion_time IN date
 ,status          IN varchar2
);

-- 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.

procedure endDataFileCopyResync;


/*----------------------------*
 * Corrupt Block resync       *
 *----------------------------*/

function beginBackupCorruptionResync return number;

procedure checkBackupCorruption(
  bcb_recid      IN number
 ,bcb_stamp      IN number
 ,set_stamp      IN number
 ,set_count      IN number
 ,piece#         IN number
 ,file#          IN number
 ,block#         IN number
 ,blocks         IN number
 ,corrupt_scn    IN number
 ,marked_corrupt IN varchar2
);

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
);

procedure endCopyCorruptionResync;

-- addCorrupt is called to add information about a block corruption in a
-- datafile copy or backup datafile.
--
-- Input Parameters:
--   bdf_key
--     Primary key of the bdf record which was returned by addBackupDatafile.
--   block#
--     The block number of the corrupt block.
--   corrupt_scn
--     SCN of the redo that corrupted the block for logically corrupt blocks.
--     Zero if the block is media corrupt.
--   marked_corrupt
--     'T' if we could not read the block from disk and had to reformat
--     the block as corrupt for storing in the backup set or copy.  'F'
--     otherwise.
--   piece#
--     The backup piece where the corrupt block was written.  Null if
--     this is for a datafile copy.



/*----------------------------*
 * Deleted Object resync      *
 *----------------------------*/

function beginDeletedObjectResync return number;

procedure checkDeletedObject(
  do_recid        IN number
 ,do_stamp        IN number
 ,object_type     IN varchar2
 ,object_recid    IN number
 ,object_stamp    IN number
);

procedure endDeletedObjectResync;

/*-------------------*
 * Proxy Copy resync *
 *-------------------*/

function beginProxyResync return number;

procedure checkProxyDataFile(
  xdf_recid       IN number
 ,xdf_stamp       IN number
 ,tag             IN varchar2
 ,file#           IN number
 ,create_scn      IN number
 ,create_time     IN date
 ,reset_scn       IN number
 ,reset_time      IN date
 ,incr_level      IN number
 ,ckp_scn         IN number
 ,ckp_time        IN date
 ,onl_fuzzy       IN varchar2
 ,bck_fuzzy       IN varchar2
 ,abs_fuzzy_scn   IN number
 ,rcv_fuzzy_scn   IN number
 ,rcv_fuzzy_time  IN date
 ,blocks          IN number
 ,block_size      IN number
 ,min_offr_recid  IN number
 ,device_type     IN varchar2
 ,handle          IN varchar2
 ,comments        IN varchar2
 ,media           IN varchar2
 ,media_pool      IN number
 ,start_time      IN date
 ,completion_time IN date
 ,status          IN varchar2
);

-- 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

procedure endProxyResync;

/*----------------------------*
 * Change Procedures          *
 *----------------------------*/

procedure changeDataFileCopy(
  cdf_recid IN number
 ,cdf_stamp IN number
 ,status    IN varchar2
);

-- changeDatafileCopy is called to change the availability or delete a 
-- datafile copy in recovery catalog
--
-- Input Parameters:
--   cdf_key
--     primary key of the datafile copy in recovery catalog
--   status
--     the new status
--     if status is 'A' or 'U', update the datafile copy status in rcvcat
--     if status is 'D', delete the datafile copy entry from rcvcat

procedure changeControlfileCopy(
  cdf_recid IN number
 ,cdf_stamp IN number
 ,status    IN varchar2
);

-- 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 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
);

-- changeProxyCopy is called to change the availability of or delete a
-- proxy copy


/*----------------------------*
 * Stored Script Procedures   *
 *----------------------------*/

procedure createScript(name IN varchar2);

procedure replaceScript(name IN varchar2);

-- These procedures create or replace a script with the given name.  Each
-- database registered in the catalog has its own namespace for scripts.
-- The putLine function must be called to pass the lines of the script
-- to the catalog.

procedure putLine(line IN varchar2);

-- This procedure passes 1 line of the script to the catalog.  The client
-- must call this until all lines have been passed in.  After the last line,
-- call putLine(NULL) to indicate that all lines have been passed in.
-- The maximum line length is 1024 bytes.

procedure deleteScript(name IN varchar2);

-- Deletes a script.

procedure getScript(name IN varchar2);

-- This procedure gets a script from the recovery catalog.  The client must
-- call getLine to fetch the lines of the script.

function getLine return varchar2;

-- This function returns 1 line of the stored script.  When all lines have
-- been passed back, it returns NULL.

procedure commitChanges;

-- Just does a commit;

/*---------------------------------------*
 * Procedures for clone database support *
 *---------------------------------------*/

procedure setCloneName(file#            IN number
                      ,creation_change# IN number
                      ,clone_fname       IN varchar2);

/*----------------------------*
 * Version info               *
 *----------------------------*/

function getPackageVersion return varchar2;
function getCatalogVersion return varchar2;

/*-------------------------*
 * Catalog upgrade support *
 *-------------------------*/

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

PROCEDURE bsStatusRecalc(status IN varchar2);

/*-------------------*
 * Utility functions *
 *-------------------*/

-- reNormalize is the interface to the recovery catalog used to apply a
-- one-time fixup on Windows-NT by re-applying the normalization algorithm,
-- which has been improved for 8.1.6.

-- The first call to reNormalize passes a null value for newname, to
-- initialize the procedure.  Each call returns an oldname which needs to be
-- normalized.  The caller normalizes the name and passes it back to
-- reNormalize, in newname, on the next call.  reNormalize will then update
-- the previous row and return the next one.  oldname is returned to the caller
-- as null when there are no more names.

PROCEDURE reNormalize(newname IN varchar2, oldname OUT varchar2);

end dbms_rcvcat;
>>>

# 
#    NAME
#      dbmsrman.sql - Recovery MANager package specification
#
#    DESCRIPTION
#      This package contains procedures querying information that
#      Recovery Manager needs from the recovery catalog or the target
#      database controlfile
#
#    NOTES
#      This package is only intended to be used by the Recovery Manager
#
#      Note that there are two implementations of this package. One is
#      a normal stored package (prvtrmnu.plb) that get its information from
#      the recovery catalog. The other one is a fixed package (prvtrmns.plb)
#      that gets its information from the target database controlfile.
#
#      Tags, tablepace names and filenames must be passed exactly as
#      stored in the controlfile and the recovery catalog. RMAN must
#      uppercase tag and tablespace names (unless quoted) and
#      normalize filenames before passing them to dbms_rcvman. 
#
#      Do not use boolean types in the package specification since
#      RMAN accesses it using embedded SQL. Use numbers instead,
#      0 (FALSE#) and 1 (TRUE#).
#
#    MODIFIED   (MM/DD/YY)
#       swerthei 06/01/98 - proxy copy name translation
#       swerthei 05/27/98 - proxy restore
#       dbeusee  05/18/98 - misc_81_fixes_1: added initialize(rman_vsn) and
#                           translateDatafile(fno, ckpscn)
#       dbeusee  04/21/98 - rpt_redundancy_enh                               
#       dbeusee  03/01/98 - list enh.                                        
#       gpongrac 12/22/97 - change findcontrolfilebackup
#       gpongrac 09/03/97 - have dfcopy name xlat return filesize
#       tpystyne 09/11/97 - bug 480172, fix name translation
#       swerthei 08/29/97 - add getdatafile.read_only
#       swerthei 08/18/97 - add getdatafile.stop_change#
#       dalpern  04/16/97 - make fixed packages a distinct "world"
#       gpongrac 04/11/97 - add cfscn to computeRecoveryActions
#       gpongrac 04/10/97 - add rlgtime to getRecoveryAction
#       gpongrac 04/04/97 - add translateBackupPieceTag
#       gpongrac 04/01/97 - add offline range info to computeRecoveryActions
#       swerthei 03/27/97 - add getDataFile.ts_name
#       swerthei 03/12/97 - get file sizes in blocks, for metrics
#       gpongrac 03/15/97 - add to_time for implicit offline ranges in comput
#       gpongrac 03/11/97 - remove finddatafilebackup and friends
#       gpongrac 03/10/97 - add function to set debug flag
#       gpongrac 03/03/97 - add success return code for computeRecoveryAction
#       gpongrac 02/28/97 - move comment to body
#       gpongrac 02/27/97 - add cf_current as arg to computeRecoveryActions
#       gpongrac 02/26/97 - fix type
#       gpongrac 02/26/97 - add new recovery methods
#       swerthei 01/21/97 - change parameters for translateBackupSetKey
#       swerthei 01/15/97 - add dumpState
#       gpongrac 01/14/97 - add getCloneName
#       tpystyne 12/17/96 - add listRollbackSegTableSpace
#       swerthei 01/08/97 - change parameters for backup piece translation
#       gpongrac 01/06/97 - add nxtscn to getArchivedLog
#       swerthei 01/03/97 - add getParentIncarnation
#       swerthei 12/30/96 - use named constants for backup/copy types
#       swerthei 12/27/96 - add reportTranslateDFDel, reportGetDFDel
#       swerthei 12/20/96 - add getDataFile.kbytes
#       gpongrac 11/14/96 - add setDbincKey
#       tpystyne 11/13/96 - add reset_scn and time to findDataFileBackup
#       swerthei 11/08/96 - change parms to translateArchivedLogKey
#       swerthei 11/08/96 - add findPrevDataFileBackup.ckptime
#       tpystyne 10/29/96 - change create_scn to an in parameter
#       swerthei 11/05/96 - add findDataFileBackup.ckp_time
#       swerthei 11/04/96 - add getdatafile.creation_time
#       swerthei 11/01/96 - honor setDeviceType in list functions
#       swerthei 10/31/96 - add setDeviceTypeAny
#       swerthei 10/31/96 - add getDataFile.unrecoverable_change#
#       swerthei 10/28/96 - add functions for LIST INCARNATION OF DATABASE
#       swerthei 10/23/96 - add new functions for LIST command
#       tpystyne 10/18/96 - online redo log support
#       gpongrac 10/07/96 - have getDatafile return blksize too
#       gpongrac 10/02/96 - add getCheckpoint
#       gpongrac 09/24/96 - change getDataFile to return creation SCN and fna
#       swerthei 09/12/96 - add getDataFileLocation
#       tpystyne 08/26/96 - add findOfflineRangeBackup
#       gpongrac 07/31/96 - add translateArchivedLogCancel
#       tpystyne 07/10/96 - change translateBackupSet prototypes
#       tpystyne 06/24/96 - add getIncrementalScn
#       tpystyne 06/13/96 - add findPrevDataFileBackup
#       swerthei 06/11/96 - add validation parameters to findDataFileBackup
#       tpystyne 05/28/96 - separate translateArchiveLogRangeName and Pattern
#       tpystyne 05/22/96 - add fname parameter to findDataFileBackup
#       gpongrac 05/13/96 - fix merge errors
#    tpystyne    05/13/96 - complete beta1 functionality
#    asurpur     04/09/96 - Dictionary Protection Implementation
#    tpystyne    05/07/96 - implement restore procedures
#    tpystyne    04/24/96 - enable restore procedures
#    gpongrac    03/22/96 - add restore procedures
#    tpystyne    02/20/96 - more stuff
#    gpongrac    01/25/96 - remove domain arg from setDatabase
#    tpystyne    12/06/95 - rename procedures
#    tpystyne    12/05/95 - Created
#

define dbmsrman_sql
<<<
create or replace package dbms_rcvman is

-- DE-HEAD       <- tell SED where to cut

----------------------------------------
-- PUBLIC VARIABLES AND TYPES SECTION --
----------------------------------------

TRUE#  CONSTANT number := 1;
FALSE# CONSTANT number := 0;

-- The values here must never be changed, because the 8.0 rman executables have
-- these values hard-coded in the krmkbt enum in krmk.h.  The setFrom procedure
-- in particular is using hard-coded values.

-- The 8.1.5 rman executable calls a procedure, set_package_constants, that
-- re-assigns these constants to whatever the package needs them to be, then
-- queries the package for their new values.  The 8.1.5 rman does not care
-- what the values are, however, the cursor used by reportGetDFDel used to use
-- these values to perform an order-by to return rows in preference order.
-- The preference order is used to decide which ones to delete.

-- As of 8.1.6, the order-by in reportGetDFDel is independant of these values.
-- The 8.1.6 rman does not use these values at all, except in setFrom.
-- However, for backwards compatibility with the 8.1.5 RMAN, these must remain
-- as public package constants.

COPY                  number := 1; -- any image copy of a file
FULL_DF_BACKUP        number := 2; -- datafile in a full backup set
INCREMENTAL_DF_BACKUP number := 3; -- datafile in an incr backup set
BACKUP                number := 4; -- any file in a backup set
OFFLINE_RANGE         number := 5; -- an offline range
CUMULATIVE            number := 6; -- cumulative incremental - for LIST only
PROXY                 number := 7; -- any proxy copy of a file

-- Recovery Action Kinds (Obsolete as of 8.1.6) --

implicitOfflRange CONSTANT NUMBER := 2**0;
cleanRange        CONSTANT NUMBER := 2**1;
applyOfflRange    CONSTANT NUMBER := 2**2;
dfCopy            CONSTANT NUMBER := 2**3;
proxyFull         CONSTANT NUMBER := 2**4;
buSet             CONSTANT NUMBER := 2**5;
applyIncremental  CONSTANT NUMBER := 2**6;
redo              CONSTANT NUMBER := 2**7;

-- kind masks
maxKind           CONSTANT NUMBER := redo; 	      -- last real kind above
allKind           CONSTANT NUMBER := (maxKind*2) - 1; -- all real backup types
fullKind          CONSTANT NUMBER := dfCopy + proxyFull + buSet;
tagKind           CONSTANT NUMBER := fullKind + applyIncremental;

-- pseudo kinds
deletedKind       CONSTANT NUMBER := maxKind*2;      -- action deleted

---------------------
-- Datafile Record --
---------------------

TYPE dfRec_t IS RECORD
(
   dfNumber		number,
   dfCreationSCN	number,
   dfCreationTime	date,
   fileName		varchar2(1024),
   tsName		varchar2(30),
   status               number,
   blocks		number,
   blockSize		number,
   kbytes		number,
   unrecovSCN           number,
   stopSCN		number,
   readOnly		number
);

-------------------------
-- Archived Log Record --
-------------------------

TYPE alRec_t IS RECORD
(
   key			number,
   recid		number,
   stamp		number,
   thread		number,
   sequence		number,
   fileName		varchar2(1024),
   lowSCN		number,
   lowTime		date,
   nextSCN		number,
   nextTime		date,
   rlgSCN		number,
   rlgTime		date,
   blocks		number,
   blockSize		number,
   status		char(1),
   compTime		date
);

-- All of the queries which return data about a backup/imagecopy/proxycopy
-- select into a rcvRec_t record type.  We have standardized all of our
-- queries to have a common select-list and the results of the queries are
-- returned through a common public package function.  The reason for this is
-- so that krmk.pc can populate its internal data structures consistantly,
-- regardless of what particular procedure it has called to query the catalog.

-- By having all queries select into the same record type, we can ensure
-- that all queries use the same select list.  Any new fields that get added
-- to this record will require updating the select lists of all queries.
-- Failure to make the correct updates will result in PLSQL giving an error
-- when the package body is re-created, so the error will be easily detected
-- without the need to run any test suite.

-- The record is divided into three sections.  These correpond to
-- three krmk.h data structures which will be populated with the data
-- from this record.  Refer to krmk.h for a description of the purpose
-- of each of these three data strucutres.

-- Think of this as:  the container acts on the object.

---------------------
-- Recovery Record --
---------------------

-- NOTE!!!!!
--
-- If you add a new field here, update the ORDER BY in openRecoveryActionCursor
--

TYPE rcvRec_t IS RECORD
(
   -- *** Recovery Container Section ***

   type_con		number(3),	-- recovery container type
   key_con		number(15),	-- primary key
   recid_con		number(10),	-- recid
   stamp_con		number(10),	-- stamp
   setStamp_con		number(10),	-- set count if backup set (null)
   setCount_con		number(10),	-- set stamp if backup set (null)
   bsRecid_con		number(10),	-- backup set recid (null)
   bsStamp_con		number(10),	-- backup set stamp (null)
   bsKey_con            number(15),	-- backup set key (null)
   bsLevel_con          number(1),	-- backup set level (null)
   bsType_con		char(1),	-- backup set type
   pieceCount_con	number(2),	-- backup set piece count (null)
   fileName_con		varchar2(1024),	-- filename if a copy (null)
   tag_con		varchar2(32),	-- tag (null)
					-- filled in by addAction() for
					-- backup sets
   copyNumber_con	number(1),	-- backup set copy# (null)
					-- filled in by addAction() only
   status_con		char(1),	-- status (null)
   blocks_con		number(10),	-- size of file in blocks (null)
   blockSize_con	number(5),	-- block size (null)
   deviceType_con	varchar2(255),	-- device type required (null)
					-- filled in by addAction() for
					-- backup sets
   compTime_con		date,		-- completion time
   cfCreationTime_con   date,		-- controlfile creation time if
					-- offline range (null)

   -- *** Recovery Action Section ***

   type_act		number(2),	-- recovery action type
   fromSCN_act		number(15),
   toSCN_act		number(15),
   toTime_act		date,
   rlgSCN_act		number(15),
   rlgTime_act		date,
   dbincKey_act		number(15),
   level_act		number(1),

   -- *** Recovery Object Section ***

   dfNumber_obj		number(6),
   dfCreationSCN_obj	number(15),
   logSequence_obj	number(10),
   logThread_obj	number(4),
   logRlgSCN_obj	number(15),
   logRlgTime_obj	date,
   logLowSCN_obj	number(15),
   logLowTime_obj	date,
   logNextSCN_obj	number(15),
   logNextTime_obj	date
);

------------------------------
-- Recovery Container Types --
------------------------------

-- NOTE:  Order is important, it is used in an ORDER BY.

offlineRangeRec_con_t	CONSTANT NUMBER := 2**0;
proxyCopy_con_t		CONSTANT NUMBER := 2**1;
imageCopy_con_t		CONSTANT NUMBER := 2**2;
backupSet_con_t		CONSTANT NUMBER := 2**3;
deleted_con_t		CONSTANT NUMBER := 2**8;

-- Masks
backupMask_con_t	CONSTANT NUMBER := proxyCopy_con_t + imageCopy_con_t +
                                           backupSet_con_t;
tagMask_con_t		CONSTANT NUMBER := proxyCopy_con_t + imageCopy_con_t +
                                           backupSet_con_t;

---------------------------
-- Recovery Action Types --
---------------------------

full_act_t		CONSTANT NUMBER := 2**0;
incremental_act_t	CONSTANT NUMBER := 2**1;
redo_act_t		CONSTANT NUMBER := 2**2;
offlineRange_act_t	CONSTANT NUMBER := 2**3;
cleanRange_act_t	CONSTANT NUMBER := 2**4;
implicitRange_act_t	CONSTANT NUMBER := 2**5;
spanningRange_act_t	CONSTANT NUMBER := 2**6;

-----------------------------------------
-- Recovery Record Returning Functions --
-----------------------------------------

-- These defines are used ad the funCode arg to getRcvRec to tell it which
-- function it should call.  We do this so that krmk.pc can have a single
-- interface routine for getting a rcvRec_t.

getCfCopy		CONSTANT NUMBER := 0;
getDfCopy               CONSTANT NUMBER := 1;
getAnyProxy		CONSTANT NUMBER := 2;
getCfBackup		CONSTANT NUMBER := 3;
listCfCopy		CONSTANT NUMBER := 4;
listDfCopy		CONSTANT NUMBER := 5;
listCfBackup		CONSTANT NUMBER := 6;
listDfBackup		CONSTANT NUMBER := 7;
listAlBackup		CONSTANT NUMBER := 8;
listDfProxy		CONSTANT NUMBER := 9;
getRecovAction		CONSTANT NUMBER := 10;
getAlBackup		CONSTANT NUMBER := 11;
listAlCopy              CONSTANT NUMBER := 12;

-----------------------
-- Backup Set Record --
-----------------------

TYPE bsRec_t IS RECORD
(
   recid		number,
   stamp		number,
   key                  number,
   setStamp		number,
   setCount		number,
   bsType               char(1),
   level		number,
   compTime             date,
   status               char(1),
   pieceCount		number
);

------------------------
-- Backup Piece Record --
-------------------------

TYPE bpRec_t IS RECORD
(
   recid		number,
   stamp		number,
   key			number,
   setStamp		number,
   setCount		number,
   pieceNumber		number,
   copyNumber		number,
   status		char(1),
   compTime		date,
   handle		varchar2(1024),
   tag                  varchar2(32)
);

---------------------------------
-- Backupset Validation Record --
---------------------------------

TYPE validBackupSetRec_t IS RECORD
(
   deviceType   varchar2(255),
   tag		varchar2(32),			-- may be null
   copyNumber   number,				-- null if code 2 or 3
   code         number				-- 1 => same copy#
						-- 2 => mix of copy#s, but
                                                --      same tag
                                                -- 3 => mix of copy#s and tags
);

-----------------------------------------------------
-- PUBLIC FUNCTION/PROCEDURE SPECIFICATION SECTION --
-----------------------------------------------------

----------------------------------------
-- Debugging functions and procedures --
----------------------------------------

FUNCTION dumpState(
   lineno IN number)
RETURN varchar2;

PROCEDURE setDebugOn;

PROCEDURE setDebugOff;

----------------------------
-- Package Initialization --
----------------------------
 
-- This is a vestigal function that was released to customers in 8.1.3 Beta.
-- It is no longer called, and is no longer needed, but must still be here
-- because this version of the package may be called by an 8.1.3 rman
-- executable.

PROCEDURE initialize(rman_vsn IN number);

-- Used by 8.1.5 to re-assign the order of the backup_type constants to their
-- correct order.  This procedure is not called by 8.1.4-, so the constants
-- will reamin set to the above values for those executables.

PROCEDURE set_package_constants;

-----------------------
-- Utility functions --
-----------------------

FUNCTION stamp2date(stamp IN number) RETURN date;

------------------------------
-- Set Database Incarnation --
------------------------------

-- setDatabase selects which target database subsequent dbms_rcvman 
-- 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 

-- When dbms_rcvman package executes against the target database controlfile,
-- setDatabase just returns without doing anything.

-- 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:
--   DATABASE_NOT_FOUND (ORA-20001)
--     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 (ORA-20003)
--     No database incarnation matches the given arguments
--     The database incarnation must be registered using resetDatabase first

PROCEDURE setDatabase(
   db_name    IN varchar2
  ,reset_scn  IN number
  ,reset_time IN date
  ,db_id      IN number);

-- setDbincKey used in lieu of setDatabase for when SET DBID command is
-- issued.

PROCEDURE setDbincKey(
   key IN number);

-- getParent Incarnation returns the parent incarnation.  If resetlogs_change#
-- is NULL on input, then the current incarnation is returned.  Returns TRUE
-- if a row was returned, otherwise returns FALSE.

FUNCTION getParentIncarnation(
   resetlogs_change# IN OUT number
  ,resetlogs_time    IN OUT date)
RETURN number;

-- getCheckpoint gets and returns the highest recovery catalog checkpoint SCN
-- for FULL checkpoints.  This SCN indicates how current the datafilenames and
-- lognames in the recovery catalog are.  This SCN can be compared with a
-- backup controlfile SCN to decide which name to use if they differ.

PROCEDURE getCheckpoint(
   scn OUT number
  ,seq OUT number);

-------------------
-- Query Filters --
-------------------

-- setCompletedRange sets completedBefore and/or completedAfter filters for
-- use by computeRecoveryActions.
-- setLikePattern sets fileName patter for computeRecoveryActions.
--
-- setUntilTime, setUntilScn, setUntilLog, resetUntil These procedures are
-- used to inform dbms_rcvman of an until_clause.  The setUntil remains in
-- effect until another setUntil has been called, or until resetUntil has been
-- called.  If none of these have been called, then all queries for name
-- translation, restore, and recovery should assume that a complete recovery
-- is being done.  Otherwise, all restore and recovery queries should limit
-- their replies to backup sets and datafile copies that are appropriate for
-- use in an incomplete recovery until the specified until condition.  Name
-- translations should be done relative to the specified epoch.
--
-- "appropriate" means that the fuzziness of the backup datafile or datafile
-- copy ends at an SCN less than the untilChange SCN (for untilChange), or the
-- low SCN of the specified log (for untilLog), or the fuzziness timestamp is
-- less than the specified time (for unttime).  Note that datafiles have three
-- kinds of fuzziness, all of which must be less than the specified SCN or
-- time.  If the fuzziness of a datafile is unknown, then it should be
-- ignored.
--
-- The setUntil procedures will signal an error when executed against
-- the target database controlfile. The resetUntil procedure can be
-- executed against the controlfile, it but doesn't have any effect.

-- Input parameters:
--   unttime
--     The incomplete recovery will stop when this timestamp is reached
--     in the redo log.
--   scn
--     The incomplete recovery will stop when this scn is reached in the redo
--     log.
--   sequence#, thread#
--     The incomplete recovery will stop when this log becomes the very next
--     log to be applied.
--
-- Exceptions:
--   NO_RECOVERY_CATALOG (ORA-20300)
--     this operation is not supported without the recovery catalog
--   SEQUENCE_IS_NULL (ORA-20205)
--     A null log sequence# was given
--   LOG_MISSING (ORA-20206)
--     No log with the give thread# and sequence# was found

PROCEDURE setCompletedRange(
   after  IN date
  ,before IN date);

PROCEDURE setLikePattern(
   pattern IN varchar2);

-- Obsolete as of 8.1.6
PROCEDURE setAllFlag(
   flag IN boolean);

PROCEDURE setAllIncarnations(
   flag IN boolean);

PROCEDURE setUntilTime(
   unttime IN date);

PROCEDURE setUntilScn(
   scn IN number);

PROCEDURE setUntilLog(
   sequence# IN number
  ,thread#   IN number);

FUNCTION getUntilTime return date;

FUNCTION getUntilScn return number;

PROCEDURE resetUntil;

-- setFrom is used to limit the potential restore candidates to either
-- backup sets or datafile copies, or to allow either kind of file to
-- be used.
--
-- Input parameters:
--   restorefrom
--     One of BACKUP, COPY, or NULL.

PROCEDURE setFrom(
   restorefrom IN number DEFAULT NULL);

-- setDeviceType specifies the type of an allocated device.  It is called 1 or
-- more (up to 8) times , depending on the number of different device types
-- that are allocated.  dbms_rcvman should return only files that can be
-- accessed through one of the device types specifed through this call.
--
-- Input parameters:
--   type
--     type of the device
-- Exceptions:
--   NULL_DEVICE_TYPE
--     A null device type was specied
--   TOO_MANY_DEVICE_TYPES
--     At most 8 device types can be specified

PROCEDURE setDeviceType(
   type IN varchar2);

-- setDeviceTypeAny is an alternative to setDeviceType.  It causes dbms_rcvman
-- to return a backup set on ANY device type.

PROCEDURE setDeviceTypeAny;

-- resetDeviceType resets the list of device types to null.

PROCEDURE resetDeviceType;

-- setTag is used to limit the restore candidates to backups and copies with
-- the given tag. If the tag is NULL then all backups and copies are searched
-- by the find functions.
--
-- Input parameters:
--   tag
--     tag of the datafile copies to be translated
--     The tag must be passed in uppercase ### ok?

PROCEDURE setTag(tag IN varchar2 DEFAULT NULL);

-- resetAll calls resetUntil, setFrom, resetDevice and setTag to reset
-- everything. 

PROCEDURE resetAll;

---------------------------
-- Backup Set Validation --
---------------------------

-- Use the findValidBackupSetRec public variable to save a backupset record
-- for later use as an input argument to this procedure.

findValidBackupSetRcvRec rcvRec_t;		-- place to save a rcvRec_t

PROCEDURE findValidBackupSet(
   backupSetRec		   IN     rcvRec_t
  ,deviceType		   IN	  varchar2 DEFAULT NULL
  ,tag			   IN     varchar2 DEFAULT NULL
  ,available               IN     number DEFAULT TRUE#
  ,unavailable             IN     number DEFAULT FALSE#
  ,deleted                 IN     number DEFAULT FALSE#
  ,expired                 IN     number DEFAULT FALSE#);

findValidBackupSetBsRec  bsRec_t;		-- place to save a bsRec_t

PROCEDURE findValidBackupSet(
   backupSetRec		   IN     bsRec_t
  ,deviceType		   IN	  varchar2 DEFAULT NULL
  ,tag			   IN     varchar2 DEFAULT NULL
  ,available               IN     number   DEFAULT TRUE#
  ,unavailable             IN     number   DEFAULT FALSE#
  ,deleted                 IN     number   DEFAULT FALSE#
  ,expired                 IN     number   DEFAULT FALSE#);

FUNCTION getValidBackupSet(
   validBackupSetRec		OUT validBackupSetRec_t
  ,checkDeviceIsAllocated  	IN  number DEFAULT FALSE#)
RETURN number;					-- TRUE# -> got a record
						-- FALSE# -> no_data_found

---------------------
-- Get an rcvRec_t --
---------------------

-- This function is a cover function for all procedures/functions that
-- return a rcvRec_t.  It routes the call to the correct procedure.  It
-- is provided for the convienence of krmk.pc.  The function return value
-- is whatever the underlying function returns.  If we call a procedure,
-- then getRcvRec returns 0.  Refer to the funCode list above in the
-- types/variables section.

FUNCTION getRcvRec(
   funCode	IN number
  ,rcvRec       OUT rcvRec_t
  ,callAgain    OUT number)
RETURN number;

--------------------------
-- Datafile Translation --
--------------------------

-- translateTableSpace translates a tablespace name into a list of datafile
-- numbers.  translateDataBase translates the database into a list of datafile 
-- numbers in the database excluding datafiles belonging to tablespaces
-- specified using skipTableSpace.  The translation is performed relative to
-- epoch setting currently in use.  getDataFile is used to obtain the datafile
-- numbers, one at a time until null is returned.

-- When doing the translation relative to current time the client should
-- ensure that recovery catalog is up-to-date. When doing translations
-- relative to an point-in-time in the past two potential anomalies may
-- show up.
--
-- 1) files belonging to a tablespace that was dropped before the point-in-time
-- may be returned since the drop_scn and drop_time are approximations.
-- As a result of this point-in-time recovery will restore and recover
-- a tablespace which will be dropped before the database is opened.
-- No real harm, just extra work for the recovery. And this won't happen
-- if rcvcat is resynced immediatly after dropping a tablespace.
-- 2) A tablespace which is created and dropped between two consecutive
-- recovery catalog resyncs will never be recorded in the rcvcat. It is
-- conceivable that such a tablespace existed at the intended point-in-time.
-- As a result the tablespace will not be recovered and must be dropped
-- after the database is opened. The worst case scenario is that a rollback
-- segment was also created in this tablespace. The recovered database
-- might fail to rollback some transactions. Again, this won't happen if
-- rcvcat is always resynced after creating a tablespace.
-- PS. These anomalies won't occur if the point-in-time is chosen to be
-- a rcvcat checkpoint.

-- Input parameters:
--   ts_name
--     name of the tablespace to be translated or skipped.
--     The name must be in uppercase
-- Exceptions:
--   TABLESPACE_DOES_NOT_EXIST (ORA-20202)
--     the tablespace to be translated does not exists (does not have any
--     datafiles). Check that the recovery catalog is current.
--   TRANSLATION_IN_PROGRESS (ORA-20203)
--     the previous translation conversation is still in progess.
--     To terminate get all datafiles with getDataFile.
--   TRANSLATION_NOT_IN_PROGRESS (ORA-20204)
--     getDataFile was called with no translation in progress

PROCEDURE translateDatabase(
   sinceUntilSCN IN number DEFAULT NULL);

PROCEDURE skipTableSpace(
   ts_name IN varchar2);

PROCEDURE translateTablespace(
   ts_name IN varchar2);

-- translateDataFile translates the datafile name/number into 
-- a datafile number and creation SCN and filename.  getDataFile must
-- be called to obtain the translation info, just as for the other translate
-- functions.
-- Unlike the other translation functions, translateDatafile by name is always
-- performed relative to current time.  If an until setting is in effect,
-- and if the filename is ambiguous, then an exception is raised.  Ambiguous
-- means that the filename refers to different datafile at the until time than
-- it does at the current time.  This happens only when a filename has been
-- reused.  When fno and ckpscn are passed, the filename and other info as of
-- that scn is returned.

-- Input parameters:
--   fname
--     name of the datafile to be translated.
--     The name must be a normalized filename.
--   fno
--     The datafile number.  If the datafile number was not in use at the
--     until time, then an exception is raised.
-- Exceptions:
--   DATAFILE_DOES_NOT_EXIST (ORA-20201)
--     the datafile to be translated does not exists
--     Check that the recovery catalog is current.

PROCEDURE translateDataFile(
   fname IN varchar2);

PROCEDURE translateDatafile(
   fno   IN number);

PROCEDURE translateDatafile(
   fno    IN number
  ,ckpscn IN number);

-- translateAllDatafile returns a list of all datafiles that ever
-- existed in this database.

PROCEDURE translateAllDatafile;

PROCEDURE getDatafile(
   dfRec     OUT dfRec_t
  ,oldClient IN  boolean DEFAULT FALSE);

-- Obsolete as of 8.1.6
PROCEDURE getDataFile(
   file# 	OUT number
  ,crescn 	OUT number
  ,creation_time OUT date
  ,fname 	OUT varchar2
  ,ts_name 	OUT varchar2
  ,status 	OUT number
  ,blksize 	OUT number
  ,kbytes 	OUT number
  ,blocks 	OUT number
  ,unrecoverable_change# OUT number
  ,stop_change# OUT number
  ,read_only 	OUT number);

----------------------------
-- Online Log Translation --
----------------------------

-- translateOnlineLogs translates the database to a list of online redo logs.
-- The translation is always performed relative to current epoch. 

PROCEDURE translateOnlineLogs;

PROCEDURE getOnlineLog(
   fname 	OUT varchar2
  ,thread# 	OUT number 
  ,group# 	OUT number); 

-----------------------------
-- Archivedlog Translation --
-----------------------------

-- translateArchivedLogKey translates the archived log key to a archived
-- log recid and stamp in V$ARCHIVED_LOG.

-- translateArchivedLogRange* procedures translate a specified 
-- archive log range to a list of archived logs.
-- getArchivedLog is used to get the recid and stamp for each archived log,
-- one at a time until null is returned.

-- The available, unavailable and deleted parameters are used to limit
-- the translation to archived logs with the desired status. For example,
-- only available archived logs can be backed up, but unavailable and deleted
-- archived logs can be restored from backups.

-- The duplicates parameter controls whether the translation returns all
-- archived logs or eliminates duplicate ones. Archived logs that have the
-- same thread#, sequence# and low_scn are considered duplicates. (duplicate 
-- archived logs are usually created by copying archived logs). 

-- Note that only archived logs recorded in the recovery catalog or
-- controlfile file are returned. If there is an archived log that belongs
-- to the range but is not known, there will be a "hole" in the range. 

-- Input parameters:
--    al_key
--      key of the archived log record in the recovery catalog
--   thread#
--     return only logs that belong to this thread#
--     if NULL return logs for all threads 
--   fromseq#
--     lowest log sequence number in the range
--   toseq#
--     highest log sequence number in the range
--   fromtime
--     exclude logs that were switched out before fromtime 
--   totime
--     exclude logs that were switched in after totime
--   fromscn
--     exclude logs that were switched out before fromscn 
--   toscn
--     exclude logs that were switched in after toscn
--   pattern
--     return only archived logs whose filename match the pattern 
--     The pattern is matched against normalized filenames ### ok?
--   available
--     if TRUE (1) return available archived logs
--   unavailable
--     if TRUE (1) return unavailable archived logs
--   deleted
--     if TRUE (1) return deleted archived logs
--   online
--     if TRUE (1) return also inspected online logs (in addition to 
--     archived logs)
--   duplicates
--     if TRUE (1) return all archived logs
--     if FALSE (0) eliminate duplicate archived logs
-- Output parameters:
--    recid
--      recid of the archived log record (in V$ARCHIVED_LOG)
--    stamp
--      stamp of the archived log record (in V$ARCHIVED_LOG)
--    thread#
--    sequence#
--    low_scn
--    fname
--    reset_scn
--    block_size
-- Exceptions:
--  NO_RECOVERY_CATALOG (ORA-20300)
--    this operation is not supported without the recovery catalog
--  ARCHIVED_LOG_DOES_NOT_EXIST  
--    the key does not match any archived log
--   TRANSLATION_IN_PROGRESS (ORA-20203)
--     the previous translation conversation is still in progess.
--     To terminate get all datafiles with getArchivedLog.
--   TRANSLATION_NOT_IN_PROGRESS (ORA-20204)
--     getArchivedLog was called with no translation in progress
--   THREAD_IS_NULL (ORA-20210)
--     a null thread# was passed to translateArchivedLogSeqRange
--   HIGH_SEQUENCE_IS_NULL
--     a null toseq# was passed to translateArchivedLogSeqRange
--   UNTIL_TIME_IS_NULL (ORA-20212)
--     a null totime was passed to translateArchivedLogTimeRange
--   UNTIL_SCN_IS_NULL (ORA-20213)
--     a null toscn was passed to translateArchivedLogSCNRange
--   ARCHIVED_LOG_RANGE_IS_EMPTY
--     the specified range doesn't contain any archived log

------------------------------
-- Archived Log Translation --
------------------------------

PROCEDURE getArchivedLog(
   alRec       OUT alRec_t
  ,closeCursor IN  boolean DEFAULT FALSE);

PROCEDURE translateArchivedLogKey(
   alKey 	IN number);

-- Obsolete as of 8.1.6
PROCEDURE translateArchivedLogKey(
   al_key 	IN number
  ,available 	IN number
  ,unavailable	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,recid 	OUT number
  ,stamp	OUT number
  ,thread# 	OUT number
  ,sequence# 	OUT number
  ,low_scn 	OUT number
  ,reset_scn 	OUT number
  ,block_size 	OUT number
  ,fname 	OUT varchar2);

PROCEDURE translateArchivedLogName(
   fname 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number);

PROCEDURE translateArchivedLogSeqRange(
   thread# 	IN number
  ,fromseq# 	IN number
  ,toseq# 	IN number
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number);

PROCEDURE translateArchivedLogTimeRange(
   thread# 	IN number
  ,fromTime 	IN date
  ,toTime 	IN date
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number);

PROCEDURE translateArchivedLogSCNRange(
   thread# 	IN number
  ,fromSCN 	IN number
  ,toSCN 	IN number
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number);

PROCEDURE translateArchivedLogPattern(
   pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number);

PROCEDURE translateArchivedLogCancel;

-- translateBackupPieceKey looks up a backup piece by primary key.
-- translateBackupPieceHandle looks up a backup piece by handle and deviceType.
-- translatebackupPieceTag looks up backup pieces by tag.

-- The available are unavailable parameters are used to limit the translation
-- to backup pieces with the desired status. For example, only available 
-- backup pieces can be backed up, but unavailable pieces can be made
-- available.  Deleted backup pieces are never returned.

-- Input parameters:
--    bp_key
--      key of the backup piece record in the recovery catalog
--    handle
--      backup piece handle
--    device type
--      device type on which the backup piece resides
-- Exceptions:
--   NO_RECOVERY_CATALOG (ORA-20300)
--     this operation is not supported without the recovery catalog
--   BACKUP_PIECE_DOES_NOT_EXIST
--     the key does not match any backup piece
--   BACKUP_PIECE_HANDLE_IS_AMBIGUOUS
--     the key does not match any backup piece

-- Obsolete as of 8.1.6
PROCEDURE getArchivedLog(
   recid 	OUT number
  ,stamp 	OUT number
  ,thread# 	OUT number
  ,sequence# 	OUT number
  ,low_scn 	OUT number
  ,nxt_scn 	OUT number
  ,fname 	OUT varchar2
  ,reset_scn 	OUT number
  ,block_size 	OUT number
  ,blocks 	OUT number);

---------------------------------
-- Controlfilecopy Translation --
---------------------------------

-- translateControlFileCopyName translates a control file name into a list of
-- control file copies.
-- Input parameters:
--   fname
--     name of the controlfile copy to be translated.
--     The name must be a normalized filename
-- Exceptions:
--   CONTROLFILE_COPY_DOES_NOT_EXIST
--     The filename does not match any controlfile copy

PROCEDURE translateControlFileCopyName(
   fname 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,duplicates 	IN number);

PROCEDURE getControlFileCopy(
   rcvRec	IN OUT rcvRec_t);

-- Obsolete as of 8.1.6
PROCEDURE getControlFileCopy(
   recid 	OUT number
  ,stamp 	OUT number
  ,reset_scn 	OUT number
  ,ckp_scn 	OUT number
  ,block_size 	OUT number);

------------------------------
-- Datafilecopy Translation --
------------------------------

PROCEDURE getDataFileCopy(
   rcvRec 	OUT rcvRec_t
  ,closeCursor  IN  boolean DEFAULT FALSE);

-- translateDataFileCopyKey translates the datafile copy key into a
-- datafile copy recid and stamp in V$DATAFILE_COPY. 

-- translateDataFileCopyNumber translates a file number and (optional) tag
-- to a datafile copy recid and stamp. Not used currently in 8.0.

-- translatedDataFileCopyName translates the datafile copy name into a
-- a list of datafile copies and getDataFileCopy returns the recid and stamp 
-- of each datafile copy. The duplicates parameter controls whether 
-- getDataFileCopy returns all matching datafile copies or just the most 
-- recent copy (highest stamp in rcvcat or highest recid in controlfile).

-- translateDataFileCopyTag translates the tag into a list of datafile
-- copies and getDataFileCopy returns the recid and stamp of each datafile copy
-- one at a time until null is returned.

-- The available are unavailable parameters are used to limit the translation
-- to datafile copies with the desired status. For example, only available 
-- datafile copies can be backed up, but unavailable copies can be made
-- available. Deleted copies are never returned.

-- The duplicates parameter controls whether getDataFileCopy returns all
-- datafile copies or just the most recent (highest checkpoint scn) copy 
-- of each datafile (file#).

-- Input parameters:
--   cdf_key
--     key of the datafile copy record in the recovery catalog
--   fname
--     name of the datafile copy to be translated.
--     The name must be a normalized filename
--   tag
--     tag of the datafile copies to be translated
--     The tag must be passed exactly as stored in the controlfile,
--     it is not uppercased by translate
--   available
--     if TRUE (1) return available datafile copies
--   unavailable
--     if TRUE (1) return unavailable datafile copies
--   duplicates
--     if TRUE (1) return all datafile copies
--     if FALSE (0) eliminate duplicate datafile copies
--
--    The remaining parameters are returned for deleteDataFileCopy
--
--    file#
--    fname
--    reset_scn
--    create_scn
--    ckp_scn
--    blocks_size
--
-- Exceptions:
--   NO_RECOVERY_CATALOG (ORA-20300)
--     translation by key is not supported without the recovery catalog
--   DATAFILE_COPY_DOES_NOT_EXIST
--     the specified key or filename does not match any datafile copy
--   DATAFILE_COPY_NAME_AMBIGUOUS
--     the specified filename matches more than one datafile copy
--   TAG_DOES_NOT_MATCH
--     the specified tag doesn't match any datafile copies
--   TRANSLATION_IN_PROGRESS (ORA-20203)
--     the previous translation conversation is still in progess.
--     To terminate get all datafiles with getDataFileCopy.
--   TRANSLATION_NOT_IN_PROGRESS (ORA-20204)
--     getDataFileCopy was called with no translation in progress

PROCEDURE translateDataFileCopyKey(
   cdf_key 	IN number
  ,available 	IN number
  ,unavailable 	IN number);

-- Obsolete as of 8.1.6
PROCEDURE translateDataFileCopyKey(
   cdf_key 	IN number
  ,available 	IN number
  ,unavailable 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,file# 	OUT number
  ,fname 	OUT varchar2
  ,reset_scn 	OUT number
  ,create_scn 	OUT number
  ,ckp_scn 	OUT number
  ,block_size 	OUT number
  ,blocks 	OUT number);

PROCEDURE translateDataFileCopyName(
   fname 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,duplicates 	IN number);

PROCEDURE translateDataFileCopyTag(
   tag 		IN varchar2
  ,available 	IN number
  ,unavailable	IN number
  ,duplicates 	IN number);

-- Obsolete as of 8.1.6
PROCEDURE getDataFileCopy(
   recid 	OUT number
  ,stamp 	OUT number
  ,file# 	OUT number
  ,fname 	OUT varchar2
  ,reset_scn 	OUT number
  ,create_scn 	OUT number
  ,ckp_scn 	OUT number
  ,block_size 	OUT number
  ,blocks 	OUT number);

----------------------------
-- Proxy Copy Translation --
----------------------------

PROCEDURE getProxyCopy(
   rcvRec	OUT rcvRec_t
  ,closeCursor  IN  boolean DEFAULT FALSE);

PROCEDURE translateProxyCopyKey(
   pc_key 	IN number
  ,deviceType 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,expired      IN number);

-- Obsolete as of 8.1.6
PROCEDURE translateProxyCopyKey(
   pc_key 	IN number
  ,device_type 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,handle 	OUT varchar2);

PROCEDURE translateProxyCopyHandle(
   handle 	IN varchar2
  ,deviceType 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,expired      IN number);

-- Obsolete as of 8.1.6
PROCEDURE translateProxyCopyHandle(
   handle 	IN varchar2
  ,device_type 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number);

PROCEDURE translateProxyCopyTag(
   tag         	IN varchar2
  ,device_type 	IN varchar2
  ,available   	IN number
  ,unavailable 	IN number
  ,deleted     	IN number);

-- translateProxyCopyKey translates a proxy copy key to a
-- recid and stamp in V$PROXY_DATAFILE/V$PROXY_ARCHIVEDLOG
-- translateProxyCopyHandle translates handle and device type to a 
-- proxy copy recid and stamp.

-- getProxyCopy returns one proxy copy after calling translateProxyCopyTag.
-- keep calling getProxyCopy until recid is null.

-- The available and unavailable parameters are used to limit the
-- translation to backup pieces with the desired status.

-- Input parameters:
--    pc_key
--      key of the proxy copy record in the recovery catalog
--    handle
--      proxy copy handle
--    device type
--      device type on which the proxy copy resides
-- Output parameters:
--    recid
--      recid/stamp of the proxy copy record (in V$PROXY_DATAFILE or
--                                               V$PROXY_ARCHIVEDLOG)
-- Exceptions:
--   NO_RECOVERY_CATALOG (ORA-20300)
--     this operation is not supported without the recovery catalog
--   PROXY_COPY_DOES_NOT_EXIST
--     the key does not match any proxy copy
--   PROXY_COPY_HANDLE_IS_AMBIGUOUS
--     the key matches more than one proxy copy

-- Obsolete as of 8.1.6
PROCEDURE getProxyCopy(
   recid OUT number
  ,stamp OUT number
  ,handle OUT varchar2);
                       
------------------------------
-- Backup Piece Translation --
------------------------------

PROCEDURE getBackupPiece(
   bpRec	OUT bpRec_t
  ,closeCursor  IN  boolean DEFAULT FALSE);

PROCEDURE translateBackupPieceKey(
   key 		IN number
  ,available 	IN number
  ,unavailable 	IN number
  ,expired      IN number);

-- Obsolete as of 8.1.6
PROCEDURE translateBackupPieceKey(
   bp_key 	IN number
  ,available 	IN number
  ,unavailable 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,handle 	OUT varchar2
  ,set_stamp 	OUT number
  ,set_count 	OUT number
  ,piece# 	OUT number);

PROCEDURE translateBackupPieceHandle(
   handle 	IN varchar2
  ,deviceType 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,expired      IN number);

-- Obsolete as of 8.1.6
PROCEDURE translateBackupPieceHandle(
   handle 	IN varchar2
  ,device_type 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,set_stamp 	OUT number
  ,set_count 	OUT number
  ,piece# 	OUT number);

PROCEDURE translateBackupPieceTag(
   tag         	IN varchar2
  ,available   	IN number
  ,unavailable  IN number
  ,expired      IN number DEFAULT NULL);

PROCEDURE translateBackupPieceBSKey(
   key		IN number
  ,tag		IN varchar2 DEFAULT NULL
  ,deviceType	IN varchar2 DEFAULT NULL
  ,pieceCount	IN number
  ,duplicates	IN number DEFAULT TRUE#
  ,copyNumber   IN number DEFAULT NULL
  ,available	IN number DEFAULT TRUE#
  ,unavailable  IN number DEFAULT FALSE#
  ,deleted      IN number DEFAULT FALSE#
  ,expired      IN number DEFAULT FALSE#);

-- Obsolete as of 8.1.6
PROCEDURE translateBackupSetKey(
   bs_key          IN  number
  ,device_type     IN  varchar2
  ,available       IN  number
  ,unavailable     IN  number
  ,deleted         IN  number
  ,duplicates      IN  number
  ,backup_type     OUT varchar2
  ,recid           OUT number
  ,stamp           OUT number
  ,set_stamp       OUT number
  ,set_count       OUT number
  ,bslevel         OUT number
  ,completion_time OUT date);

-- Obsolete as of 8.1
PROCEDURE translateBackupSetKey(
   bs_key      IN  number
  ,device_type IN  varchar2
  ,available   IN  number
  ,unavailable IN  number
  ,deleted     IN  number
  ,duplicates  IN  number
  ,backup_type OUT varchar2
  ,recid       OUT number
  ,stamp       OUT number);

-- Obsolete as of 8.1.6
PROCEDURE translateBackupSetRecid(
   recid       IN  number
  ,stamp       IN  number
  ,device_type IN  varchar2
  ,bs_key      OUT number
  ,bslevel     OUT number
  ,completed   OUT date);

-- Obsolete as of 8.1
PROCEDURE translateBackupSetRecid(
   recid       IN  number
  ,stamp       IN  number
  ,device_type IN  varchar2);

-- translateBackupPieceBSKey translates the specified backup set into a list of
-- backup pieces.  If there are multiple available copies of a piece then
-- only the latest (with highest stamp) is returned.  If there is no available
-- copy of a piece then raise an exception.

-- Input parameters:
--    key
--      key of the backup set record in the recovery catalog
--    recid
--      recid of the backup set record (in V$BACKUP_SET)
--    stamp
--      stamp of the backup set record (in V$BACKUP_SET)
-- Output parameters:
--   device_type
--     type of the device on which the piece resides
--   handle
--     handle to the backup piece
-- Exceptions:
--   BACKUP_SET_MISSING
--     no backup set with the specified recid and stamp found
--   NO_RECOVERY_CATALOG (ORA-20300)
--     translation by bs_key is not supported without the recovery catalog

-- Obsolete as of 8.1.6
PROCEDURE getBackupPiece(
   recid      OUT number
  ,stamp      OUT number
  ,bpkey      OUT number
  ,set_stamp  OUT number
  ,set_count  OUT number
  ,piece#     OUT number
  ,copy#      OUT number
  ,status     OUT varchar2
  ,completion OUT date
  ,handle     OUT varchar2);

-- Obsolete as of 8.1
PROCEDURE getBackupPiece(
   recid      OUT number
  ,stamp      OUT number
  ,set_stamp  OUT number
  ,set_count  OUT number
  ,piece#     OUT number
  ,handle     OUT varchar2);

----------------------------
-- Backup Set Translation --
----------------------------

PROCEDURE translateBackupSetKey(
   key	      IN  number
  ,bsRec      OUT bsRec_t);

------------------------
-- Controlfile Backup --
------------------------

-- getControlfileBackup finds the optimal copy or backup of the controlfile
-- based on the given criteria.
-- The optimal copy is the one with highest checkpoint SCN.  Returns one of:
-- SUCCESS, AVAILABLE, UNAVAILABLE.

FUNCTION getControlfileBackup(
   rcvRec	OUT rcvRec_t)
RETURN number;

-- This is for 8.0.4 compatibility
FUNCTION findControlFileBackup(
   type       	OUT number
  ,recid      	OUT number
  ,stamp    	OUT number
  ,fname      	OUT varchar2
  ,device_type 	OUT varchar2
  ,ckp_scn    	OUT number)
RETURN number;

-- Obsolete as of 8.1.6
FUNCTION findControlFileBackup(
   type       	OUT number
  ,recid      	OUT number
  ,stamp    	OUT number
  ,fname      	OUT varchar2
  ,device_type 	OUT varchar2
  ,ckp_scn    	OUT number
  ,rlg_scn    	OUT number
  ,blksize     	OUT number)
RETURN number;

-------------------------
-- Archived Log Backup --
-------------------------

PROCEDURE findArchivedLogBackup(
   thread    IN number
  ,sequence  IN number
  ,lowSCN    IN number);

-- findArchivedLogBackup finds a backup set containing the given archived log.
-- getArchivedLogBackup returns the record for the backup set.  The return
-- value is one of:  SUCCESS, AVAILABLE, UNAVAILABLE.
--
-- Input Parameter:
--    thread#
--    sequence#
--    low_scn

FUNCTION getArchivedLogBackup(
   rcvRec	OUT rcvRec_t)
RETURN binary_integer;

-- Obsolete as of 8.1.6
FUNCTION findArchivedLogBackup(
   thread#    IN number
  ,sequence#  IN number
  ,low_scn    IN number
  ,type       OUT number
  ,recid      OUT number
  ,stamp      OUT number
  ,device_type OUT varchar2)
RETURN number;

---------------
-- List Copy --
---------------

PROCEDURE listTranslateControlfileCopy(
   tag             IN  varchar2
  ,completedAfter  IN  date
  ,completedBefore IN  date
  ,available	   IN number DEFAULT TRUE#	-- for backward compatibility
  ,unavailable     IN number DEFAULT TRUE#	-- for backward compatibility
  ,deleted	   IN number DEFAULT FALSE#	-- for backward compatibility
  ,expired   	   IN number DEFAULT TRUE#);	-- for backward compatibility

PROCEDURE listGetControlfileCopy(
   rcvRec OUT rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION listGetControlfileCopy(
   bcfkey     OUT number
  ,ckpscn     OUT number
  ,ckptime    OUT date
  ,status     OUT varchar2
  ,completion OUT date
  ,fname      OUT varchar2)
RETURN number;

PROCEDURE listTranslateDataFileCopy(
   file#             IN number
  ,creation_change#  IN number
  ,tag               IN varchar2 DEFAULT NULL
  ,file_name_pattern IN varchar2 DEFAULT NULL
  ,completedAfter    IN date DEFAULT NULL
  ,completedBefore   IN date DEFAULT NULL
  ,available	     IN number DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number DEFAULT TRUE#);	-- for backward compatibility

PROCEDURE listGetDataFileCopy(
   rcvRec OUT rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION listGetDataFileCopy(
   cdf_key            OUT number
  ,status             OUT varchar2
  ,fname              OUT varchar2
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date)
RETURN number;

PROCEDURE listTranslateArchivedLogCopy(
   thread#           IN number
  ,sequence#         IN number
  ,first_change#     IN number
  ,file_name_pattern IN varchar2 DEFAULT NULL
  ,completedAfter    IN date     DEFAULT NULL
  ,completedBefore   IN date     DEFAULT NULL
  ,available	     IN number   DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number   DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number   DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number   DEFAULT TRUE#);-- for backward compatibility

PROCEDURE listGetArchivedLogCopy(
   rcvRec	OUT rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION listGetArchivedLogCopy(
   al_key          OUT number
  ,status          OUT varchar2
  ,fname           OUT varchar2
  ,completion_time OUT date)
RETURN number;

-----------------
-- List Backup --
-----------------

PROCEDURE listTranslateControlfileBackup(
   tag             IN  varchar2
  ,completedAfter  IN  date
  ,completedBefore IN  date
  ,available	   IN number DEFAULT TRUE#	-- for backward compatibility
  ,unavailable     IN number DEFAULT TRUE#	-- for backward compatibility
  ,deleted	   IN number DEFAULT FALSE#	-- for backward compatibility
  ,expired   	   IN number DEFAULT TRUE#);	-- for backward compatibility

PROCEDURE listGetControlfileBackup(
   rcvRec OUT rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION listGetControlfileBackup(
   bskey      OUT number,
   ckpscn     OUT number,
   ckptime    OUT date)
RETURN number;

PROCEDURE listTranslateDataFileBackup(
   file#             IN number
  ,creation_change#  IN number
  ,tag               IN varchar2 DEFAULT NULL
  ,completedAfter    IN date     DEFAULT NULL
  ,completedBefore   IN date     DEFAULT NULL
  ,available	     IN number   DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number   DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number   DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number   DEFAULT TRUE#);-- for backward compatibility

PROCEDURE listGetDataFileBackup(
   rcvRec OUT rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION listGetDataFileBackup(
   bs_key             OUT number
  ,backup_type        OUT varchar2
  ,incremental_level  OUT number
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date)
RETURN number;

-- A stupid idea from the 8.1 LIST implementation
PROCEDURE translateBackupFile(
   bs_recid    IN  number
  ,bs_stamp    IN  number
  ,fno         IN  number
  ,bskey       OUT number
  ,inclevel    OUT number
  ,backup_type OUT varchar2
  ,completed   OUT date);

-- Used by 8.0 and 8.1.6, but not 8.1
PROCEDURE listTranslateArchivedLogBackup(
   thread#       	IN number
  ,sequence#     	IN number
  ,first_change# 	IN number
  ,completedAfter 	IN date   DEFAULT NULL
  ,completedBefore	IN date   DEFAULT NULL
  ,available		IN number DEFAULT TRUE#
  ,unavailable	  	IN number DEFAULT TRUE#
  ,deleted		IN number DEFAULT FALSE#
  ,expired   		IN number DEFAULT TRUE#);

PROCEDURE listGetArchivedLogBackup(
   rcvRec OUT rcvRec_t);

-- Obsolete as of 8.1
FUNCTION listGetArchivedLogBackup(
   bs_key          OUT number
  ,completion_time OUT date)
RETURN number;

-- Obsolete as of 8.1.6
PROCEDURE listTranslateArchivedLogBackup(
   thread#	IN number   DEFAULT NULL
  ,lowseq       IN number   DEFAULT NULL
  ,highseq      IN number   DEFAULT NULL
  ,lowscn       IN number   DEFAULT NULL
  ,highscn      IN number   DEFAULT NULL
  ,from_time    IN date     DEFAULT NULL
  ,until_time   IN date     DEFAULT NULL
  ,pattern	IN varchar2 DEFAULT NULL);

-- Obsolete as of 8.1.6
FUNCTION listGetArchivedLogBackup(
   bs_key          OUT number
  ,thread#         OUT number
  ,sequence#       OUT number
  ,first_change#   OUT number
  ,next_change#    OUT number
  ,first_time      OUT date
  ,next_time       OUT date)
RETURN number;

---------------------
-- List Proxy Copy --
---------------------

-- Note that this is used for both datafiles and the controlfile
PROCEDURE listTranslateProxyDataFile(
   file#             IN number
  ,creation_change#  IN number
  ,tag               IN varchar2 DEFAULT NULL
  ,handle_pattern    IN varchar2 DEFAULT NULL
  ,completedAfter    IN date     DEFAULT NULL
  ,completedBefore   IN date     DEFAULT NULL
  ,available	     IN number   DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number   DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number   DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number   DEFAULT TRUE#);-- for backward compatibility

PROCEDURE listGetProxyDataFile(
   rcvRec OUT rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION listGetProxyDataFile(
   xdf_key            OUT number
  ,recid              OUT number
  ,stamp              OUT number
  ,status             OUT varchar2
  ,handle             OUT varchar2
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date)
RETURN number;

-- This procedure serves absolutely no purpose.  It is here only for
-- backwards compatbility with 8.1.5.  The only call to this is from
-- krmkafs(), which gets called from krmkgra().  Since the calls are always
-- in sequence, we can simply save the last record returned from
-- getRecoveryAction and avoid doing an extra query.
-- The only value this functions returns that krmkgra() didn't already have
-- in 8.1.5 is the xdf_key.  Completion time was being estimated from the
-- stamp.
PROCEDURE listTranslateProxyDFRecid(
   recid              IN number
  ,stamp              IN number
  ,xdf_key            OUT number
  ,file#              OUT number
  ,status             OUT varchar2
  ,handle             OUT varchar2
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date);

-------------------------------
-- List Database Incarnation --
-------------------------------

PROCEDURE listTranslateDBIncarnation(
   db_name	IN varchar2 DEFAULT NULL);

FUNCTION listGetDBIncarnation(
   db_key            OUT number
  ,dbinc_key         OUT number
  ,db_name           OUT varchar2
  ,db_id             OUT number
  ,current_inc       OUT varchar2
  ,resetlogs_change# OUT number
  ,resetlogs_time    OUT date)
RETURN number;

--------------------------------------
-- List Rollback Segment Tablespace --
--------------------------------------

PROCEDURE listRollbackSegTableSpace;

FUNCTION listGetTableSpace(
   ts#               OUT number
  ,ts_name           OUT varchar2)
RETURN number;

------------------------
-- Incremental Backup --
------------------------

-- getIncrementalScn returns the starting scn for an incremental backup.
-- Input Parameters:
--   file#
--     datafile number
--   reset_scn
--     the resetlogs SCN of the datafile
--   reset_time
--     the resetlogs time of the datafile
--   incr_scn
--     level of the incremental backup
--   cumulative
--      TRUE# if the backup is cumulative

-- Exceptions
--   DATAFILE_DOES_NOT_EXIST
--   INVALID_LEVEL
--   NO_PARENT_BACKUP_FOUND

FUNCTION getIncrementalScn(
   file# 	IN number
  ,create_scn 	IN number
  ,reset_scn 	IN number
  ,reset_time 	IN date
  ,incr_level 	IN number
  ,cumulative 	IN number)
RETURN number;

--------------------
-- Offline Ranges --
--------------------

PROCEDURE findOfflineRangeCopy(
   offr_recid   IN number
  ,offr_ckpscn  IN number
  ,cf_cretime   IN date
  ,dbinc_key    IN number);

PROCEDURE getOfflineRangeCopy(
   rcvRec	OUT	rcvRec_t);

-- Obsolete as of 8.1.6
FUNCTION getOfflineRangeCopy
RETURN varchar2;

-- findOfflineRangeCopy begins the search for a controlfile copy
-- containing a specified offline range.  getOfflinRangeCopy is called
-- to retrieve the controlfile names one by one.  NULL is returned at
-- end of fetch.

-- Input Parameters:
--   offr_recid
--     recid of offline range
--   offr_ckpscn
--     online checkpoint SCN (end) of offline range
--   dbinc_rlgscn
--     resetlogs SCN of the db incarnation that contains this range
-- Output Parameters:
--   offr_recid
--     recid of the offline range record
--   offr_stamp
--     stamp of the offline range record
--   type
--     type of the controlfile that contains the offline range. 
--     COPY or BACKUP
--   recid
--     the recid of datafile copy record or
--     the recid of the backup set record
--   stamp
--     The timestamp associated with the recid in the controlfile.
--   fname
--     filename of the controlfile copy
--     NULL if a backup controlfile is returned

-- returns TRUE (1) if a copy or backup was found
-- returns FALSE (0) if no copy or backup was found 

-- Exceptions:
--   OFFLINE_RANGE_NOT_FOUND (ORA-20250)
--     No offline range was found for the datafile starting at the offline SCN 

---------------------------------------
-- Recovery Functions and Procedures --
---------------------------------------

PROCEDURE setComputeRecoveryActionMasks(
   containerMask	IN number
  ,actionMask		IN number
  ,allRecords           IN number);

-- Obsolete as of 8.1.6
PROCEDURE setRAflags(
   kindMask    IN number
  ,allRecords  IN boolean);

FUNCTION computeRecoveryActions(
fno        IN number,   -- Datafile number.
crescn     IN number,   -- Datafile creation SCN.
df_rlgscn  IN number    -- Datafile resetlogs SCN.  Null if this is a RESTORE
   default null,        -- command, else this is the value in the datafile
                        -- header for the datafile we are RECOVERing.
df_rlgtime IN date      -- Datafile resetlogs time.  Null if df_rlgscn is
   default null,        -- null, else value from datafile header.
df_ckpscn  IN number    -- Datafile checkpoint SCN.  Null if df_rlgscn is
   default null,        -- null, else value from datafile header. 
offlscn    IN number    -- kccfeofs (may be null).
   default 0,
onlscn     IN number    -- kccfeonc (null if offlscn is null).
   default 0,
onltime    IN date      -- kccfeonc_time
   default null,
cleanscn   IN number    -- kccfecps if either SOR or WCC set, else null.
   default 0,
clean2scn  IN number    -- CF ckpt SCN if WCC set, infinity if SOR bit set
   default 0,           -- else null.
clean2time IN date      -- cf ckpt time if WCC, SYSDATE if SOR
   default null,
allowfuzzy IN boolean   -- TRUE if can be fuzzy at until SCN/time, FALSE if
  default FALSE,        -- not.  default is FALSE.
partial_rcv IN boolean  -- TRUE if can do partial recovery, FALSE if not
  default FALSE,
cf_scn     IN number    -- controlfile checkpoint SCN (NULL if none mounted)
  default NULL,
cf_cretime IN date      -- controlfile creation time (NULL if none mounted)
  default NULL,
cf_offrrid IN number    -- recid of oldest offline range in controlfile
  default NULL,         -- (NULL if none mounted)
allCopies  IN boolean	-- if TRUE, then stack all valid copies of a bu set
  default FALSE
) return binary_integer;

-- Returns:
--   SUCCESS -> the file can be restored/recovered.
--   else one of RESTORABLE, AVAILABLE, UNAVAILABLE, NO_ACTION.

-- computeRecoveryActions return values --

SUCCESS     CONSTANT binary_integer := 0;
UNAVAILABLE CONSTANT binary_integer := 1;
AVAILABLE   CONSTANT binary_integer := 2;
RESTORABLE  CONSTANT binary_integer := 3;
NO_ACTION   CONSTANT binary_integer := 4;

-- SUCCESS:      A file has been found for RESTORE, or the file on disk
--               can be recovered.
-- UNAVAILABLE:  If RESTORE, then no datafilecopy or level 0 backup was found.
--               If RECOVER, then some incremental backup is missing, or the
--               datafile on disk is too old to recover.
-- AVAILABLE:    If RESTORE, then some level 0 or datafilecopy exists, but
--               the required device type is not allocated.
-- RESTORABLE:   This is returned only when doing a RECOVER.  It means that
--               the file on disk cannot be recovered, but there is some level 
--               0 or datafilecopy that could be restored and then recovered.
-- NO_ACTION:    There are no incrementals or offline ranges to apply, but
--               the file should be recoverable with redo.  No guarantee is
--               made that the logs needed are actually available.

FUNCTION getRecoveryAction(
   action OUT rcvRec_t)
RETURN binary_integer;

-- Obsolete as of 8.1.6
FUNCTION getRecoveryAction(
   kind       OUT number
  ,set_stamp  OUT number
  ,set_count  OUT number
  ,recid      OUT number
  ,stamp      OUT number
  ,fname      OUT varchar2
  ,blocksize  OUT number
  ,blocks     OUT number
  ,devtype    OUT varchar2
  ,from_scn   OUT number
  ,to_scn     OUT number
  ,to_time    OUT date
  ,rlgscn     OUT number
  ,rlgtime    OUT date
  ,cfcretime  OUT date
  ,dbinc_key  OUT number)
RETURN binary_integer;

PROCEDURE printRecoveryActions;

PROCEDURE trimRecoveryActions(
   maxActions 		IN number
  ,containerMask 	IN number
  ,actionMask		IN number);

-- trimRecoveryActions will trim the stack down to the specified number
-- actions if it contains more.  This is used by report obsolete to implement
-- the redundancy count.  The reason for it is that getRecoveryActions
-- returns actions in LIFO order.  This means the oldest actions, which
-- were stacked most recently, are returned first.  However, report obsolete
-- wants to keep only the most recent backups when constructing the 
-- "must keep" list.  We solve the problem by getting rid of any excess
-- actions first, and so the order in which getRecoveryActions returns them
-- won't matter.  Note that only actions whose type_con and type_act are
-- selected by the masks will be deleted.  Other actions are left on the
-- stack.

---------------------
-- Report Obsolete --
---------------------

PROCEDURE reportTranslateDFDel ;

-- pre 8.1.5 version
FUNCTION reportGetDFDel(
   file#               OUT number
  ,filetype            OUT number
  ,checkpoint_change#  OUT number
  ,checkpoint_time     OUT date
  ,resetlogs_change#   OUT number
  ,resetlogs_time      OUT date
  ,incremental_change# OUT number
  ,fuzzy_change#       OUT number
  ,recid               OUT number
  ,stamp               OUT number
  ,fname               OUT varchar2
  ,restorable          OUT number)
RETURN number;

-- 8.1.5+ version
FUNCTION reportGetDFDel(
   file#               OUT number
  ,filetype            OUT number
  ,checkpoint_change#  OUT number
  ,checkpoint_time     OUT date
  ,resetlogs_change#   OUT number
  ,resetlogs_time      OUT date
  ,incremental_change# OUT number
  ,fuzzy_change#       OUT number
  ,recid               OUT number
  ,stamp               OUT number
  ,fname               OUT varchar2
  ,restorable          OUT number
  ,key                 OUT number
  ,completion_time     OUT date)
RETURN number;

------------
-- TSPITR --
------------

FUNCTION getCloneName(
   fno IN number
  ,crescn IN number)
RETURN varchar2;

------------------
-- Version Info --
------------------

FUNCTION getPackageVersion
RETURN varchar2;

pragma TIMESTAMP('1997-03-12:13:51:00');

END; -- dbms_rcvman or x$dbms_rcvman

-- CUT_HERE    <- tell sed where to chop off the rest
>>>

# recovery catalog version
define rcver
<<<
CREATE TABLE rcver
(
version varchar2(12) NOT NULL,
constraint rcver_version_unique unique(version)
) &tablespace&
>>>

# This libmem must contain exactly one entry, which describes the version
# of the recovery catalog.  This table does not describe the versions of
# rman which can run against this recovery catalog.  That is done by the
# 'getpackageversion' procedure in each catalog package.
# This just describes what version of the recovery catalog tables we have.
define rcver_insert
<<<
begin
  insert into rcver values('08.01.06');
end;
>>>


define commit
<<<
commit
>>>

define prvtrvct_plb
<<<
--
-- $Header: prvtrvct.sql@@/main/st_rdbms_dbeusee_bug-728666/3 Checked in on 05/18/99 17:28:53 by dbeusee $
--

CREATE OR replace PACKAGE BODY dbms_rcvcat IS

-- prvtrvct.sql
--
--  Copyright (c) Oracle Corporation 1995, 1996, 1997, 1998, 1999. ALL Rights Reserved.
--
--    NAME
--      prvtrvct.sql
--
--    DESCRIPTION
--
--    NOTES
--
--  MODIFIED   (MM/DD/YY)
--  swerthei    09/14/99 - add dbms_rcvcat.reNormalize
--  gpongrac    08/16/99 - add 8.1.6 to versionlist
--  gpongrac    08/04/99 - really delete records now
--  gpongrac    07/14/99 - add stop_time to df table
--  gpongrac    07/06/99 - change rcver to 8.1.6
--  gpongrac    12/18/98 - clean up checkBackupPiece
--  dbeusee     09/13/98 - bug-728666
--  rlu         05/03/99 - 621515_restore_main
--  gpongrac    04/08/99 - change REM to --
--  rlu         11/25/98 - bug_621515
--  swerthei    10/22/98 - change proxy messages
--  fsanchez    10/07/98 - bug-607271
--  swerthei    06/19/98 - prepare for wrapping to recover.bsq
--  swerthei    06/17/98 - make compatible with 8.0
--  swerthei    06/01/98 - add media_pool
--  swerthei    06/01/98 - add changeProxyCopy
--  swerthei    05/18/98 - resync proxy copy records
--  dbeusee     04/20/98 - rpt_redundancy_enh
--  gpongrac    05/06/98 - add bsStatusRecalc
--  dbeusee     04/06/98 - xcheck enh
--  fsanchez    03/29/98 - Duplexed backup sets
--  gpongrac    01/27/98 - change getCatalogVersion
--  gpongrac    01/16/98 - bug 612344:  deal with null fname in checkDatafile
--  fsanchez    01/04/98 - Allow setDatabase to receive dbid without dbname
--  gpongrac    09/02/97 - add setDatafile Size
--  tpystyne    09/12/97 - bug 480172, fix name translation
--  gpongrac    08/12/97 - deal with clone_name becoming the real filename
--  gpongrac    07/01/97 - fix typo
--  gpongrac    06/30/97 - keep offline clean and read-only scn in df table
--  gpongrac    06/30/97 - record current offline range in kccor
--  gpongrac    06/26/97 - uppercase all keywords and reformat
--  gpongrac    04/08/97 - deal with 0 creation scn in checkofflinerange
--  gpongrac    04/03/97 - consider offline ranges in ckptneeded
--  gpongrac    03/31/97 - change to use version_time instead of cf_create_t
--  gpongrac    03/31/97 - add cf_create_time to offr
--  tpystyne    03/20/97 - update catalog version to 8.00.03
--  tpystyne    02/27/97 - add ckptneeded
--  gpongrac    02/20/97 - add compltion_time to bdf
--  swerthei    01/14/97 - add setclonename
--  ### comments from 1996 removed

type version_list_type is table of varchar2(8) index by binary_integer;
version_list version_list_type;
version_max_index binary_integer;
version_counter binary_integer := 1;

/*-----------*
 * Constants *
 *-----------*/

MAXNUMVAL CONSTANT      NUMBER := 2**32-1;
catalog_version CONSTANT VARCHAR2(12) := '08.01.06';

-- The "compatible" variable contains a user-specified version number.
-- We must maintain behaviour that is compatible with this version of the
-- package.  Note that is different from the package version because the
-- package version describes only the version of the interface, but not how
-- the package itself behaves.  The compatible version number will always
-- be in the range of versions supported by this package.
compatible              NUMBER;

/*-------------------------*
 * Package State Variables *
 *-------------------------*/

-- Package State Variables:
--   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

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_rlh_recid  NUMBER;
last_al_recid   NUMBER;
last_offr_recid NUMBER;
last_bs_recid   NUMBER;
last_bp_recid   NUMBER;
last_bdf_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;

reNorm_state binary_integer;
RENORM_DFATT CONSTANT binary_integer := 1;
RENORM_ORL   CONSTANT binary_integer := 2;
RENORM_AL    CONSTANT binary_integer := 3;
RENORM_BP    CONSTANT binary_integer := 4;
RENORM_CCF   CONSTANT binary_integer := 5;
RENORM_CDF   CONSTANT binary_integer := 6;

/*---------*
 * Cursors *
 *---------*/

-- Package Cursors:
--   tsQ
--     Used to resync the list of tablespaces.
--   dfQ
--     Used to resync the list of datafiles.
--   rtQ
--     Used to resync the list of threads.
--   orlQ
--     Used to resync the list of online redo logs.
--   scrlQ
--     Used to fetch lines from a stored script.

-- select all current tablespaces in this database incarnation
cursor tsQ IS
  SELECT ts.ts_name, ts.ts#, ts.create_scn, ts.create_time, tsatt.rbs_count
  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 orl.fname;


-- select all available pieces that match the device_type and handle
cursor bpq(device_type VARCHAR2, handle VARCHAR2,
           bp_recid VARCHAR2, bp_stamp VARCHAR2) IS
  SELECT bp_key
  FROM bp
  WHERE db_key = this_db_key
  AND   device_type = bpq.device_type
  AND   handle = bpq.handle
  AND   handle_hashkey = substr(bpq.device_type,1,10) ||
                         substr(bpq.handle,1,10) ||
                         substr(bpq.handle,-10)
  AND   status = 'A' -- ### why not all pieces w/ same name???
  AND   bp_stamp <= bpq.bp_stamp
  AND   NOT (bp_recid = bpq.bp_recid AND
             bp_stamp = bpq.bp_stamp);


-- select all lines from a stored script
cursor scrlQ(key NUMBER) IS
  SELECT text
  FROM scrl
  WHERE scr_key = key
  ORDER BY linenum;

-- Get all the recovery catalog versions
cursor rcverQ IS
  SELECT version
  FROM rcver
  ORDER BY version;

cursor reNorm_dfatt_c IS
  SELECT fname
  FROM dfatt
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;

cursor reNorm_orl_c IS
  SELECT fname
  FROM orl
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;

cursor reNorm_al_c IS
  SELECT fname
  FROM al
  where dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;

cursor reNorm_bp_c IS
  SELECT handle
  FROM bp
  WHERE device_type = 'DISK' and db_key = this_db_key
  FOR UPDATE;

cursor reNorm_ccf_c IS
  SELECT fname
  FROM ccf
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;

cursor reNorm_cdf_c IS
  SELECT fname
  FROM cdf
  WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key)
  FOR UPDATE;

/*----------------------*
 * Cursor Row Variables *
 *----------------------*/

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

tsRec           tsQ%rowtype;
dfRec           dfQ%rowtype;
rtRec           rtQ%rowtype;
orlRec          orlQ%rowtype;

/*---------------*
 * Private Types *
 *---------------*/

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

-- 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;

FUNCTION deleteAL(
   recid IN number
  ,stamp IN number) RETURN boolean
IS
BEGIN
   IF (compatible >= 080106) THEN
      -- We can really delete the row.
      DELETE FROM al
       WHERE al.dbinc_key IN (SELECT dbinc_key
                                FROM dbinc
                               WHERE dbinc.db_key = this_db_key)
         AND al_recid = recid
         AND al_stamp = stamp;
      IF (SQL%ROWCOUNT = 0) THEN
         RETURN FALSE;
      ELSE
         RETURN TRUE;
      END IF;
   ELSE
      -- We cannot delete the row, so just mark it 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_recid = recid
         AND al_stamp = stamp;
      IF (SQL%ROWCOUNT = 0) THEN
         RETURN FALSE;
      ELSE
         RETURN TRUE;
      END IF;
   END IF;
END deleteAL;

/*-------------------*
 * Register Database *
 *-------------------*/

PROCEDURE registerDatabase(db_id      IN NUMBER
                          ,db_name    IN VARCHAR2
                          ,reset_scn  IN NUMBER
                          ,reset_time IN DATE
                          ) IS

local dbinc%rowtype;                    -- local variables

BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible recovery catalog');
  END;

  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030 , 'Resync in progress');
  END IF;

  this_db_key := NULL;
  this_dbinc_key := NULL;

  BEGIN
    INSERT INTO db(db_key, db_id) VALUES(rman_seq.nextval, db_id);
  EXCEPTION
    WHEN dup_val_on_index THEN
      raise_application_error(-20002, 'Database already registered');
  END;
  SELECT rman_seq.currval INTO local.db_key FROM dual;

  INSERT INTO dbinc
    (dbinc_key, db_key, db_name, reset_scn, reset_time)
  VALUES
    (rman_seq.nextval, local.db_key, db_name, reset_scn,reset_time);

  SELECT rman_seq.currval INTO local.dbinc_key FROM dual;

  UPDATE db SET curr_dbinc_key = local.dbinc_key
  WHERE db.db_key = local.db_key;

  commit;

-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;

END registerDatabase;

-- register a new database incarnation and make it the current incarnation
-- after opening the database with resetlogs option

PROCEDURE resetDatabase(db_id      IN NUMBER
                       ,db_name    IN VARCHAR2
                       ,reset_scn  IN NUMBER
                       ,reset_time IN DATE
                       ,parent_reset_scn  IN NUMBER
                       ,parent_reset_time IN DATE
                       ) IS

local dbinc%rowtype;                    -- local variables

BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible with recovery catalog');
  END;

  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (db_id IS NULL) THEN
    raise_application_error(-20007, 'db_id is null');
  END IF;

  this_db_key := NULL;
  this_dbinc_key := NULL;

  BEGIN
    SELECT db_key, curr_dbinc_key INTO local.db_key, local.dbinc_key
    FROM db
    WHERE db.db_id = resetDatabase.db_id;               -- should return 1 row
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20001, 'Database not found');
  END;

  -- find the parent of the new incarnation
  BEGIN
    SELECT dbinc_key INTO local.parent_dbinc_key
    FROM dbinc
    WHERE dbinc.db_key = local.db_key
    AND   dbinc.reset_scn = resetDatabase.parent_reset_scn
    AND   dbinc.reset_time = resetDatabase.parent_reset_time;
  EXCEPTION
    WHEN no_data_found THEN
      local.parent_dbinc_key := NULL;
  END;

  -- insert the new incarnation
  BEGIN
    INSERT INTO dbinc
      (dbinc_key, db_key, db_name, reset_scn, reset_time, parent_dbinc_key)
    VALUES
      (rman_seq.nextval, local.db_key, db_name, reset_scn,
       reset_time, local.parent_dbinc_key);
  EXCEPTION
    WHEN dup_val_on_index THEN
      raise_application_error(-20009, 'Db incarnation already registered');
  END;

  SELECT rman_seq.currval INTO local.dbinc_key FROM dual;

  -- and make it the current incarnation of the database
  UPDATE db SET curr_dbinc_key = local.dbinc_key
  WHERE db.db_key = local.db_key;

  commit;

-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;

END resetDatabase;

-- make an existing database incarnation the current incarnation

PROCEDURE resetDatabase(
  dbinc_key  IN NUMBER
 ,db_name    IN VARCHAR2
) IS

local dbinc%rowtype;                    -- local variables

BEGIN
  -- verify that this package is compatible with the recovery catalog
  BEGIN
    SELECT NULL INTO local.db_key FROM rcver WHERE version = catalog_version;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20298, 'Not compatible with recovery catalog');
  END;

  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;
  IF (dbinc_key IS NULL) THEN
    raise_application_error(-20008, 'Database incarnation key is missing');
  END IF;

  this_db_key := NULL;
  this_dbinc_key := NULL;

  BEGIN
    SELECT db_key, db_name
    INTO local.db_key, local.db_name
    FROM dbinc
    WHERE dbinc.dbinc_key = resetDatabase.dbinc_key;
  EXCEPTION
    WHEN no_data_found THEN
      raise_application_error(-20010, 'Database incarnation not found');
  END;

  IF (db_name <> local.db_name OR db_name IS NULL) THEN
    raise_application_error(-20004, 'Database name does not match');
  END IF;

  -- and make it the current incarnation of the database
  UPDATE db SET curr_dbinc_key = resetDatabase.dbinc_key
  WHERE db.db_key = local.db_key;

  commit;

-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;

END resetDatabase;

-- 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 findIncarnation(reset_scn  IN NUMBER, reset_time IN DATE)
                        RETURN NUMBER IS

dbinc_key NUMBER;

BEGIN
  BEGIN
    SELECT dbinc_key INTO dbinc_key
    FROM dbinc
    WHERE dbinc.db_key = this_db_key
    AND   dbinc.reset_scn = findIncarnation.reset_scn
    AND   dbinc.reset_time = findIncarnation.reset_time;
  EXCEPTION
    WHEN no_data_found THEN
      dbinc_key := NULL;
  END;

  IF dbinc_key IS NOT NULL THEN
     RETURN 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, 'UNKNOWN', reset_scn, reset_time, NULL);
  EXCEPTION
    WHEN dup_val_on_index THEN
      raise_application_error(-20009, 'Db incarnation already registered');
  END;

  SELECT rman_seq.currval INTO dbinc_key FROM dual;
  RETURN dbinc_key;

END findIncarnation;

PROCEDURE unRegisterDatabase(
  db_key     IN NUMBER
 ,db_id      IN NUMBER
) IS

tmp NUMBER;

BEGIN
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;

  -- check if the database exists in rcvcat
  BEGIN
    SELECT 0 INTO tmp FROM db
    WHERE db.db_key = unRegisterDatabase.db_key
    AND   db.db_id =  unRegisterDatabase.db_id;
  EXCEPTION
  WHEN no_data_found THEN
    raise_application_error(-20001, 'Database not found');
  END;

  DELETE FROM db WHERE db.db_key = unRegisterDatabase.db_key;
  commit;

-- rollback on error
EXCEPTION
  WHEN OTHERS THEN
    rollback;
    RAISE;

END unRegisterDatabase;

/*--------------*
 * Set Database *
 *--------------*/

-- This procedure tells the package what target database we are working with.

PROCEDURE setDatabase(db_name    IN VARCHAR2
                     ,reset_scn  IN NUMBER
                     ,reset_time IN DATE
                     ,db_id      IN NUMBER) IS

local dbinc%rowtype;                    -- local variables
current_inc VARCHAR2(3);

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')
         INTO   current_inc
         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;
   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;

END setDatabase;

/*-----------------------------*
 * Recovery Catalog Checkpoint *
 *-----------------------------*/

FUNCTION ckptNeeded(
  ckp_scn         IN NUMBER
 ,ckp_cf_seq      IN NUMBER
 ,cf_version      IN DATE
 ,cf_type         IN NUMBER
 ,high_df_recid   IN NUMBER
 ,high_orl_recid  IN NUMBER
 ,high_cdf_recid  IN NUMBER
 ,high_al_recid   IN NUMBER
 ,high_bp_recid   IN NUMBER
 ,high_do_recid   IN NUMBER
 ,high_offr_recid IN NUMBER
 ,high_pc_recid   IN NUMBER DEFAULT NULL
) RETURN NUMBER IS

ckp_type NUMBER;
local   dbinc%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;

  -- 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_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
  INTO local.cf_create_time, local.high_df_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
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key FOR UPDATE;

  ckp_type := RESYNC_NONE;

  IF (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
      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;
      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;
    END IF;
    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;
    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;
    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;
    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;
    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;
    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
      ckp_type := RESYNC_FULL;
    ELSE
      ckp_type := RESYNC_PARTIAL;
    END IF;
  END IF;

  -- if resync is not needed, release the lock on dbinc.
  IF (ckp_type = RESYNC_NONE) THEN
    rollback;
  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
     ckp_type := RESYNC_NONE;
  END IF;
  RETURN ckp_type;
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
) 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
  BEGIN
    SELECT ckp_key
    INTO local.ckp_key
    FROM ckp
    WHERE ckp.dbinc_key = this_dbinc_key
      AND ckp.ckp_key = (SELECT max(ckp_key)
                         FROM ckp ckp2
                         WHERE ckp2.dbinc_key = this_dbinc_key);

  EXCEPTION
    WHEN no_data_found THEN
      local.ckp_key := NULL;
  END;

  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;
  ELSE
    -- 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
        raise_application_error(-20032, 'Invalid checkpoint SCN');
      ELSIF (ckp_scn = local.ckp_scn AND ckp_cf_seq < local.ckp_cf_seq) THEN
        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
        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 or
      -- this is RESYNC CATALOG FROM BACKUP CONTROLFILE or
      -- the user has mounted a backup controlfile.
      -- 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
      WHERE dbinc.dbinc_key = this_dbinc_key;
    END IF;
  END IF;

  -- record resyncs
  INSERT INTO ckp
    (ckp_key, ckp_scn, ckp_cf_seq, cf_create_time, ckp_time,
     dbinc_key, ckp_type, ckp_db_status, resync_time, prev_ckp_key,
     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, local.ckp_key,
     high_df_recid);

  -- set package state variable
  SELECT rman_seq.currval INTO this_ckp_key FROM dual;

  this_ckp_scn := ckp_scn;
  this_ckp_time := ckp_time;

-- 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;
END endCkpt;


PROCEDURE cancelCkpt IS

BEGIN
  IF (this_ckp_key IS NOT NULL) THEN
    -- rollback and reset state variables
    rollback;
    this_ckp_key := NULL;
    this_ckp_scn := NULL;
    this_ckp_time := NULL;
  END IF;
END cancelCkpt;

/*-------------------*
 * Tablespace Resync *
 *-------------------*/

PROCEDURE fetchTs IS                    -- this is private to the pkg body

BEGIN
  FETCH tsQ INTO tsRec;                 -- get next row
  IF tsQ%NOTFOUND THEN
    tsRec.ts# := MAXNUMVAL;             -- indicate end of fetch
    CLOSE tsQ;
  END IF;
END fetchTs;

PROCEDURE addTs(
  ts_name      IN VARCHAR2
 ,ts#          IN NUMBER
 ,create_scn   IN NUMBER
 ,create_time  IN DATE
 ,rbs_count    IN NUMBER
) IS

BEGIN
  INSERT INTO ts
    (dbinc_key, ts#, ts_name, create_scn, create_time)
  VALUES
    (this_dbinc_key, ts#, ts_name, create_scn, create_time);

  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)
RETURN BOOLEAN IS

BEGIN
  checkResync;

  -- 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) THEN
    RETURN FALSE;
  ELSIF (high_ts_recid > last_ts_recid OR last_ts_recid IS NULL OR
         high_ts_recid IS NULL) 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
) IS

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);
  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 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);
    ELSE -- (create_scn < tsRec.create_scn)
      -- The client is passing us a tablespace with a lower creation SCN
      -- than the one we currently have listed in the rcvcat for this ts#.
      -- This is a big NO NO.  Probably the target database has an old
      -- controlfile mounted.  Signal an error.
      raise_application_error(-20042, 'Invalid tablespace creation change#');
    END IF;

    fetchTS;                            -- get next row from TS cursor

  END IF; -- (ts# < tsRec.ts)

END checkTableSpace;


PROCEDURE endTableSpaceResync IS

BEGIN
  checkResync;

  WHILE (tsRec.ts# < MAXNUMVAL) LOOP    -- while extra tablespaces in rcvcat
    dropTs(tsRec.ts#, tsRec.create_scn, this_ckp_scn, this_ckp_time);
    fetchTs;
  END LOOP;

  -- set the state variable to indicate that tablespace resync is done
  tsRec.ts# := NULL;

  -- update high_ts_resync for the next resync
  UPDATE dbinc SET high_ts_recid = nvl(last_ts_recid, high_ts_recid)
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_ts_recid := NULL;

END endTableSpaceResync;


/*-----------------*
 * Datafile Resync *
 *-----------------*/

PROCEDURE fetchDF IS                    -- private to package body
BEGIN
  FETCH dfQ INTO dfRec;
  IF dfQ%NOTFOUND THEN
    dfRec.file# := MAXNUMVAL;           -- indicate end-of-fetch
    CLOSE dfQ;
  END IF;
END fetchDF;

PROCEDURE addDf(file#       IN NUMBER,  -- private to package body
                fname       IN VARCHAR2,
                create_time IN DATE,
                create_scn  IN NUMBER,
                blocks      IN NUMBER,
                block_size  IN NUMBER,
                ts#         IN NUMBER,
                stop_scn    IN NUMBER,
                stop_time   IN DATE,
                read_only   IN number) 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)
  VALUES(this_dbinc_key, file#, create_scn, create_time, ts#, ts_create_scn,
         block_size, stop_scn, stop_time, read_only);

  INSERT INTO dfatt(dbinc_key, file#, create_scn, start_ckp_key, fname, blocks)
  VALUES(this_dbinc_key, file#, create_scn, this_ckp_key, fname, blocks);
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)
                        -- 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);
   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;

      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);

      ELSE -- (create_scn < dfRec.create_scn)
         -- The client passed us a create SCN for this datafile that is
         -- less than the SCN in the rcvcat.  I.e., the target database's
         -- controlfile now contains a previous incarnation of this datafile.
         -- This can happen only if the user has some old controlfile, or
         -- has done a resetlogs and not told us about it.
         raise_application_error(-20052, 'Invalid datafile create SCN');
      END IF;

      fetchDF;                          -- get next row from DF cursor

   END IF; -- (file# < dfRec.file#)
END checkDataFile;


PROCEDURE endDataFileResync IS

BEGIN
  checkResync;

  -- check if there are any datafiles in rcvcat that the client didn't check
  WHILE (dfRec.file# < MAXNUMVAL) LOOP
    -- if we ever allow drop datafile, replace error signalling with dropDf
    dropDf(dfRec.file#, dfRec.create_scn, this_ckp_scn, this_ckp_time);
    fetchDf;
  END LOOP;

  -- set the state variable to indicate that datafile resync is done
  dfRec.file# := NULL;

  -- update high_df_resync for the next resync
  UPDATE dbinc SET high_df_recid = last_df_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_df_recid := NULL;

END endDataFileResync;

/*---------------------*
 * Redo Thread resync  *
 *---------------------*/

PROCEDURE fetchRt IS

BEGIN
  FETCH rtQ INTO rtRec;
  IF rtQ%NOTFOUND THEN
    rtRec.thread# := MAXNUMVAL;
    CLOSE rtQ;
  END IF;
END fetchRt;

PROCEDURE addRt(
  thread#        IN NUMBER
 ,last_sequence# IN NUMBER
 ,enable_scn     IN NUMBER
 ,enable_time    IN DATE
 ,disable_scn    IN NUMBER
 ,disable_time   IN DATE
 ,status         IN VARCHAR2
) IS

BEGIN
  INSERT INTO rt
    (dbinc_key, thread#, sequence#,
     enable_scn, enable_time, disable_scn, disable_time, status)
  VALUES
    (this_dbinc_key, thread#, last_sequence#,
     enable_scn, enable_time, disable_scn, disable_time, status);
END addRt;

PROCEDURE dropRt(thread# IN NUMBER) IS
BEGIN
--  update rt set drop_ckp_key = this_ckp_key
  DELETE FROM rt
  WHERE rt.dbinc_key = this_dbinc_key
  AND   rt.thread#   = dropRt.thread#;
END dropRt;

FUNCTION beginThreadResync(
  high_rt_recid IN NUMBER
) RETURN BOOLEAN IS

BEGIN
  checkResync;

  SELECT high_rt_recid INTO last_rt_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  IF (high_rt_recid = last_rt_recid) THEN
    RETURN FALSE;
  ELSIF (high_rt_recid > last_rt_recid OR last_rt_recid IS NULL) THEN
    last_rt_recid := high_rt_recid;

    OPEN rtQ;
    fetchRt;                            -- do priming read
    last_thread# := -1;
    RETURN TRUE;
  ELSE
    raise_application_error(-20035, 'Invalid high recid');
  END IF;

END beginThreadResync;

PROCEDURE checkThread(
  thread#        IN NUMBER
 ,last_sequence# IN NUMBER
 ,enable_scn     IN NUMBER
 ,enable_time    IN DATE
 ,disable_scn    IN NUMBER
 ,disable_time   IN DATE
 ,status         IN VARCHAR2
) IS

BEGIN
  IF (rtRec.thread# IS NULL) THEN
    raise_application_error(-20061, 'Thread resync not started');
  END IF;
  IF (last_thread# >= thread#) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  last_thread# := thread#;

  WHILE (thread# > rtRec.thread#) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropRt(rtRec.thread#);
    fetchRt;
  END LOOP;
  IF (thread# < rtRec.thread#) THEN
    -- this thread is new and must be inserted into rcvcat
    addRt(thread#, last_sequence#, enable_scn, enable_time,
          disable_scn, disable_time, status);
  ELSE -- (thread# = rtRec.thread#)
    -- this is an existing thread, just update the information
    UPDATE rt SET
      sequence# = checkThread.last_sequence#,
      enable_scn = checkThread.enable_scn,
      enable_time = checkThread.enable_time,
      disable_scn = checkThread.disable_scn,
      disable_time = checkThread.disable_time,
      status = checkThread.status
    WHERE rt.dbinc_key = this_dbinc_key
    AND   rt.thread# = checkThread.thread#;

    fetchRt;
  END IF;

END checkThread;

PROCEDURE endThreadResync IS
BEGIN
  WHILE (rtRec.thread# < MAXNUMVAL) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropRt(rtRec.thread#);
    fetchRt;
  END LOOP;

  rtRec.thread# := NULL;

  -- update high_rt_resync for the next resync
  UPDATE dbinc SET high_rt_recid = last_rt_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_rt_recid := NULL;

END endThreadResync;

/*------------------------*
 * Online Redo Log resync *
 *------------------------*/

PROCEDURE fetchOrl IS

BEGIN
  FETCH orlQ INTO orlRec;
  IF orlQ%NOTFOUND THEN
    orlRec.fname := chr(255);      -- assume chr(255) is greater than any name
    CLOSE orlQ;
  END IF;
END fetchOrl;

PROCEDURE addOrl(
  thread#        IN NUMBER
 ,group#         IN NUMBER
 ,fname          IN VARCHAR2
) IS

BEGIN
  INSERT INTO orl
    (dbinc_key, thread#, group#, fname)
  VALUES
    (this_dbinc_key, thread#, group#, fname);
END addOrl;

PROCEDURE dropOrl(fname IN VARCHAR2) IS
BEGIN
--  update orl set drop_ckp_key = this_ckp_key
  DELETE FROM orl
  WHERE orl.dbinc_key = this_dbinc_key
  AND   orl.fname     = dropOrl.fname;
END dropOrl;

FUNCTION beginOnlineRedoLogResync(
  high_orl_recid IN NUMBER
) RETURN BOOLEAN IS

BEGIN
  checkResync;

  SELECT high_orl_recid INTO last_orl_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  IF (high_orl_recid = last_orl_recid) THEN
    RETURN FALSE;
  ELSIF (high_orl_recid > last_orl_recid OR last_orl_recid IS NULL) THEN
    last_orl_recid := high_orl_recid;

    OPEN orlQ;
    fetchOrl;
    last_fname := chr(1);           -- assume chr(1) is less than any name
    RETURN TRUE;
  ELSE
    raise_application_error(-20035, 'Invalid high recid');
  END IF;

END beginOnlineRedoLogResync;

PROCEDURE checkOnlineRedoLog(
  thread#        IN NUMBER
 ,group#         IN NUMBER
 ,fname          IN VARCHAR2
) IS

BEGIN
  IF (orlRec.fname IS NULL) THEN
    raise_application_error(-20061, 'Thread resync not started');
  END IF;
  IF (last_fname >= fname) THEN
    raise_application_error(-20036, 'Invalid record order');
  END IF;
  last_fname := fname;

  WHILE (fname > orlRec.fname) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropOrl(orlRec.fname);
    fetchOrl;
  END LOOP;
  IF (fname < orlRec.fname) THEN
    -- this thread is new and must be inserted into rcvcat
    addOrl(thread#, group#, fname);
  ELSE -- (fname = orlRec.fname)
    -- this is an existing thread, just update the information
    UPDATE orl SET
      thread# = checkOnlineRedoLog.thread#,
      group# = checkOnlineRedoLog.group#
    WHERE orl.dbinc_key = this_dbinc_key
    AND   orl.fname = checkOnlineRedoLog.fname;

    fetchOrl;
  END IF;

END checkOnlineRedoLog;

PROCEDURE endOnlineRedoLogResync IS
BEGIN
  WHILE (orlRec.fname != chr(255)) LOOP
    -- if we get here the thread has disappered from the controlfile
    -- this can happen only if the controlfile is recreated
    -- mark the thread as dropped in the rcvcat
    dropOrl(orlRec.fname);
    fetchOrl;
  END LOOP;

  orlRec.fname := NULL;

  -- update high_orl_resync for the next resync
  UPDATE dbinc SET high_orl_recid = last_orl_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_orl_recid := NULL;

END endOnlineRedoLogResync;

/*-------------------------*
 * Redo Log History resync *
 *-------------------------*/

FUNCTION beginLogHistoryResync RETURN NUMBER IS

BEGIN
  checkResync;

  -- 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;

  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
  -- 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;

  last_rlh_recid := NULL;

END endLogHistoryResync;

/*-------------------------*
 * Archived Log resync     *
 *-------------------------*/

FUNCTION beginArchivedLogResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_al_recid INTO last_al_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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

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 := findIncarnation(reset_scn, reset_time);

  BEGIN
    IF (compatible >= 080106 AND 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)
       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);
    END IF;

    -- mark any previous archived logs with the same name as deleted
    -- Note that only logs that belong to current database and that have
    -- archived or copied before this log are marked deleted
    UPDATE /*+ index(al al_i_fname_status)*/ al SET status = 'D'
    WHERE al.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
    AND   al.fname = checkArchivedLog.fname
    AND   al.fname_hashkey = substr(checkArchivedLog.fname,1,10)||
                          substr(checkArchivedLog.fname,-10)
    AND   al.status in ('A', 'U')
    AND   al.al_stamp <= checkArchivedLog.al_stamp
    AND   NOT (al.al_recid = checkArchivedLog.al_recid AND
               al.al_stamp = checkArchivedLog.al_stamp);

    -- 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;
      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
  UPDATE dbinc SET high_al_recid = last_al_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_al_recid := NULL;

END endArchivedLogResync;

/*-------------------------*
 * Offline range resync    *
 *-------------------------*/

FUNCTION beginOfflineRangeResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_offr_recid INTO last_offr_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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;

  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.
  ELSE
     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;
  END IF;

  BEGIN
    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
      -- This offline range record already exists in rcvcat.  Get the existing
      -- record and validate it.
      BEGIN
        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
          -- 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't know how this could happen, 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.
          BEGIN
            SELECT offr.file#, offr.offline_scn, offr.online_scn,
                   create_scn
            INTO   local.file#, local.offline_scn, local.online_scn,
                   local.create_scn
            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
               NULL;
            ELSE
               UPDATE offr SET
                 offr_recid = checkOfflineRange.offr_recid,
                 offr_stamp = checkOfflineRange.stamp
                 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
                 -- 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;

      -- check that file# and offline_scn match
      IF (file# <> local.file#) THEN
        raise_application_error(-20085, 'Invalid file#');
      END IF;
      IF (offline_scn <> local.offline_scn) THEN
        raise_application_error(-20086, 'Invalid offline SCN');
      END IF;
      IF (online_scn <> local.online_scn) THEN
        raise_application_error(-20087, 'Invalid online SCN');
      END IF;
  END;
END;

PROCEDURE endOfflineRangeResync IS
BEGIN
  UPDATE dbinc SET high_offr_recid = last_offr_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_offr_recid := NULL;

END endOfflineRangeResync;

/*-------------------------*
 * Backup Set resync       *
 *-------------------------*/

-- updateBackupSetRec calculates and updates the status of a backup set based
-- on its backup pieces.  The backup set is 'A' (available) if there is an
-- available copy of all of its pieces on one device type.  The backup set is
-- 'D' (deleted) if all bp's are deleted.  Otherwise it is marked 'O' (other).

PROCEDURE updateBackupSetRec(bs_key IN NUMBER) IS
  total_pieces     NUMBER;
  available_pieces NUMBER;
  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 (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 = 'O' OR new_status = 'A') THEN
     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;

  SELECT high_bs_recid INTO last_bs_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
) IS

local   bs%rowtype;

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
     -- 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)
    VALUES
      (rman_seq.nextval, this_db_key, bs_recid, bs_stamp, set_stamp, set_count,
       bck_type, incr_level, pieces, start_time, completion_time, 'D');

  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
  -- update high_bs_recid for the next resync
  UPDATE dbinc SET high_bs_recid = last_bs_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_bs_recid := NULL;

END endBackupSetResync;

/*-------------------------*
 * Backup piece resync     *
 *-------------------------*/

FUNCTION beginBackupPieceResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_bp_recid INTO last_bp_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
      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)
      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');
  END;

  IF localbs.bs_recid is null AND
     checkBackupPiece.piece# > localbs.pieces 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
    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;

    -- 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
  UPDATE dbinc SET high_bp_recid = last_bp_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_bp_recid := NULL;

END endBackupPieceResync;

/*-------------------------*
 * Backup Datafile resync  *
 *-------------------------*/

PROCEDURE addBackupControlFile(
  bs_key         IN NUMBER
 ,bcf_recid      IN NUMBER
 ,bcf_stamp      IN NUMBER
 ,dbinc_key      IN NUMBER
 ,ckp_scn        IN NUMBER
 ,ckp_time       IN DATE
 ,create_time    IN DATE
 ,min_offr_recid IN NUMBER
 ,block_size     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)
    VALUES (rman_seq.nextval, bs_key, dbinc_key, bcf_recid, bcf_stamp,
                    ckp_scn, ckp_time, create_time, min_offr_recid,block_size);

  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;

  SELECT high_bdf_recid INTO last_bdf_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
) 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
      select rman_seq.nextval into 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)
      VALUES
        (bs_key, this_db_key, 0, 0,
        checkBackupDataFile.set_stamp, checkBackupDataFile.set_count,
        NULL, NULL, 0,
        checkBackupDataFile.create_time, checkBackupDataFile.completion_time,
        'D');
  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;
  END;

  -- find the database incarnation key
  dbinc_key := findIncarnation(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, block_size);
  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
  UPDATE dbinc SET high_bdf_recid = last_bdf_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_bdf_recid := NULL;

END endBackupDataFileResync;

/*-------------------------*
 * Backup Redo Log resync  *
 *-------------------------*/

FUNCTION beginBackupRedoLogResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_brl_recid INTO last_brl_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
      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)
      VALUES
        (local.bs_key, this_db_key, 0, 0,
        checkBackupRedoLog.set_stamp, checkBackupRedoLog.set_count,
        NULL, NULL, 0, NULL, NULL, 'D');
  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 := findIncarnation(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
  UPDATE dbinc SET high_brl_recid = last_brl_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_brl_recid := NULL;

END endBackupRedoLogResync;

/*----------------------------*
 * Datafile Copy resync       *
 *----------------------------*/

PROCEDURE addControlFileCopy(
  ccf_recid       IN NUMBER
 ,ccf_stamp       IN NUMBER
 ,fname           IN VARCHAR2
 ,tag             IN VARCHAR2
 ,dbinc_key       IN NUMBER
 ,ckp_scn         IN NUMBER
 ,ckp_time        IN DATE
 ,create_time     IN DATE
 ,min_offr_recid  IN NUMBER
 ,block_size      IN NUMBER
 ,completion_time IN DATE
 ,status          IN VARCHAR2
) 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)
       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);
    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 in ('A','U')
    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
) 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)
       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);
    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 in ('A','U')
    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;

  SELECT high_cdf_recid INTO last_cdf_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
) 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 := findIncarnation(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);
  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);
  END IF;
END checkDataFileCopy;

PROCEDURE endDataFileCopyResync IS
BEGIN
  UPDATE dbinc SET high_cdf_recid = last_cdf_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_cdf_recid := NULL;

END endDataFileCopyResync;

/*----------------------------*
 * Proxy Datafile resync      *
 *----------------------------*/

PROCEDURE addProxyControlFile(
  dbinc_key       IN NUMBER
 ,xcf_recid       IN NUMBER
 ,xcf_stamp       IN NUMBER
 ,tag             IN VARCHAR2
 ,ckp_scn         IN NUMBER
 ,ckp_time        IN DATE
 ,create_time     IN DATE
 ,min_offr_recid  IN NUMBER
 ,block_size      IN NUMBER
 ,device_type     IN VARCHAR2
 ,handle          IN VARCHAR2
 ,comments        IN VARCHAR2
 ,media           IN VARCHAR2
 ,media_pool      IN NUMBER
 ,start_time      IN VARCHAR2
 ,completion_time IN DATE
 ,status          IN VARCHAR2
) 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)
      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);
    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 = 'A' -- ### shouldn't this be '<>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
) 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)
      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);
    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 in ('A','U') -- ### shouldn't this be '!=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;

  SELECT high_pc_recid INTO last_xdf_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
) 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 := findIncarnation(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);
  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);
  END IF;

END checkProxyDataFile;

PROCEDURE endProxyResync IS
BEGIN
  UPDATE dbinc SET high_pc_recid = GREATEST(NVL(last_xdf_recid,0),
                                            NVL(last_xal_recid,0))
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_xdf_recid := NULL;
  last_xal_recid := NULL;

END endProxyResync;

/*----------------------------*
 * Corrupt Block resync       *
 *----------------------------*/

FUNCTION beginBackupCorruptionResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_bcb_recid INTO last_bcb_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
) 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)
    VALUES
      (local.bdf_key, bcb_recid, bcb_stamp, piece#, block#, blocks,
       corrupt_scn, decode(marked_corrupt,'YES','Y','NO','N'));
  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
  UPDATE dbinc SET high_bcb_recid = last_bcb_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_bcb_recid := NULL;

END endBackupCorruptionResync;

FUNCTION beginCopyCorruptionResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_ccb_recid INTO last_ccb_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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
) 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)
    VALUES
      (local.cdf_key, ccb_recid, ccb_stamp, block#, blocks,
       corrupt_scn, decode(marked_corrupt,'YES','Y','NO','N'));
  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
  UPDATE dbinc SET high_ccb_recid = last_ccb_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_ccb_recid := NULL;

END endCopyCorruptionResync;

/*----------------------------*
 * Deleted Object resync      *
 *----------------------------*/

FUNCTION beginDeletedObjectResync RETURN NUMBER IS
BEGIN
  checkResync;

  SELECT high_do_recid INTO last_do_recid
  FROM dbinc
  WHERE dbinc.dbinc_key = this_dbinc_key;

  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)
IS
   local            bp%rowtype;
   new_status       VARCHAR2(1);
   rc               boolean;

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;

  -- mark the object deleted. 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 PIECE%') THEN
    BEGIN
      SELECT bp_key, bs_key, piece#, status
      INTO local.bp_key,local.bs_key,local.piece#, local.status
      FROM bp
      WHERE bp.db_key = this_db_key
      AND   bp.bp_recid = checkDeletedObject.object_recid
      AND   bp.bp_stamp = checkDeletedObject.object_stamp;

      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';
      ELSE
         raise_application_error(29999,
             'Internal error in checkDeletedObject(): bad object_type '||
             object_type);
      END IF;
      IF (new_status = 'D') THEN
         DELETE FROM bp
          WHERE bp.bp_key = local.bp_key;
      ELSE
         UPDATE bp SET status = new_status
          WHERE bp.bp_key = local.bp_key;
      END IF;

      -- if the backup piece was available before delete then
      -- revalidate its backup set
      updateBackupSetRec(local.bs_key);
    EXCEPTION
      WHEN no_data_found THEN
        RETURN;
    END;
  END IF;

  IF (object_type = 'DATAFILE COPY') 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 = checkDeletedObject.object_recid
    AND   cdf.cdf_stamp = checkDeletedObject.object_stamp;
  END IF;

  IF (object_type = 'ARCHIVED LOG') THEN
     rc := deleteAL(checkDeletedObject.object_recid,
                    checkDeletedObject.object_stamp);
  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';
    ELSE
       raise_application_error(29999,
           'Internal error in checkDeletedObject(): bad object_type '||
           object_type);
    END IF;

    IF (new_status = '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 = checkDeletedObject.object_recid
          AND xdf.xdf_stamp = checkDeletedObject.object_stamp;
       IF SQL%NOTFOUND 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 = checkDeletedObject.object_recid
             AND xcf.xcf_stamp = checkDeletedObject.object_stamp;
          IF SQL%NOTFOUND THEN
             -- DELETE XAL HERE ONCE ARCHIVELOG PROXY BACKUPS ARE SUPPORTED..
             -- Archivelog proxy backup support is complete in the
             -- dbms_backup_restore and all the layers in the kernel, but rman
             -- does not exploit it yet.  That is why there are no recovery
             -- catalog tables or procedures for handling proxy archivelogs.
             RETURN;
          END IF;
       END IF;
    ELSE
       UPDATE xdf SET status = new_status
        WHERE xdf.dbinc_key IN
            (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
          AND xdf.xdf_recid = checkDeletedObject.object_recid
          AND xdf.xdf_stamp = checkDeletedObject.object_stamp;
       IF SQL%NOTFOUND THEN
          UPDATE xcf SET status = new_status
           WHERE xcf.dbinc_key IN
              (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key)
             AND xcf.xcf_recid = checkDeletedObject.object_recid
             AND xcf.xcf_stamp = checkDeletedObject.object_stamp;
          IF SQL%NOTFOUND THEN
             -- UPDATE XAL HERE ONCE ARCHIVELOG PROXY BACKUPS ARE SUPPORTED..
             -- Archivelog proxy backup support is complete in the
             -- dbms_backup_restore and all the layers in the kernel, but rman
             -- does not exploit it yet.  That is why there are no recovery
             -- catalog tables or procedures for handling proxy archivelogs.
             RETURN;
          END IF;
       END IF;
    END IF;
  END IF;

END checkDeletedObject;

PROCEDURE endDeletedObjectResync IS
BEGIN
  UPDATE dbinc SET high_do_recid = last_do_recid
  WHERE dbinc.dbinc_key = this_dbinc_key;

  last_do_recid := NULL;

END endDeletedObjectResync;

/*-------------------*
 * Change Procedures *
 *-------------------*/

PROCEDURE changeDatafileCopy(
  cdf_recid IN NUMBER
 ,cdf_stamp IN NUMBER
 ,status    IN VARCHAR2
) IS

local    dbinc%rowtype;

BEGIN
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;

  IF (status = 'A' OR status = 'U') 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;

    -- check that the update succeeded
    IF (sql%rowcount = 0) THEN
      raise_application_error(-20101, 'Datafile copy not found');
    END IF;
  ELSIF (status = 'R' OR status = '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;

    -- check that the delete succeeded
    IF (sql%rowcount = 0) THEN
      raise_application_error(-20101, 'Datafile copy not found');
    END IF;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;

  -- changeDatafileCopy is an atomic operation
  commit;

END changeDatafileCopy;

PROCEDURE changeControlfileCopy(
  cdf_recid IN NUMBER
 ,cdf_stamp IN NUMBER
 ,status    IN VARCHAR2
) IS

local    dbinc%rowtype;

BEGIN
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;

  IF (status = 'A' OR status = 'U') 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;

    -- check that the update succeeded
    IF (sql%rowcount = 0) THEN
      raise_application_error(-20103, 'Controlfile copy not found');
    END IF;
  ELSIF (status = 'R' OR status = '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;

    -- check that the delete succeeded
    IF (sql%rowcount = 0) THEN
      raise_application_error(-20103, 'Controlfile copy not found');
    END IF;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;

  -- changeControlfileCopy is an atomic operation
  commit;

END changeControlfileCopy;

PROCEDURE changeArchivedLog(
  al_recid IN NUMBER
 ,al_stamp IN NUMBER
 ,status   IN VARCHAR2)
IS
   local    dbinc%rowtype;
   rc       boolean;
BEGIN
  IF (this_dbinc_key IS NULL) THEN
    raise_application_error(-20020, 'Database incarnation not set');
  END IF;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;

  IF (status = 'A' OR status = 'U') 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;

    -- check that the update succeeded
    IF (sql%rowcount = 0) THEN
      raise_application_error(-20102, 'Archived log not found');
    END IF;
  ELSIF (status = 'R' OR status = 'D') THEN
    rc := deleteAL(changeArchivedLog.al_recid,
                   changeArchivedLog.al_stamp);

    -- check that the delete succeeded
    IF (rc = FALSE) THEN
      raise_application_error(-20102, 'Archived log not found');
    END IF;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;

  -- changeArchivedLog is an atomic operation
  commit;

END changeArchivedLog;

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;
  IF (this_ckp_key IS NOT NULL) THEN
    raise_application_error(-20030, 'Resync in progress');
  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
      raise_application_error(-20104, 'Backup piece not found');
  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
  commit;

END changeBackupPiece;

PROCEDURE changeProxyCopy(
  pc_recid IN NUMBER
 ,pc_stamp IN NUMBER
 ,status   IN VARCHAR2
) IS

BEGIN
  IF this_db_key IS NULL THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  IF this_ckp_key IS NOT NULL THEN
    raise_application_error(-20030, 'Resync in progress');
  END IF;

  IF 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;
      IF sql%rowcount = 0 THEN
        raise_application_error(-20106, 'Proxy not found');
      END IF;
    END IF;
  ELSIF status = 'R' OR status = '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;
      IF sql%rowcount = 0 THEN
        raise_application_error(-20106, 'Proxy not found');
      END IF;
    END IF;
  ELSE
    raise_application_error(-20100, 'Invalid status');
  END IF;

  -- changeProxyCopy is an atomic operation
  commit;

END changeProxyCopy;

/*----------------------------*
 * Stored Script Procedures   *
 *----------------------------*/

PROCEDURE createScript(name IN VARCHAR2) IS
  foo NUMBER;
BEGIN
  scr_key := NULL;                      -- for safety
  IF (this_db_key IS NULL) THEN
    raise_application_error(-20021, 'Database not set');
  END IF;
  SELECT count(*)
    INTO foo
    FROM scr
    WHERE scr.db_key = this_db_key
    AND   scr.scr_name = createScript.name;
  IF foo > 0 THEN
    raise_application_error(-20401, 'script already exists');
  END IF;
  INSERT INTO scr VALUES(rman_seq.nextval, this_db_key, name);
  SELECT rman_seq.currval INTO scr_key FROM dual;
  scr_line := 1;
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;
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;
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;
  -- If the compatible parameter is set higher than this version, then
  -- skip it and return the next in the list.  This is necessary to prevent
  -- old RMAN versions from believing they can work with this package.  They
  -- may be interface compatible but not semantically compatible.
  if (to_number(replace(version_list(version_counter-1), '.')) < compatible)
     then return getPackageVersion;
  end if;
  return version_list(version_counter - 1);
END;

FUNCTION getCatalogVersion RETURN VARCHAR2 IS
version rcver.version%type;
BEGIN
  IF NOT rcverQ%ISOPEN THEN
    open rcverQ;
  END IF;

  FETCH rcverQ into version;

  IF rcverQ%NOTFOUND THEN  -- end of fetch
    close rcverQ;
    return NULL;
  END IF;

  RETURN version;

END;

/*---------------------------------------*
 * Procedures for clone database support *
 *---------------------------------------*/

PROCEDURE setCloneName(file#            IN NUMBER
                      ,creation_change# IN NUMBER
                      ,clone_fname      IN VARCHAR2) IS
BEGIN
  UPDATE df SET df.clone_fname = setCloneName.clone_fname 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;

/*-------------------------*
 * Catalog upgrade support *
 *-------------------------*/

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

PROCEDURE bsStatusRecalc(status IN varchar2) IS

cursor bsQ(status varchar2) IS
  SELECT bs_key
  FROM bs
  WHERE bs.status = bsStatusRecalc.status;

bsQrec bsQ%ROWTYPE;

BEGIN
  FOR bsQrec in bsQ(status) LOOP
    updateBackupSetRec(bsQrec.bs_key);
  END LOOP;
END;

/*-------------------*
 * Utility functions *
 *-------------------*/

PROCEDURE reNormalize(newname IN varchar2, oldname OUT varchar2) IS
BEGIN
   IF newname IS NULL THEN -- initialize
      IF reNorm_dfatt_c%ISOPEN THEN
         CLOSE reNorm_dfatt_c;
      END IF;
      IF reNorm_orl_c%ISOPEN THEN
         CLOSE reNorm_orl_c;
      END IF;
      IF reNorm_al_c%ISOPEN THEN
         CLOSE reNorm_al_c;
      END IF;
      IF reNorm_bp_c%ISOPEN THEN
         CLOSE reNorm_bp_c;
      END IF;
      IF reNorm_ccf_c%ISOPEN THEN
         CLOSE reNorm_ccf_c;
      END IF;
      IF reNorm_dfatt_c%ISOPEN THEN
         CLOSE reNorm_cdf_c;
      END IF;

      reNorm_state := RENORM_DFATT;
   ELSE -- update the previous row
      IF reNorm_state = RENORM_DFATT THEN
         UPDATE dfatt SET fname = newname WHERE CURRENT OF reNorm_dfatt_c;
      ELSIF reNorm_state = RENORM_ORL THEN
         UPDATE orl SET fname = newname WHERE CURRENT OF reNorm_orl_c;
      ELSIF reNorm_state = RENORM_AL THEN
         UPDATE al SET fname = newname,
           fname_hashkey = substr(newname,1,10) || substr(newname, -10)
           WHERE CURRENT OF reNorm_al_c;
      ELSIF reNorm_state = RENORM_BP THEN
         UPDATE bp SET handle = newname,
           handle_hashkey = substr(device_type,1,10) ||
                            substr(newname,1,10)     ||
                            substr(newname,-10)
           WHERE CURRENT OF reNorm_bp_c;
      ELSIF reNorm_state = RENORM_CCF THEN
         UPDATE ccf SET fname = newname,
           fname_hashkey = substr(newname,1,10) || substr(newname, -10)
           WHERE CURRENT OF reNorm_ccf_c;
      ELSIF reNorm_state = RENORM_CDF THEN
         UPDATE cdf SET fname = newname,
           fname_hashkey = substr(newname,1,10) || substr(newname, -10)
           WHERE CURRENT OF reNorm_cdf_c;
      END IF;
   END IF;

   IF reNorm_state = RENORM_DFATT THEN
      IF NOT reNorm_dfatt_c%ISOPEN THEN
         OPEN reNorm_dfatt_c;
      END IF;

      FETCH reNorm_dfatt_c INTO oldname;

      IF reNorm_dfatt_c%NOTFOUND THEN
         CLOSE reNorm_dfatt_c;
         reNorm_state := RENORM_ORL;
      END IF;
   END IF;

   IF reNorm_state = RENORM_ORL THEN
      IF NOT reNorm_orl_c%ISOPEN THEN
         OPEN reNorm_orl_c;
      END IF;

      FETCH reNorm_orl_c INTO oldname;

      IF reNorm_orl_c%NOTFOUND THEN
         CLOSE reNorm_orl_c;
         reNorm_state := RENORM_AL;
      END IF;
   END IF;

   IF reNorm_state = RENORM_AL THEN
      IF NOT reNorm_al_c%ISOPEN THEN
         OPEN reNorm_al_c;
      END IF;

      FETCH reNorm_al_c INTO oldname;

      IF reNorm_al_c%NOTFOUND THEN
         CLOSE reNorm_al_c;
         reNorm_state := RENORM_BP;
      END IF;
   END IF;

   IF reNorm_state = RENORM_BP THEN
      IF NOT reNorm_bp_c%ISOPEN THEN
         OPEN reNorm_bp_c;
      END IF;

      FETCH reNorm_bp_c INTO oldname;

      IF reNorm_bp_c%NOTFOUND THEN
         CLOSE reNorm_bp_c;
         reNorm_state := RENORM_CCF;
      END IF;
   END IF;

   IF reNorm_state = RENORM_CCF THEN
      IF NOT reNorm_ccf_c%ISOPEN THEN
         OPEN reNorm_ccf_c;
      END IF;

      FETCH reNorm_ccf_c INTO oldname;

      IF reNorm_ccf_c%NOTFOUND THEN
         CLOSE reNorm_ccf_c;
         reNorm_state := RENORM_CDF;
      END IF;
   END IF;

   IF reNorm_state = RENORM_CDF THEN
      IF NOT reNorm_cdf_c%ISOPEN THEN
         OPEN reNorm_cdf_c;
      END IF;

      FETCH reNorm_cdf_c INTO oldname;

      IF reNorm_cdf_c%NOTFOUND THEN
         CLOSE reNorm_cdf_c;
         reNorm_state := NULL;
         oldname := NULL;
         commit;
      END IF;
   END IF;
END reNormalize;

/*--------------------------------------------------*
 * Package Instantiation:  Initialize Package State *
 *--------------------------------------------------*/

BEGIN
  tsRec.ts# := NULL;                    -- not in TableSpaceResync
  dfRec.file# := NULL;                  -- not in middle of dfResync
  version_list(1) := '08.00.04';

  -- In 8.0.5 the following changes were made:
  -- 1. Allow null for fname and blocks in checkDatafile.  This was
  -- done for bug 612344, which had to do with datafiles that are
  -- MISSING or UNNAMED (KCCFECKD bit set).  In these cases, the controlfile
  -- does not contain a valid filename, and also implies the filesize
  -- which is in the fileheader cannot be obtained either.
  version_list(2) := '08.00.05';

  -- In 8.1.3 the following changes were made:
  -- 1. Added 'X' (expired) backup piece status.
  version_list(3) := '08.01.03';

  -- In 8.1.6 the following changes were made:
  -- 1)  add stopTime to checkDatafile()
  version_list(4) := '08.01.06';

  version_max_index := 4;

  SELECT to_number(value)
    INTO compatible
    FROM config
   WHERE name = 'compatible';

END dbms_rcvcat;
>>>

define prvtrmnu_plb
<<<
--
-- $Header: prvtrmnu.sql 24-sep-99.14:51:58 fsanchez Exp $
--
CREATE OR REPLACE PACKAGE BODY dbms_rcvman IS

-- prvtrmnu.sql
--
--  Copyright (c) Oracle Corporation 1995, 1996, 1997, 1998, 1999. All Rights Reserved.
--
--    NAME
--      prvtrmnu.sql - Recovery MANager package body
--
--    DESCRIPTION
--      This package contains procedures querying information that
--      Recovery Manager needs from the recovery catalog or the target
--      database control file
--
--      This is the version used by catrman.sql; there is a separate
--      body in the file prvtrmns.pls that is loaded into SYS; this one
--      is loaded into a user schema.
--
--    NOTES
--      Remember to make corresponding changes in prvtrmns.pls
--
--    MODIFIED   (MM/DD/YY)
--    dbeusee     10/20/99 - bug-1043144_8.1.6: fix findProxyCopy
--    fsanchez    09/24/99 - fix findValidBackupSet_c cursor
--    mluong      08/30/99 - fix typo
--    gpongrac    08/10/99 - fix translateArchivedLog                          
--    gpongrac    07/12/99 - bug 927353: translateArchivedLogSCNRange needs to 
--    gpongrac    07/06/99 - change rcver to 8.1.6                             
--    gpongrac    06/16/99 - report obsolete support                           
--    gpongrac    01/12/99 - 8.2 restructure
--    gpongrac    04/08/99 - change REM to --
--    swerthei    12/28/98 - 787381 - consider proxies in getIncrementalSCN
--    swerthei    11/26/98 - more debugging improvements
--    swerthei    11/25/98 - enable debugging code from rman
--    gpongrac    11/25/98 - fix debugging in getRecveryAction0
--    dbeusee     11/13/98 - misc_815: Handle LIST RECOVERABLE UNTIL TIME...
--    dbeusee     11/11/98 - misc_815: fix LIST COPY LIKE...
--    dbeusee     11/07/98 - misc_815: fix LIST COPY/BS FROM TIME/UNTIL TIME..
--    dbeusee     10/30/98 - misc_815: added stamp2date for LIST COPY
--    dbeusee     10/19/98 - misc_815: fix LIST BACKUPSET FROM TIME...
--    gpongrac    10/19/98 - misc_815: fix LIST with TAG
--    dbeusee     10/17/98 - misc_815: fix LIST ALL device type filtering
--    gpongrac    10/05/98 - misc_815: fix recovery
--    gpongrac    09/10/98 - misc_815: fix computeRecoveryActions for list cmd
--    swerthei    10/24/98 - add proxy support to REPORT OBSOLETE
--    swerthei    10/20/98 - add listTranslateProxyDataFile, listGetProxyDataF
--    fsanchez    09/04/98 - bug719092
--    swerthei    06/22/98 - prepare for wrapping to recover.bsq
--    gpongrac    06/04/98 - fix comment
--    swerthei    06/01/98 - add CHANGE PROXY
--    swerthei    05/27/98 - proxy restore
--    dbeusee     05/15/98 - misc_81_fixes_1
--    fsanchez    05/12/98 - duplex_backup_set
--    dbeusee     04/21/98 - rpt_redundancy_enh
--    gpongrac    03/04/98 - fix getPackageVersion
--    dbeusee     04/06/98 - xcheck enh.
--    dbeusee     03/29/98 - Update comment about restore only (not true any m
--    fsanchez    03/25/98 - Duplexed backup sets
--    dbeusee     03/01/98 - list enh.
--    gpongrac    01/23/98 - upgrade catalog_version to 8.0.5
--    dbeusee     02/11/98 - Fix bug 613166.
--    fsanchez    01/04/98 - Allow setDatabase to receive dbid without dbname
--    gpongrac    12/23/97 - change findcontrolfilebackup
--    gpongrac    10/30/97 - bug 560638: fix validatebackupset
--    gpongrac    10/02/97 - allow <= in setuntiltime and dbq
--    gpongrac    09/03/97 - have dfcopy name xlate return filesize
--    tpystyne    09/11/97 - bug 480172, fix name translation
--    swerthei    08/29/97 - add getdatafile.read_only
--    swerthei    08/18/97 - add getdatafile.stop_change#
--    gpongrac    07/02/97 - change to version 8.0.4
--    gpongrac    07/01/97 - avoid offr records with stamp of 0
--    mzhou       08/07/97 - fix order by ts#
--    gpongrac    07/22/97 - chang lrtbs cursor to order by tsid so system ts
--    swerthei    04/23/97 - fix list backupset
--    gpongrac    04/22/97 - offline range can end at resetlogs scn
--    dalpern     04/16/97 - renamed as prvtrmnu, v. prvtrmns
--    gpongrac    04/11/97 - add cfscn to computeRecoveryActions
--    gpongrac    04/10/97 - add rlgtime to getRecoveryAction
--    gpongrac    04/10/97 - fix incarnation check at top of computeRecov...0
--    gpongrac    04/04/97 - add translateBackupPieceTag
--    gpongrac    04/04/97 - deal with controlfiles that have no offline range
--    gpongrac    04/01/97 - add offline range info to computeRecoveryActions
--    swerthei    03/27/97 - add getDataFile.ts_name
--    tpystyne    03/20/97 - update catalog version to 8.00.03
--    swerthei    03/21/97 - fix merge error
--    swerthei    03/12/97 - get file sizes in blocks, for metrics
--    gpongrac    03/17/97 - fix cursor to deal with inplicit offline ranges c
--    gpongrac    03/15/97 - add to_time for implicit offline ranges in comput
--    gpongrac    03/10/97 - remove df_ckpscn as arg to openRecoveryActionCurs
--    gpongrac    03/10/97 - add setDebugOn and setDebugOff
--    gpongrac    03/03/97 - verify df_rlgscn in computeRecoveryActions0
--    gpongrac    02/28/97 - check allocate device types in addAction
--    gpongrac    02/27/97 - only do partial recovery if have current controlf
--    gpongrac    02/26/97 - fix compile errors
--    gpongrac    02/26/97 - fix syntax errors
--    gpongrac    02/26/97 - add new recovery functions
--    gpongrac    02/20/97 - require offr.online_time be strictly less than un
--    gpongrac    02/20/97 - simplify handling of until time/scn in where clau
--    swerthei    01/21/97 - change parameters for translatebackupsetkey
--    swerthei    01/15/97 - add dumpState
--    gpongrac    01/14/97 - add getCloneName
--    tpystyne    12/17/96 - add listRollbackSegTableSpace
--    swerthei    01/08/97 - change parameters for backup piece translation
--    swerthei    01/06/97 - continue REPORT DELETABLE
--    gpongrac    01/06/97 - add nxtscn to getArchivedLog
--    swerthei    01/03/97 - add getParentIncarnation
-- ### comments from 1996 removed

---------------------------------------------
-- *** PACKAGE VARIABLES/TYPES SECTION *** --
---------------------------------------------

----------------------
-- Global Constants --
----------------------

MAXSCNVAL  CONSTANT number := 9e125; -- guaranteed higher than any SCN
MAXSEQVAL  CONSTANT number := 2**32-1;
MINDATEVAL CONSTANT date   := to_date('01/01/1900','MM/DD/YYYY');
MAXDATEVAL CONSTANT date   := to_date('12/31/9999','MM/DD/YYYY');

----------------------
-- Global Variables --
----------------------

this_db_key     number := NULL;
this_dbinc_key  number := NULL;
this_reset_scn  number := NULL;
this_reset_time date;

debug           boolean := FALSE;

--------------------
-- rcvRec_t Stack --
--------------------

TYPE rcvRecTab_t IS TABLE OF rcvRec_t;		-- recovery record stack type

rcvRecStack rcvRecTab_t := rcvRecTab_t();   	-- recovery record stack

-----------------------
-- getPackageVersion --
-----------------------

TYPE versionList_t IS TABLE OF varchar2(8) INDEX BY binary_integer;

versionList		versionList_t;
versionMaxIndex 	binary_integer;
versionCounter 		binary_integer;

-----------------
-- setDatabase --
-----------------

catalogVersion CONSTANT VARCHAR2(8) := '08.01.06';

-- For getParentIncarnation
getParentIncarnationKey number;

---------------------
-- Filtering Flags --
---------------------

allIncarnations	number;			-- allow records from non-current
					-- incarnation

ignoreCreationSCN number;		-- a stupid flag that is here
					-- only to provide behaviour that is
					-- backwards compatible with a kludge
					-- dbeusee put in to compensate for
					-- one of his bugs.

untilSCN        number;
untilTime	date;

restoreSource   number;
restoreTag      bp.tag%TYPE;

-------------------
-- setDeviceType --
-------------------

TYPE deviceList_t IS TABLE OF rc_backup_piece.device_type%TYPE
     INDEX BY binary_integer;

deviceList      deviceList_t;
deviceCount     number;
diskDevice	boolean;
anyDevice       boolean;

----------------------------
-- computeRecoveryActions --
----------------------------

TYPE rcvRecCursor_t IS REF CURSOR return rcvRec_t;

TYPE rcvRecStackState_t IS RECORD
(
   lowAction number,                    -- action with lowest from_scn on
                                        -- rcvRecStack that is not a fullKind.
                                        -- Usually points to top of stack, but
                                        -- if computeRA_allRecords is TRUE,
                                        -- then may point down into stack.
   savePoint number,                    -- most recently added full_act_t
   top       number			-- top of stack
);

rcvRecStackState	rcvRecStackState_t;

computeRA_allRecords 	number;		-- do not stop at first full backup and
					-- return all incremental backups

computeRA_restorable 	boolean;	-- cannot recover the datafile we've
					-- requested to recover, but there
					-- is a backup that we could restore
					-- and recover.

computeRA_available 	boolean;	-- there is a backup available on
					-- some non-allocated device type

-- computeRecoveryAction/addAction/addRedo return code values
action_OK                number := 0;
action_FAIL              number := 1;
action_SKIP              number := 2;
action_OLD_REDO          number := 3;
action_WRONG_INCARNATION number := 4;

old_redo exception;                     -- redo from old incarnation
pragma exception_init(old_redo, -20501);

-----------------------
-- getRecoveryAction --
-----------------------

getRA_containerMask	number;
getRA_actionMask	number;
getRA_likePattern    	cdf.fname%TYPE;

getRA_completedAfter 	date;
getRA_completedBefore	date;

------------------------
-- Controlfile Backup --
------------------------

CURSOR findControlfileCopy(
   currentIncarnation   IN     number 	      DEFAULT TRUE#
  ,tag			IN     varchar2       DEFAULT NULL
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces:  ccf_name, lccf
   SELECT imageCopy_con_t	type_con,
          ccf_key		key_con,
          ccf_recid		recid_con,
 	  ccf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null            	bsStamp_con,
          null			bsKey_con,
          null			bsLevel_con,
	  null                  bsType_con,
          null			pieceCount_con,
	  fname			fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  null			blocks_con,	-- ccf doesn't have blocks
	  block_size		blockSize_con,
	  'DISK'		deviceType_con,
	  completion_time	compTime_con,
	  create_time		cfCreationTime_con,
	
          full_act_t		type_act,
	  0			fromSCN_act,
	  ccf.ckp_scn		toSCN_act,
	  ccf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  ccf.dbinc_key		dbincKey_act,
          null			level_act,

	  0			dfNumber_obj,
	  0			dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM ccf, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = ccf.dbinc_key
      AND (findControlfileCopy.currentIncarnation = FALSE# OR
           this_dbinc_key = ccf.dbinc_key)
      AND (findControlfileCopy.tag is NULL OR
           findControlfileCopy.tag = tag)
      AND (findControlfileCopy.pattern is NULL OR
           fname LIKE findControlfileCopy.pattern)
      AND (findControlfileCopy.completedAfter is NULL OR
           completion_time >= findControlfileCopy.completedAfter)
      AND (findControlfileCopy.completedBefore is NULL OR
	   completion_time <= findControlfileCopy.completedBefore)
      AND (findControlfileCopy.untilSCN is NULL OR
           ccf.ckp_scn <= findControlfileCopy.untilSCN)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
      -- CHANGE/CROSSCHECK sets status to 'D', not 'X'.  I think this was
      -- done to maintain backwards compatibility.
                     decode(expired,     1, 'D', NULL))
    ORDER BY toSCN_act desc,		-- for finding best backup
             stamp_con desc;		-- for LIST and to get most recent


CURSOR findControlfileProxyCopy(
   currentIncarnation   IN     number 	      DEFAULT TRUE#
  ,tag			IN     varchar2       DEFAULT NULL
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces: xcfq, lxdf
   SELECT proxyCopy_con_t	type_con,
          xcf_key		key_con,
          xcf_recid		recid_con,
 	  xcf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
	  null                  bsType_con,
          null			pieceCount_con,
	  handle		fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  null			blocks_con,	-- xcf doesn't have blocks
	  block_size		blockSize_con,
	  device_type		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  xcf.ckp_scn		toSCN_act,
	  xcf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  xcf.dbinc_key		dbincKey_act,
          null			level_act,

	  0			dfNumber_obj,
	  0			dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM xcf, dbinc
    WHERE db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = xcf.dbinc_key
      AND (findControlfileProxyCopy.currentIncarnation = FALSE# OR
           this_dbinc_key = xcf.dbinc_key)
      AND (findControlfileProxyCopy.tag is NULL OR
           findControlfileProxyCopy.tag = tag)
      AND (findControlfileProxyCopy.pattern is NULL OR
           handle LIKE findControlfileProxyCopy.pattern)
      AND (findControlfileProxyCopy.completedAfter is NULL OR
           completion_time >= findControlfileProxyCopy.completedAfter)
      AND (findControlfileProxyCopy.completedBefore is NULL OR
	   completion_time <= findControlfileProxyCopy.completedBefore)
      AND (findControlfileProxyCopy.untilSCN is NULL OR
           xcf.ckp_scn <= findControlfileProxyCopy.untilSCN)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
                     decode(expired,     1, 'X', NULL))
    ORDER BY toSCN_act desc,		-- to find best backup
             stamp_con desc;		-- for LIST and to get most recent

CURSOR findControlfileBackup_c(
   currentIncarnation   IN     number 	      DEFAULT TRUE#
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL)
RETURN rcvRec_t IS
      -- Relaces: bcfq, lbcf.  Note that for lbcf, filtering for tags
      -- is now done elsewhere.
   SELECT backupSet_con_t	type_con,
          bcf_key		key_con,
  	  bcf_recid		recid_con,
 	  bcf_stamp		stamp_con,
	  bs.set_stamp		setStamp_con,
   	  bs.set_count		setCount_con,
          bs.bs_recid		bsRecid_con,
          bs.bs_stamp		bsStamp_con,
	  bs.bs_key		bsKey_con,
          bs.incr_level		bsLevel_con,
	  bs.bck_type           bsType_con,
          bs.pieces		pieceCount_con,
	  null		 	fileName_con,
	  null			tag_con,
          null			copyNumber_con,
	  null	 		status_con,
	  null			blocks_con,	-- bcf doesn't have blocks
	  bcf.block_size	blockSize_con,
	  null			deviceType_con,
	  bs.completion_time	compTime_con,
	  bcf.create_time	cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  bcf.ckp_scn		toSCN_act,
	  bcf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  bcf.dbinc_key		dbincKey_act,
          null			level_act,

	  0			dfNumber_obj,
	  0			dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM bcf, bs, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND bs.db_key = this_db_key		-- belongs to this database
      AND bcf.dbinc_key = dbinc.dbinc_key	-- join bcf and dbinc
      AND bcf.bs_key = bs.bs_key		-- join bcf and bs
      AND (findControlfileBackup_c.currentIncarnation = FALSE# OR
           this_dbinc_key = bcf.dbinc_key)
      AND (findControlfileBackup_c.completedAfter is NULL OR
           bs.completion_time >= findControlfileBackup_c.completedAfter)
      AND (findControlfileBackup_c.completedBefore is NULL OR
           bs.completion_time <= findControlfileBackup_c.completedBefore)
      AND (findControlfileBackup_c.untilSCN is NULL OR
           bcf.ckp_scn <= findControlfileBackup_c.untilSCN)
    ORDER BY toSCN_act desc,
             stamp_con desc;

---------------------
-- Datafile Backup --
---------------------

CURSOR findDatafileCopyFno(
   fno                  IN     number
  ,crescn               IN     number         DEFAULT NULL
  ,currentIncarnation   IN     number         DEFAULT TRUE#
  ,tag			IN     varchar2       DEFAULT NULL
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL
  ,level		IN     number         DEFAULT NULL
  ,minSCN		IN     number         DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces these cursors:  lcdf cdf_incrq
   SELECT imageCopy_con_t	type_con,
          cdf_key		key_con,
          cdf_recid		recid_con,
 	  cdf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null			pieceCount_con,
          null                  bsType_con,
	  fname			fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  blocks		blocks_con,
	  block_size		blockSize_con,
	  'DISK'		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  cdf.ckp_scn		toSCN_act,
	  cdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  cdf.dbinc_key		dbincKey_act,
          incr_level		level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM cdf, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = cdf.dbinc_key	-- join cdf and dbinc
      AND (findDatafileCopyFno.fno = file#)
      AND (findDatafileCopyFno.crescn IS NULL OR
           findDatafileCopyFno.crescn = create_scn)
      AND (findDatafileCopyFno.currentIncarnation = FALSE# OR
           this_dbinc_key = cdf.dbinc_key)
      AND (findDatafileCopyFno.tag is NULL OR
           findDatafileCopyFno.tag = tag)
      AND (findDatafileCopyFno.pattern is NULL OR
           fname LIKE findDatafileCopyFno.pattern)
      AND (findDatafileCopyFno.completedAfter is NULL OR
           completion_time >= findDatafileCopyFno.completedAfter)
      AND (findDatafileCopyFno.completedBefore is NULL OR
	   completion_time <= findDatafileCopyFno.completedBefore)
      AND (findDatafileCopyFno.untilSCN is NULL OR
           cdf.ckp_scn <= findDatafileCopyFno.untilSCN)
      AND (findDatafileCopyFno.level is NULL OR
           incr_level <= findDatafileCopyFno.level)
      AND (findDatafileCopyFno.minSCN is NULL OR
           cdf.ckp_scn >= findDatafileCopyFno.minSCN)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
      -- CHANGE/CROSSCHECK sets status to 'D', not 'X'.  I think this was
      -- done to maintain backwards compatibility.
                     decode(expired,     1, 'D', NULL))
    ORDER BY dfNumber_obj,		-- for duplicate filtering
             toSCN_act desc,		-- for tag translation
             stamp_con desc;		-- to get most recent

CURSOR findDatafileCopyKey(
   copyKey              IN     number
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces these cursors:  cursor in translateDataFileCopyKey
   SELECT imageCopy_con_t	type_con,
          cdf_key		key_con,
          cdf_recid		recid_con,
 	  cdf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
	  fname			fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  blocks		blocks_con,
	  block_size		blockSize_con,
	  'DISK'		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  cdf.ckp_scn		toSCN_act,
	  cdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  cdf.dbinc_key		dbincKey_act,
          incr_level		level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM cdf, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = cdf.dbinc_key	-- join cdf and dbinc
      AND (findDatafileCopyKey.copyKey = cdf_key)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
      -- CHANGE/CROSSCHECK sets status to 'D', not 'X'.  I think this was
      -- done to maintain backwards compatibility.
                     decode(expired,     1, 'D', NULL))
    ORDER BY dfNumber_obj,		-- for duplicate filtering
             toSCN_act desc,		-- for tag translation
             stamp_con desc;		-- to get most recent

CURSOR findDatafileCopy(
   currentIncarnation   IN     number         DEFAULT TRUE#
  ,tag			IN     varchar2       DEFAULT NULL
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL
  ,level		IN     number         DEFAULT NULL
  ,minSCN		IN     number         DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces these cursors:  cdf_name, cdf_tag, lcdf
   SELECT imageCopy_con_t	type_con,
          cdf_key		key_con,
          cdf_recid		recid_con,
 	  cdf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
	  fname			fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  blocks		blocks_con,
	  block_size		blockSize_con,
	  'DISK'		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  cdf.ckp_scn		toSCN_act,
	  cdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  cdf.dbinc_key		dbincKey_act,
          incr_level		level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM cdf, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = cdf.dbinc_key	-- join cdf and dbinc
      AND (findDatafileCopy.currentIncarnation = FALSE# OR
           this_dbinc_key = cdf.dbinc_key)
      AND (findDatafileCopy.tag is NULL OR
           findDatafileCopy.tag = tag)
      AND (findDatafileCopy.pattern is NULL OR
           fname LIKE findDatafileCopy.pattern)
      AND (findDatafileCopy.completedAfter is NULL OR
           completion_time >= findDatafileCopy.completedAfter)
      AND (findDatafileCopy.completedBefore is NULL OR
	   completion_time <= findDatafileCopy.completedBefore)
      AND (findDatafileCopy.untilSCN is NULL OR
           cdf.ckp_scn <= findDatafileCopy.untilSCN)
      AND (findDatafileCopy.level is NULL OR
           incr_level <= findDatafileCopy.level)
      AND (findDatafileCopy.minSCN is NULL OR
           cdf.ckp_scn >= findDatafileCopy.minSCN)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
      -- CHANGE/CROSSCHECK sets status to 'D', not 'X'.  I think this was
      -- done to maintain backwards compatibility.
                     decode(expired,     1, 'D', NULL))
    ORDER BY dfNumber_obj,		-- for duplicate filtering
             toSCN_act desc,		-- for tag translation
             stamp_con desc;		-- to get most recent

CURSOR findDatafileProxyCopyFno(
   fno                  IN     number
  ,crescn               IN     number
  ,currentIncarnation   IN     number         DEFAULT TRUE#
  ,tag			IN     varchar2       DEFAULT NULL
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL
  ,level		IN     number         DEFAULT NULL
  ,minSCN		IN     number         DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces these cursors: lxdf
   SELECT proxyCopy_con_t	type_con,
          xdf_key		key_con,
  	  xdf_recid		recid_con,
 	  xdf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
	  handle		fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  blocks		blocks_con,
	  block_size		blockSize_con,
	  device_type		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  xdf.ckp_scn		toSCN_act,
	  xdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  xdf.dbinc_key		dbincKey_act,
          incr_level		level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM xdf, dbinc
    WHERE db_key = this_db_key			-- belongs to this database
      AND dbinc.dbinc_key = xdf.dbinc_key	-- join xdf and dbinc
      AND (findDatafileProxyCopyFno.currentIncarnation = FALSE# OR
           this_dbinc_key = xdf.dbinc_key)
      AND (findDatafileProxyCopyFno.fno = file#)
      AND (findDatafileProxyCopyFno.crescn IS NULL OR
           findDatafileProxyCopyFno.crescn = create_scn)
      AND (findDatafileProxyCopyFno.tag is NULL OR
           findDatafileProxyCopyFno.tag = tag)
      AND (findDatafileProxyCopyFno.pattern is NULL OR
           handle LIKE findDatafileProxyCopyFno.pattern)
      AND (findDatafileProxyCopyFno.completedAfter is NULL OR
           completion_time >= findDatafileProxyCopyFno.completedAfter)
      AND (findDatafileProxyCopyFno.completedBefore is NULL OR
	   completion_time <= findDatafileProxyCopyFno.completedBefore)
      AND (findDatafileProxyCopyFno.untilSCN is NULL OR
           xdf.ckp_scn <= findDatafileProxyCopyFno.untilSCN)
      AND (findDatafileProxyCopyFno.level is NULL OR
           incr_level <= findDatafileProxyCopyFno.level)
      AND (findDatafileProxyCopyFno.minSCN is NULL OR
           xdf.ckp_scn >= findDatafileProxyCopyFno.minSCN)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
                     decode(expired,     1, 'X', NULL))
    ORDER BY toSCN_act desc,
             stamp_con desc;

CURSOR findDatafileBackup(
   fno                  IN     number	      -- not null so we can use index
  ,crescn               IN     number
  ,rlgSCN               IN     number         DEFAULT NULL
  ,rlgTime              IN     date           DEFAULT NULL
  ,completedAfter	IN     date           DEFAULT NULL
  ,completedBefore	IN     date           DEFAULT NULL
  ,untilSCN		IN     number         DEFAULT NULL
  ,level		IN     number         DEFAULT NULL
  ,minSCN		IN     number         DEFAULT NULL)
RETURN rcvRec_t IS
   -- Replaces:  lbdf
   SELECT backupSet_con_t	type_con,
          bdf_key		key_con,
  	  bdf_recid		recid_con,
 	  bdf_stamp		stamp_con,
	  bs.set_stamp		setStamp_con,
   	  bs.set_count		setCount_con,
          bs.bs_recid		bsRecid_con,
	  bs.bs_stamp        	bsStamp_con,
	  bs.bs_key		bsKey_con,
          bs.incr_level		bsLevel_con,
          bs.bck_type           bsType_con,
          bs.pieces		pieceCount_con,
	  null			fileName_con,
	  null			tag_con,
          null			copyNumber_con,
	  null	 		status_con,
	  bdf.blocks		blocks_con,
	  bdf.block_size	blockSize_con,
	  null			deviceType_con,
	  bs.completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          decode(bdf.incr_scn, 0, full_act_t, incremental_act_t)
				type_act,
	  bdf.incr_scn		fromSCN_act,
	  bdf.ckp_scn		toSCN_act,
	  bdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  bdf.dbinc_key		dbincKey_act,
          bdf.incr_level	level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM bdf, bs, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND bs.db_key = this_db_key		-- belongs to this database
      AND bdf.dbinc_key = dbinc.dbinc_key	-- join bdf and dbinc
      AND bdf.bs_key = bs.bs_key		-- join bdf and bs
      AND (findDatafileBackup.fno = file#)
      AND (findDatafileBackup.rlgSCN IS NULL OR
           (dbinc.reset_scn = rlgSCN AND
            dbinc.reset_time = rlgTime))
      AND (findDatafileBackup.crescn IS NULL OR
           findDatafileBackup.crescn = create_scn)
      AND (findDatafileBackup.completedAfter is NULL OR
           bs.completion_time >= findDatafileBackup.completedAfter)
      AND (findDatafileBackup.completedBefore is NULL OR
	   bs.completion_time <= findDatafileBackup.completedBefore)
      AND (findDatafileBackup.untilSCN is NULL OR
           bdf.ckp_scn <= findDatafileBackup.untilSCN)
      AND (findDatafileBackup.level is NULL OR
           bdf.incr_level <= findDatafileBackup.level)
      AND (findDatafileBackup.minSCN is NULL OR
           bdf.ckp_scn >= findDatafileBackup.minSCN)
    ORDER BY stamp_con desc;			-- to get most recent

------------------
-- Proxy Copies --
------------------

CURSOR findProxyCopy(
   tag			IN     varchar2	      DEFAULT NULL
  ,handle		IN     varchar2       DEFAULT NULL
  ,deviceType		IN     varchar2       DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   SELECT proxyCopy_con_t	type_con,
          xdf_key		key_con,
  	  xdf_recid		recid_con,
 	  xdf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null              	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
          handle		fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
          blocks		blocks_con,
	  block_size		blockSize_con,
	  device_type		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
          xdf.ckp_scn		toSCN_act,
	  xdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  xdf.dbinc_key		dbincKey_act,
          incr_level		level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM xdf, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = xdf.dbinc_key	-- join xdf and dbinc
      AND (findProxyCopy.tag IS NULL OR
           findProxyCopy.tag = tag)
      AND (findProxyCopy.handle IS NULL OR
           findProxyCopy.handle = handle)
      AND (findProxyCopy.deviceType IS NULL OR
           findProxyCopy.deviceType = device_type)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(expired,     1, 'X', NULL),
                     decode(deleted,     1, 'D', NULL))
    UNION ALL
   SELECT proxyCopy_con_t	type_con,
          xcf_key		key_con,
          xcf_recid		recid_con,
 	  xcf_stamp		stamp_con,
          null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
	  handle		fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
          to_number(null)       blocks_con,     -- xcf doesn't have blocks
	  block_size		blockSize_con,
          device_type		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  xcf.ckp_scn		toSCN_act,
	  xcf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  xcf.dbinc_key		dbincKey_act,
          to_number(null)       level_act,

	  0			dfNumber_obj,
	  0			dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM xcf, dbinc
    WHERE db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = xcf.dbinc_key	-- join dbinc and xcf
      AND (findProxyCopy.tag IS NULL OR
           findProxyCopy.tag = tag)
      AND (findProxyCopy.handle IS NULL OR
           findProxyCopy.handle = handle)
      AND (findProxyCopy.deviceType IS NULL OR
           findProxyCopy.deviceType = device_type)
      AND status IN (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(expired,     1, 'X', NULL),
                     decode(deleted,     1, 'D', NULL));

CURSOR findProxyCopyKey(
   key			IN     number         DEFAULT NULL
  ,deviceType		IN     varchar2       DEFAULT NULL
  ,available		IN     number         DEFAULT TRUE#
  ,unavailable		IN     number         DEFAULT FALSE#
  ,deleted 		IN     number         DEFAULT FALSE#
  ,expired 		IN     number         DEFAULT FALSE#)
RETURN rcvRec_t IS
   SELECT proxyCopy_con_t	type_con,
          xdf_key		key_con,
  	  xdf_recid		recid_con,
 	  xdf_stamp		stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null              	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
          handle		fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
          blocks		blocks_con,
	  block_size		blockSize_con,
	  device_type		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
          xdf.ckp_scn		toSCN_act,
	  xdf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  xdf.dbinc_key		dbincKey_act,
          incr_level		level_act,

	  file#			dfNumber_obj,
	  create_scn		dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM xdf, dbinc
    WHERE dbinc.db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = xdf.dbinc_key	-- join xdf and dbinc
      AND (findProxyCopyKey.key = xdf_key)
      AND (findProxyCopyKey.deviceType IS NULL OR
           findProxyCopyKey.deviceType = device_type)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(expired,     1, 'X', NULL),
                     decode(deleted,     1, 'D', NULL))
    UNION ALL
   SELECT proxyCopy_con_t	type_con,
          xcf_key		key_con,
          xcf_recid		recid_con,
 	  xcf_stamp		stamp_con,
          null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
	  handle		fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
          to_number(null)       blocks_con,     -- xcf doesn't have blocks
	  block_size		blockSize_con,
          device_type		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  xcf.ckp_scn		toSCN_act,
	  xcf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  xcf.dbinc_key		dbincKey_act,
          to_number(null)       level_act,

	  0			dfNumber_obj,
	  0			dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM xcf, dbinc
    WHERE db_key = this_db_key		-- belongs to this database
      AND dbinc.dbinc_key = xcf.dbinc_key	-- join dbinc and xcf
      AND (findProxyCopyKey.key = xcf_key)
      AND (findProxyCopyKey.deviceType IS NULL OR
           findProxyCopyKey.deviceType = device_type)
      AND status IN (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(expired,     1, 'X', NULL),
                     decode(deleted,     1, 'D', NULL));

------------------------
-- Archivelog Backups --
------------------------

CURSOR findArchivedLogCopy(
   thread		IN     number
  ,sequence		IN     number
  ,lowSCN		IN     number
  ,pattern		IN     varchar2       DEFAULT NULL
  ,completedAfter	IN     date	      DEFAULT NULL
  ,completedBefore      IN     date	      DEFAULT NULL
  ,available		IN     number 	      DEFAULT TRUE#
  ,unavailable		IN     number 	      DEFAULT FALSE#
  ,deleted 		IN     number 	      DEFAULT FALSE#
  ,expired 		IN     number 	      DEFAULT FALSE#)
RETURN rcvRec_t IS
   -- Replaces these cursors:  lcal
   SELECT imageCopy_con_t	type_con,
          al_key		key_con,
          recid			recid_con,
 	  stamp			stamp_con,
	  null			setStamp_con,
   	  null			setCount_con,
          null			bsRecid_con,
	  null               	bsStamp_con,
	  null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
          name			fileName_con,
	  null			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  blocks		blocks_con,
          block_size		blockSize_con,
	  'DISK'		deviceType_con,
	  completion_time	compTime_con,
	  to_date(null)		cfCreationTime_con,

          full_act_t		type_act,
	  null			fromSCN_act,
          null			toSCN_act,
	  null			toTime_act,
	  null			rlgSCN_act,
	  null			rlgTime_act,
	  dbinc_key		dbincKey_act,
          null			level_act,

	  null			dfNumber_obj,
	  null			dfCreationSCN_obj,
	  sequence#		logSequence_obj,
	  thread#		logThread_obj,
          resetlogs_change#  	logRlgSCN_obj,
          resetlogs_time	logRlgTime_obj,
	  first_change#		logLowSCN_obj,
	  first_time		logLowTime_obj,
	  next_change#		logNextSCN_obj,
	  next_time		logNextTime_obj
     FROM rc_archived_log
    WHERE dbinc_key = this_dbinc_key
      AND thread# = thread
      AND sequence# = sequence
      AND first_change# = lowSCN
      -- Pattern isn't really needed here.  The LIKE clause should be
      -- handled during name translation by krmkaltr.  However, krmg.y
      -- allows the LIKE clause as a listQualifier, so we need to support
      -- it for that bizzarre case.
      AND (pattern IS NULL OR name LIKE pattern)
      AND (completedAfter IS NULL OR completion_time >= completedAfter)
      AND (completedBefore IS NULL OR completion_time <= completedBefore)
      AND status IN (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
                     decode(expired,     1, 'X', NULL))
    ORDER BY stamp_con desc;

CURSOR findArcLogBackup(
   thread		IN     number
  ,sequence		IN     number
  ,lowSCN		IN     number
  ,completedAfter	IN     date	      DEFAULT NULL
  ,completedBefore      IN     date	      DEFAULT NULL)
RETURN rcvRec_t IS
   SELECT backupSet_con_t	type_con,
          brl_key		key_con,
          recid			recid_con,
 	  stamp			stamp_con,
	  set_stamp		setStamp_con,
   	  set_count		setCount_con,
          bs_recid		bsRecid_con,
	  bs_stamp           	bsStamp_con,
	  bs_key		bsKey_con,
          null			bsLevel_con,
          backup_type           bsType_con,
          pieces                pieceCount_con,
	  null			fileName_con,
	  null			tag_con,
          null			copyNumber_con,
	  null	 		status_con,
	  blocks		blocks_con,
	  block_size		blockSize_con,
	  null			deviceType_con,
	  completion_time	compTime_con,
	  null			cfCreationTime_con,

          full_act_t		type_act,
	  null			fromSCN_act,
          null			toSCN_act,
	  null			toTime_act,
	  null			rlgSCN_act,
	  null			rlgTime_act,
	  dbinc_key		dbincKey_act,
          null			level_act,

	  null			dfNumber_obj,
	  null			dfCreationSCN_obj,
          sequence#		logSequence_obj,
	  thread#		logThread_obj,
          resetlogs_change# 	logRlgSCN_obj,
          resetlogs_time	logRlgTime_obj,
	  first_change#		logLowSCN_obj,
	  first_time		logLowTime_obj,
          next_change#		logNextSCN_obj,
	  next_time		logNextTime_obj
     FROM rc_backup_redolog
    WHERE dbinc_key = this_dbinc_key
      AND thread# = thread
      AND sequence# = sequence
      AND first_change# = lowSCN
      -- LIKE as a listOperand is not allowed for LIST BACKUP, so
      -- pattern is ignored here.
      AND (completedAfter IS NULL OR completion_time >= completedAfter)
      AND (completedBefore IS NULL OR completion_time <= completedBefore)
    ORDER BY stamp_con desc;

---------------------------
-- Backup Set Validation --
---------------------------

TYPE validateBackupSetCursor_t IS REF CURSOR RETURN validBackupSetRec_t;

getValidBackupSetLast	validBackupSetRec_t;

-- This cursor returns the device_type(s), tags, and copy#'s for which all
-- backup pieces of the specified backup set are available.  A code field
-- is also provided which indicates whether the ORDER BY included all
-- 3 fields (1), or just device_type and tag (2), or just device_type (3).

-- NOTE:  If you change the ORDER BY in the cursor, you probably need
--        to change getValidBackupSet.  It depends on the order-by.

CURSOR findValidBackupSet_c(
   bsKey		   IN     number
  ,pieceCount              IN     number
  ,deviceType		   IN	  varchar2 DEFAULT NULL
  ,tag			   IN     varchar2 DEFAULT NULL
  ,available               IN     number   DEFAULT TRUE#
  ,unavailable             IN     number   DEFAULT FALSE#
  ,deleted                 IN     number   DEFAULT FALSE#
  ,expired                 IN     number   DEFAULT FALSE#)
RETURN validBackupSetRec_t IS
   -- Group by device_type, tag, and copy#.  This way, we can see if there is
   -- a set of pieces with the same copy# and tag.
   SELECT device_type, tag, copy#, 1
     FROM rc_backup_piece
    WHERE bs_key = findValidBackupSet_c.bsKey
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
                     decode(expired,     1, 'X', NULL))
      AND (findValidBackupSet_c.tag IS NULL OR
           findValidBackupSet_c.tag = tag)
      AND (findValidBackupSet_c.deviceType IS NULL OR
           findValidBackupSet_c.deviceType = device_type)
    GROUP BY device_type, tag, copy#
   HAVING count(DISTINCT piece#) = findValidBackupSet_c.pieceCount

    UNION ALL

   -- Allow a mix of copy numbers, but still the same tag.  It is possible
   -- that the backup set is available only if pieces with different copy#'s
   -- are combined to form the complete set.
   SELECT device_type, tag, to_number(null), 2
     FROM rc_backup_piece
    WHERE bs_key = findValidBackupSet_c.bsKey
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
                     decode(expired,     1, 'X', NULL))
      AND (findValidBackupSet_c.tag IS NULL OR
           findValidBackupSet_c.tag = tag)
      AND (findValidBackupSet_c.deviceType IS NULL OR
           findValidBackupSet_c.deviceType = device_type)
    GROUP BY device_type, tag
   HAVING count(DISTINCT piece#) = findValidBackupSet_c.pieceCount

    UNION ALL

   -- Allow a mix of tags.  It is possible that the backup set is available
   -- only if we combine pieces with different tags.  Since we are mixing
   -- tags, we are definitly also going to be mixing copy#s because any set of
   -- pieces composed of pieces with different tags must also have different
   -- copy#s.  Note that this is really moot until BACKUP BACKUP PIECE is
   -- implemented.  Until then all pieces of a backup set will have the same
   -- tag, or no tag at all.
   SELECT device_type, to_char(null), to_number(null), 3
     FROM rc_backup_piece
    WHERE bs_key = findValidBackupSet_c.bsKey
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL),
                     decode(expired,     1, 'X', NULL))
      AND (findValidBackupSet_c.tag IS NULL OR
           findValidBackupSet_c.tag = tag)
      AND (findValidBackupSet_c.deviceType IS NULL OR
           findValidBackupSet_c.deviceType = device_type)
    GROUP BY device_type
   HAVING count(DISTINCT piece#) = findValidBackupSet_c.pieceCount
    ORDER BY 1,2,3,4;

-------------------
-- Backup Pieces --
-------------------

-- Replaces the following 8.1.x cursors:
-- bsq1, bsq2
-- NOTE:  no indexs are used, so this cusor is likely to require a full
-- table scan of the bp table.

CURSOR findBackupPiece_c(
   tag          IN     varchar2 DEFAULT NULL
  ,handle	IN     varchar2 DEFAULT NULL
  ,deviceType   IN     varchar2 DEFAULT NULL
  ,copyNumber   IN     number   DEFAULT NULL
  ,available 	IN     number   DEFAULT TRUE#
  ,unavailable 	IN     number   DEFAULT FALSE#
  ,deleted 	IN     number   DEFAULT FALSE#
  ,expired 	IN     number   DEFAULT FALSE#)
RETURN bpRec_t IS
   SELECT bp_recid,
          bp_stamp,
          bp_key,
          set_stamp,
          set_count,
          piece#,
          copy#,
          bp.status,
          bp.completion_time,
          handle,
          tag
     FROM bp, bs
    WHERE bp.db_key = this_db_key		-- belongs to this db
      AND bs.db_key = this_db_key		-- belongs to this db
      AND bp.bs_key = bs.bs_key			-- join bp and bs
      AND (findBackupPiece_c.tag IS NULL OR
           tag = findBackupPiece_c.tag)
      AND (findBackupPiece_c.handle IS NULL OR
           handle = findBackupPiece_c.handle)
      AND (findBackupPiece_c.deviceType IS NULL OR
           device_type = findBackupPiece_c.deviceType)
      AND (findBackupPiece_c.copyNumber IS NULL OR
           copy# = findBackupPiece_c.copyNumber)
      AND bp.status in (decode(available,   1, 'A', NULL),
                        decode(unavailable, 1, 'U', NULL),
                        decode(deleted,     1, 'D', NULL),
                        decode(expired,     1, 'X', NULL))
    ORDER BY piece#, copy# desc, bp_stamp desc;

CURSOR findBackupPieceBpKey(
   bpKey 	IN     number
  ,tag          IN     varchar2 DEFAULT NULL
  ,handle	IN     varchar2 DEFAULT NULL
  ,deviceType   IN     varchar2 DEFAULT NULL
  ,copyNumber   IN     number   DEFAULT NULL
  ,available 	IN     number   DEFAULT TRUE#
  ,unavailable 	IN     number   DEFAULT FALSE#
  ,deleted 	IN     number   DEFAULT FALSE#
  ,expired 	IN     number   DEFAULT FALSE#)
RETURN bpRec_t IS
   SELECT bp_recid,
          bp_stamp,
          bp_key,
          set_stamp,
          set_count,
          piece#,
          copy#,
          bp.status,
          bp.completion_time,
          handle,
          tag
     FROM bp, bs
    WHERE bp.db_key = this_db_key
      AND bs.db_key = this_db_key
      AND bp.bs_key = bs.bs_key
      AND (bp_key = findBackupPieceBpKey.bpkey)
      AND (findBackupPieceBpKey.tag IS NULL OR
           tag = findBackupPieceBpKey.tag)
      AND (findBackupPieceBpKey.handle IS NULL OR
           handle = findBackupPieceBpKey.handle)
      AND (findBackupPieceBpKey.deviceType IS NULL OR
           device_type = findBackupPieceBpKey.deviceType)
      AND (findBackupPieceBpKey.copyNumber IS NULL OR
           copy# = findBackupPieceBpKey.copyNumber)
      AND bp.status in (decode(available,   1, 'A', NULL),
                        decode(unavailable, 1, 'U', NULL),
                        decode(deleted,     1, 'D', NULL),
                        decode(expired,     1, 'X', NULL))
    ORDER BY piece#, copy# desc, bp_stamp desc;

CURSOR findBackupPieceBsKey(
   bsKey        IN     number
  ,tag          IN     varchar2 DEFAULT NULL
  ,handle	IN     varchar2 DEFAULT NULL
  ,deviceType   IN     varchar2 DEFAULT NULL
  ,copyNumber   IN     number   DEFAULT NULL
  ,available 	IN     number   DEFAULT TRUE#
  ,unavailable 	IN     number   DEFAULT FALSE#
  ,deleted 	IN     number   DEFAULT FALSE#
  ,expired 	IN     number   DEFAULT FALSE#)
RETURN bpRec_t IS
   SELECT bp_recid,
          bp_stamp,
          bp_key,
          set_stamp,
          set_count,
          piece#,
          copy#,
          bp.status,
          bp.completion_time,
          handle,
          tag
     FROM bp, bs
    WHERE bp.db_key = this_db_key
      AND bs.db_key = this_db_key
      AND bp.bs_key = bs.bs_key
      AND (bs.bs_key = findBackupPieceBsKey.bsKey)
      AND (findBackupPieceBsKey.tag IS NULL OR
           tag = findBackupPieceBsKey.tag)
      AND (findBackupPieceBsKey.handle IS NULL OR
           handle = findBackupPieceBsKey.handle)
      AND (findBackupPieceBsKey.deviceType IS NULL OR
           device_type = findBackupPieceBsKey.deviceType)
      AND (findBackupPieceBsKey.copyNumber IS NULL OR
           copy# = findBackupPieceBsKey.copyNumber)
      AND bp.status in (decode(available,   1, 'A', NULL),
                        decode(unavailable, 1, 'U', NULL),
                        decode(deleted,     1, 'D', NULL),
                        decode(expired,     1, 'X', NULL))
    ORDER BY piece#, copy# desc, bp_stamp desc;

----------------------
-- Name Translation --
----------------------

TYPE noRows_t IS RECORD
(
   error	number,			-- error number
   msg		varchar2(100)		-- error msg
);

-- The tablespace_list is used to hold the list of tablespace names to
-- skip when the SKIP TABLESPACE clause is specified.

TYPE tablespaceList_t is table of rc_tablespace.name%TYPE
  index by binary_integer;

skipTablespaceList	tablespaceList_t;
skipTablespaceCount 	number;		-- number of tablespaces in list

--------------------------
-- Datafile Translation --
--------------------------

getDatafileCursor varchar2(30);		-- pointer to current cursor
getDatafileNoRows noRows_t;		-- Set by function that opens cursor
getDatafileLast	  dfRec_t;		-- The last row returned

-- Translate the database within an SCN range. The datafiles are not part of
-- the database at their until the dictionary transaction commits.
-- Therefore the creation scn must be less than toSCN.
-- ### offline drop makes the above statement dangerous, so allow <=
-- Note that fromSCN and toSCN must not be null.

CURSOR translateDatabase_c(
   fromSCN number,
   toSCN   number)
RETURN dfRec_t IS
   SELECT file#, creation_change#, creation_time,
          name, tablespace_name,
          null, blocks, block_size, bytes / 1024,
          null, stop_change#, read_only
     FROM rc_datafile
    WHERE db_key = this_db_key AND		-- belongs to this database
          dbinc_key = this_dbinc_key AND
          creation_change# <= toSCN AND
          (drop_change# is null OR drop_change# > fromSCN)
    ORDER BY file#;

-- Translate a tablespace name (always relative to current time)

CURSOR translateTablespace_c(
   tsName varchar2)
RETURN dfRec_t IS
   SELECT file#, creation_change#, creation_time,
          name, tablespace_name,
          null, blocks, block_size, bytes / 1024,
          null, stop_change#, read_only
     FROM rc_datafile
    WHERE db_key = this_db_key AND		-- part of this db
	  tablespace_name = translateTablespace_c.tsName AND
          dbinc_key = this_dbinc_key AND
          ((untilSCN is null AND drop_change# is null) OR
           ((creation_change# <= untilSCN) AND
            (drop_change# is null or drop_change# > untilSCN)))
    ORDER BY file#;

-- Translate a datafile name (always relative to current time

CURSOR translateDatafileName(
   fileName varchar2)
RETURN dfRec_t IS
   SELECT file#, creation_change#, creation_time,
          name, tablespace_name,
          null, blocks, block_size, bytes / 1024,
          null, stop_change#, read_only
     FROM rc_datafile
    WHERE db_key = this_db_key AND		-- belongs to this database
          name = translateDatafilename.fileName AND -- filename matches
          dbinc_key = this_dbinc_key AND
          drop_change# is null AND	-- filename currently part of db
          (untilSCN is null OR creation_change# < untilSCN) AND
           -- following "and" clause to detect ambiguous names
          ((untilSCN is null) OR	 -- no until clause
           ((untilTime is not null) AND not exists
            (SELECT 1 
               FROM rc_datafile
              WHERE dbinc_key = this_dbinc_key AND
                    name = translateDatafilename.fileName AND
                    nvl(creation_time, MINDATEVAL) < untilTime AND
                    drop_time > untilTime)) OR
           ((untilSCN is not null) AND not exists
            (SELECT 1 
               FROM rc_datafile
              WHERE dbinc_key = this_dbinc_key AND
                    name = translateDatafilename.fileName AND
                    creation_change# < untilSCN AND
                    drop_change# > untilSCN)));

-- Translate a datafile number

CURSOR translateDatafileNumber(
   fno	varchar2)
RETURN dfRec_t IS
   SELECT file#, creation_change#, creation_time,
          name, tablespace_name,
          null, blocks, block_size, bytes / 1024,
          null, stop_change#, read_only
     FROM rc_datafile
    WHERE db_key = this_db_key AND		-- belongs to this database
          file# = translateDataFileNumber.fno AND	-- filenumber matches
          dbinc_key = this_dbinc_key AND
          ((untilSCN is null AND drop_change# is null) OR
           ((nvl(creation_time, MINDATEVAL) < untilTime OR
             creation_change# < untilSCN) AND
            (drop_time > untilTime OR
             drop_change# > untilSCN OR
             drop_change# is null)));

-- Translate a datafile number and checkpoint SCN

CURSOR translateDatafileCheckpoint(
   fno		varchar2
  ,ckpSCN	number)
RETURN dfRec_t IS
   SELECT file#, creation_change#, creation_time,
          name, tablespace_name,
          null, blocks, block_size, bytes / 1024,
          null, stop_change#, read_only
     FROM rc_datafile
    WHERE db_key = this_db_key			-- belongs to this database
      AND file# = translateDatafileCheckpoint.fno	-- filenumber matches
      AND dbinc_key = this_dbinc_key
      AND translateDatafileCheckpoint.ckpSCN >= creation_change#
      AND (drop_change# IS NULL OR
           translateDatafileCheckpoint.ckpSCN < drop_change#);

-- Translate all datafiles that ever existed
-- Note that we return nulls for most fields here.  This is because
-- the controlfile version of this cursor has to return nulls for them
-- because it doesn't have the information, and so we return nulls here
-- so that things are consistant.
-- We assume that if a datafile was added and backed up, then a resync
-- was done before the datafile was dropped.  RMAN could not have created
-- the backup otherwise.
CURSOR translateAllDatafile_c
RETURN dfRec_t IS
   SELECT DISTINCT
          file#			dfNumber,
          creation_change#	dfCreationSCN,
	  creation_time		dfCreationTime,
	  name			fileName,
	  tablespace_name	tsName,
          null			status,
	  blocks		blocks,
	  block_size		blockSize,
	  bytes / 1024		kbytes,
   	  null			unrecovSCN,
	  null			stopSCN,
	  FALSE#		readOnly
     FROM rc_datafile
    WHERE db_key = this_db_key
    ORDER BY file#;

----------------------------
-- Online Log Translation --
----------------------------

CURSOR translateOnlineLogs_c IS
   SELECT thread#, group#, name
     FROM rc_redo_log
    WHERE dbinc_key = this_dbinc_key
    ORDER BY thread#, group#, name;

------------------------------
-- Archived Log Translation --
------------------------------

getArchivedLogNoRows		noRows_t;
getArchivedLogDuplicates	number;		-- Duplicate filtering flag
getArchivedLogLast		alRec_t;	-- used for duplicate filtering
getArchivedLogCursor		varchar2(40);

CURSOR translateArcLogKey(
   alKey	number)
RETURN alRec_t IS
   SELECT al_key,
          recid,
          stamp,
          thread#,
          sequence#,
          name,
          first_change#,
          first_time,
          next_change#,
          next_time,
          resetlogs_change#,
          resetlogs_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM rc_archived_log
    WHERE db_key = this_db_key
      AND al_key = translateArcLogKey.alKey;

CURSOR translateArcLogName(
   fname 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number)			-- IGNORED!
RETURN alRec_t IS
   SELECT al_key,
          recid,
          stamp,
          thread#,
          sequence#,
          name,
          first_change#,
          first_time,
          next_change#,
          next_time,
          resetlogs_change#,
          resetlogs_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM rc_archived_log
    WHERE db_key = this_db_key
      AND name = translateArcLogName.fname
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL))
      AND archived = 'YES'
    ORDER BY stamp desc;

CURSOR translateArcLogSeqRange(
   thread# 	IN number
  ,fromseq# 	IN number
  ,toseq# 	IN number
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number)			-- IGNORED!
RETURN alRec_t IS
   SELECT al_key,
          recid,
          stamp,
          thread#,
          sequence#,
          name,
          first_change#,
          first_time,
          next_change#,
          next_time,
          resetlogs_change#,
          resetlogs_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM rc_archived_log
    WHERE dbinc_key = this_dbinc_key
      AND thread# = translateArcLogSeqRange.thread#
      AND sequence# between nvl(fromseq#, 0)
                        and nvl(toseq#, MAXSEQVAL)
      AND name is not null		-- filter out cleared logs
      AND (pattern is null OR name like pattern)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL))
      AND archived = 'YES'
    ORDER BY thread#, first_change#, sequence#, stamp desc;

CURSOR translateArcLogSeqRange2(
   thread# 	IN number
  ,fromseq# 	IN number
  ,toseq# 	IN number)
RETURN alRec_t IS
   SELECT al_key,
          recid,
          stamp,
          thread#,
          sequence#,
          name,
          first_change#,
          first_time,
          next_change#,
          next_time,
          resetlogs_change#,
          resetlogs_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM rc_archived_log
    WHERE dbinc_key = this_dbinc_key
      AND thread# = translateArcLogSeqRange2.thread#
      AND sequence# between nvl(fromseq#, 0)
                        and nvl(toseq#, MAXSEQVAL)
      AND name is not null		-- filter out cleared logs
      AND archived = 'YES'

    UNION ALL

   SELECT to_number(null),
          to_number(null),
          -1,					-- to sort last (desc)
          thread#,
          sequence#,
          to_char(null),
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          'D',
          to_date(null)
     FROM brl, dbinc
    WHERE brl.dbinc_key = dbinc.dbinc_key	-- join condition
      AND brl.dbinc_key = this_dbinc_key
      AND thread# = translateArcLogSeqRange2.thread#
      AND sequence# between nvl(fromseq#, 0)
                        and nvl(toseq#, MAXSEQVAL)
       --    thread#, low_scn, al_stamp desc
    ORDER BY       4,       7,        3 desc;

CURSOR translateArcLogTimeRange(
   thread# 	IN number
  ,fromTime 	IN date
  ,toTime 	IN date
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number)		-- IGNORED!
RETURN alRec_t IS
   SELECT al_key,
          al_recid,
          al_stamp,
          thread#,
          sequence#,
          fname,
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM al, dbinc
    WHERE al.dbinc_key = this_dbinc_key
      AND dbinc.dbinc_key = this_dbinc_key
      AND (translateArcLogTimeRange.thread# is NULL OR 
           thread# = translateArcLogTimeRange.thread#)
      AND next_time >  nvl(fromTime, MINDATEVAL)
      AND low_time  <= nvl(toTime, MAXDATEVAL)
      AND fname is not null		-- filter out cleared logs
      AND (pattern is null OR fname like pattern)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL))
      AND archived = 'Y'
    ORDER BY thread#, low_scn, sequence#, al_stamp desc;

CURSOR translateArcLogTimeRange2(
   thread# 	IN number
  ,fromTime 	IN date
  ,toTime 	IN date)
RETURN alRec_t IS
   SELECT al_key,
          al_recid,
          al_stamp,
          thread#,
          sequence#,
          fname,
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM al, dbinc
    WHERE al.dbinc_key = this_dbinc_key
      AND dbinc.dbinc_key = this_dbinc_key
      AND (translateArcLogTimeRange2.thread# is NULL OR 
           thread# = translateArcLogTimeRange2.thread#)
      AND next_time >  nvl(fromTime, MINDATEVAL)
      AND low_time  <= nvl(toTime, MAXDATEVAL)
      AND fname is not null		-- filter out cleared logs
      AND archived = 'Y'

    UNION ALL

   SELECT to_number(null),
          to_number(null),
          -1,					-- to sort last
          thread#,
          sequence#,
          to_char(null),
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          'D',
          to_date(null)
     FROM brl, dbinc
    WHERE brl.dbinc_key = dbinc.dbinc_key	-- join condition
      AND brl.dbinc_key = this_dbinc_key
      AND next_time >  nvl(fromTime, MINDATEVAL)
      AND low_time  <= nvl(toTime, MAXDATEVAL)
      AND (translateArcLogTimeRange2.thread# is NULL OR 
           thread# = translateArcLogTimeRange2.thread#)
       --    thread#, low_scn, al_stamp desc
    ORDER BY       4,       7,        3 desc;

-- This is the only cursor that respects the online parameter.

CURSOR translateArcLogSCNRange(
   thread# 	IN number
  ,fromSCN 	IN number
  ,toSCN 	IN number
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number)
RETURN alRec_t IS
   SELECT al_key,
          al_recid,
          al_stamp,
          thread#,
          sequence#,
          fname,
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM al, dbinc
    WHERE al.dbinc_key = this_dbinc_key
      AND dbinc.dbinc_key = this_dbinc_key
      AND (translateArcLogSCNRange.thread# is NULL OR
           thread# = translateArcLogSCNRange.thread#)
      AND next_scn > nvl(fromSCN, 0)
      AND low_scn  < nvl(toSCN, MAXSCNVAL)
      AND fname is not null		-- filter out cleared logs
      AND (pattern is null OR fname like pattern)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL))
      AND (online = 1 OR archived = 'Y')
    ORDER BY thread#, low_scn, al_stamp desc;

-- This cursor is used by krmkdmr().  krmkdmr() uses
-- this to get all archivelogs in the SCN range (x..infinity) where x
-- is the SCN where  media recovery first requests a log.  This is the only
-- cursor that respects the online parameter.  Note that if we have
-- an inspected current online log in the list, then either that log has never
-- been archived, or if it has been archived, then the archived record has
-- a higher stamp, so the order by stamp desc means the archived copy
-- is returned first.
-- We union the al table with the brl table because
-- an archivedlog in the SCN range may have had its al record deleted or
-- uncataloged, but we still have a backup of that log.  We could instead/also
-- union with rc_log_history, but if that gives us a log we wouldn't otherwise
-- see, then we will fail anyway since there is no copy or backup of that log.
-- As it is, we will give a meaningful error message if recovery asks for a log
-- we don't have, so there is no need to look at the log_history.

CURSOR translateArcLogSCNRange2(
   thread# 	IN number
  ,fromSCN 	IN number
  ,toSCN 	IN number
  ,toTime       IN date)
RETURN alRec_t IS
   SELECT al_key,
          al_recid,
          al_stamp,
          thread#,
          sequence#,
          fname,
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM al, dbinc
    WHERE al.dbinc_key = this_dbinc_key
      AND dbinc.dbinc_key = this_dbinc_key
      AND (translateArcLogSCNRange2.thread# is NULL OR
           thread# = translateArcLogSCNRange2.thread#)
      AND next_scn > nvl(fromSCN, 0)
      AND low_scn  < nvl(toSCN, MAXSCNVAL)
      AND (toTime IS NULL OR low_time < toTime)

    UNION ALL

   SELECT to_number(null),
          to_number(null),
          -1,					-- to sort last
          thread#,
          sequence#,
          to_char(null),
          low_scn,
          low_time,
          next_scn,
          next_time,
          reset_scn,
          reset_time,
          blocks,
          block_size,
          'D',
          to_date(null)
     FROM brl, dbinc
    WHERE brl.dbinc_key = dbinc.dbinc_key	-- join condition
      AND brl.dbinc_key = this_dbinc_key
      AND (translateArcLogSCNRange2.thread# is NULL OR
           thread# = translateArcLogSCNRange2.thread#)
      AND next_scn > nvl(fromSCN, 0)
      AND low_scn  < nvl(toSCN, MAXSCNVAL)
      AND (toTime IS NULL OR low_time < toTime)
       --    thread#, low_scn, al_stamp desc;
    ORDER BY       4,       7,        3 desc;

CURSOR translateArcLogPattern(
   pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number)			-- IGNORED!
RETURN alRec_t IS
   SELECT al_key,
          recid,
          stamp,
          thread#,
          sequence#,
          name,
          first_change#,
          first_time,
          next_change#,
          next_time,
          resetlogs_change#,
          resetlogs_time,
          blocks,
          block_size,
          status,
          completion_time
     FROM rc_archived_log
    WHERE dbinc_key = this_dbinc_key
      AND (pattern is null or name like pattern)
      AND status in (decode(available,   1, 'A', NULL),
                     decode(unavailable, 1, 'U', NULL),
                     decode(deleted,     1, 'D', NULL))
      AND archived = 'YES'
    ORDER BY thread#, first_change#, sequence#, stamp desc;

---------------------------------
-- Controlfilecopy Translation --
---------------------------------

getControlFileCopySingleRow	boolean;

---------------------
-- getDatafileCopy --
---------------------

getDatafileCopyCursor           varchar2(30);
getDatafileCopyNoRows		noRows_t;
getDatafileCopyDuplicates	number;
getDatafileCopyLast		rcvRec_t;
getDatafileCopySingleRow        boolean;

------------------
-- getProxyCopy --
------------------

getProxyCopyCursor              varchar2(30);
getProxyCopyNoRows		noRows_t;
getProxyCopyByHandle		boolean;

--------------------
-- getBackupPiece --
--------------------

getBackupPieceCursor		varchar2(30);
getBackupPieceNoRows		noRows_t;
getBackupPieceDuplicates	number;	-- TRUE# -> duplicates OK
					-- FALSE# -> eliminate duplicates
getBackupPieceLast		bpRec_t;
-- getBackupPieceDeviceType must not be null if getBackupPieceDuplicates
-- is FALSE#.
getBackupPieceDeviceType	bp.device_type%TYPE;
getBackupPieceExpectedPieces	number;
getBackupPiecePieceCount	number;
getBackupPieceByHandle		boolean;

----------
-- List --
----------

listGetBackupTag		bp.tag%TYPE;
listGetBackupAvailable		number;
listGetBackupUnavailable	number;
listGetBackupDeleted		number;
listGetBackupExpired		number;

listGetProxyDatafileCursor	varchar2(30);

-- This cursor is just a bad idea.  It is here only for backward
-- compatibility with 8.1.

CURSOR lbal2(thread#       number,
             lowseq        number,
             highseq       number,
             lowscn        number,
             highscn       number,
             from_time     date  ,
             until_time    date)
RETURN rcvRec_t IS
   SELECT backupSet_con_t	type_con,
          brl_key		key_con,
          recid			recid_con,
 	  stamp			stamp_con,
	  set_stamp		setStamp_con,
   	  set_count		setCount_con,
          bs_recid		bsRecid_con,
	  bs_stamp           	bsStamp_con,
	  bs_key		bsKey_con,
          null			bsLevel_con,
          backup_type           bsType_con,
          pieces                pieceCount_con,
	  null			fileName_con,
	  null			tag_con,
          null			copyNumber_con,
	  null	 		status_con,
	  blocks		blocks_con,
	  block_size		blockSize_con,
	  null			deviceType_con,
	  completion_time	compTime_con,
	  null			cfCreationTime_con,

          full_act_t		type_act,
	  null			fromSCN_act,
          null			toSCN_act,
	  null			toTime_act,
	  null			rlgSCN_act,
	  null			rlgTime_act,
	  dbinc_key		dbincKey_act,
          null			level_act,

	  null			dfNumber_obj,
	  null			dfCreationSCN_obj,
          sequence#		logSequence_obj,
	  thread#		logThread_obj,
          resetlogs_change# 	logRlgSCN_obj,
          resetlogs_time	logRlgTime_obj,
	  first_change#		logLowSCN_obj,
	  first_time		logLowTime_obj,
          next_change#		logNextSCN_obj,
	  next_time		logNextTime_obj
  from   rc_backup_redolog br
  where br.db_key = this_db_key
  and  (lbal2.thread# is null or br.thread# = lbal2.thread#)
  and  (br.sequence# between nvl(lbal2.lowseq, 0)
                     and     nvl(lbal2.highseq, MAXSEQVAL))
  and  (lowscn        is null or br.first_change#   >= lowscn)
  and  (highscn       is null or br.next_change#    <  highscn)
  and  (from_time     is null or br.completion_time >= from_time)
  and  (until_time    is null or br.completion_time <= until_time)
--and  (pattern       is null or br.name like pattern)
  and   br.status != 'D'
  order by br.bs_key, br.thread#, br.sequence#;

CURSOR ldbi(
   db_name varchar2)
IS
   SELECT db_key, dbinc_key, name, dbid, current_incarnation,
          resetlogs_change#, resetlogs_time
     FROM rc_database_incarnation
    WHERE (ldbi.db_name IS NULL OR
           name = upper(ldbi.db_name))
    ORDER BY dbid, resetlogs_change#;

CURSOR lrtbs(
   prev_ckp_key number
  ,next_ckp_key number)
IS
   SELECT ts.ts#, ts.ts_name
     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.create_scn < untilSCN or untilSCN is NULL)
      AND (ts.drop_scn > untilSCN or ts.drop_scn is NULL)
      AND tsatt.start_ckp_key <= prev_ckp_key
      AND (tsatt.end_ckp_key > prev_ckp_key or tsatt.end_ckp_key is NULL)
      AND tsatt.rbs_count > 0
    UNION
   SELECT ts.ts#, ts.ts_name
     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.create_scn < untilSCN or untilSCN is NULL)
      AND (ts.drop_scn > untilSCN or ts.drop_scn is NULL)
      AND tsatt.start_ckp_key <= next_ckp_key
      AND (tsatt.end_ckp_key > next_ckp_key or tsatt.end_ckp_key is NULL)
      AND tsatt.rbs_count > 0
    ORDER BY 1;

-------------------------------------------------------
-- listTranslateProxyDFRecid and translateBackupFile --
-------------------------------------------------------

rcvRec_last	rcvRec_t;			-- last record returned from:
						-- getRecoveryAction
						-- findControlFileBackup

-------------------------
-- getOfflineRangeCopy --
-------------------------

CURSOR getOfflineRangeCopy_c(
   offrRecid    number
  ,offrCkpSCN   number
  ,cfCreTime    date
  ,dbincKey     number)
RETURN rcvRec_t IS
   SELECT imageCopy_con_t	type_con,
          ccf_key		key_con,
          ccf_recid		recid_con,
 	  ccf_stamp		stamp_con,
	  null			setStamp_con,
	  null			setCount_con,
          null			bsRecid_con,
	  null 			bsStamp_con,
          null			bsKey_con,
          null			bsLevel_con,
          null                  bsType_con,
          null			pieceCount_con,
	  fname			fileName_con,
	  tag			tag_con,
          null			copyNumber_con,
	  status 		status_con,
	  null			blocks_con,	-- ccf doesn't have blocks
	  block_size		blockSize_con,
	  'DISK'		deviceType_con,
	  completion_time	compTime_con,
	  create_time		cfCreationTime_con,

          full_act_t		type_act,
	  0			fromSCN_act,
	  ccf.ckp_scn		toSCN_act,
	  ccf.ckp_time		toTime_act,
	  dbinc.reset_scn	rlgSCN_act,
	  dbinc.reset_time	rlgTime_act,
	  ccf.dbinc_key		dbincKey_act,
          null			level_act,

	  0			dfNumber_obj,
	  0			dfCreationSCN_obj,
	  null			logSequence_obj,
	  null			logThread_obj,
          null			logRlgSCN_obj,
	  to_date(null)		logRlgTime_obj,
	  null			logLowSCN_obj,
	  to_date(null)		logLowTime_obj,
	  null			logNextSCN_obj,
	  to_date(null)		logNextTime_obj
     FROM ccf, dbinc
    WHERE dbinc.dbinc_key = getOfflineRangeCopy_c.dbincKey
      AND dbinc.dbinc_key = ccf.dbinc_key
      AND getOfflineRangeCopy_c.cfCretime = create_time
      AND getOfflineRangeCopy_c.offrCkpSCN < ccf.ckp_scn
      AND getOfflineRangeCopy_c.offrRecid >= min_offr_recid
      AND status = 'A'
    ORDER BY stamp_con desc;

-- list all backups/copies/offline-ranges for all datafiles,
-- for REPORT OBSOLETE

cursor rddf is

  -- datafile copies

  select 2 preference,
         file#, COPY filetype, checkpoint_change#, checkpoint_time,
         resetlogs_change#, resetlogs_time, 0 incremental_change#,
         decode(decode(online_fuzzy,'NO',0,1)+decode(backup_fuzzy,'NO',0,1),
            0,greatest(nvl(absolute_fuzzy_change#,0),
                       nvl(recovery_fuzzy_change#,0)),
            maxscnval) fuzzy_change#,
            recid, stamp, name, 0 set_stamp, 0 set_count, cdf_key key,
            completion_time, 'DISK' device_type
  from rc_datafile_copy
  where db_key = this_db_key
  and   status != 'D'

  union all

  -- proxy datafile copies

  select 3,
         file#, PROXY, checkpoint_change#, checkpoint_time,
         resetlogs_change#, resetlogs_time, 0,
         decode(decode(online_fuzzy,'NO',0,1)+decode(backup_fuzzy,'NO',0,1),
            0,greatest(nvl(absolute_fuzzy_change#,0),
                       nvl(recovery_fuzzy_change#,0)),
            maxscnval),
            recid, stamp, handle, 0, 0, xdf_key, completion_time, device_type
  from rc_proxy_datafile
  where db_key = this_db_key
  and   status != 'D'

  union all

  -- datafiles in backup sets

  select decode(bs.backup_type, 'D', 4,
                                'I', 5), 
         file#,
         decode(bs.backup_type, 'D', FULL_DF_BACKUP,
                                'I', INCREMENTAL_DF_BACKUP),
         bd.checkpoint_change#, bd.checkpoint_time,
         bd.resetlogs_change#, bd.resetlogs_time, bd.incremental_change#,
         nvl(bd.absolute_fuzzy_change#,0),
         bs.recid, bs.stamp, null, bs.set_stamp, bs.set_count, bs.bs_key,
         bs.completion_time, null
  from rc_backup_datafile bd, rc_backup_set bs
  where bd.db_key = this_db_key
  and   bd.bs_key = bs.bs_key
  and   bd.status != 'D'

  union all

  -- controlfile copies

  select 2,
         0, COPY, checkpoint_change#, checkpoint_time,
         resetlogs_change#, resetlogs_time, 0, 0, recid, stamp, name, 0, 0,
         ccf_key, completion_time, 'DISK'
  from rc_controlfile_copy
  where db_key = this_db_key
  and   status != 'D'

  union all

  -- proxy controlfile copies

  select 3,
         0, PROXY, checkpoint_change#, checkpoint_time,
         resetlogs_change#, resetlogs_time, 0, 0, recid, stamp, handle, 0, 0,
         xcf_key, completion_time, device_type
  from rc_proxy_controlfile
  where db_key = this_db_key
  and   status != 'D'

  union all

  -- controlfiles in backup sets

  select 4,
         0, FULL_DF_BACKUP,
         checkpoint_change#, checkpoint_time,
         resetlogs_change#, resetlogs_time, 0, 0, bs.recid, bs.stamp, null,
         bs.set_stamp, bs.set_count, bs.bs_key, bs.completion_time, null
  from rc_backup_controlfile bc, rc_backup_set bs
  where bc.db_key = this_db_key
  and   bc.bs_key = bs.bs_key
  and   bc.status != 'D'

  union all

  -- offline ranges

  select 1,
         file#, OFFLINE_RANGE, online_change#, online_time,
         resetlogs_change#, resetlogs_time, offline_change#, 0,
         recid, stamp, null, 0, 0, 0, online_time, null
  from rc_offline_range ofr, rc_database_incarnation di
  where ofr.db_key = this_db_key
  and   di.db_key = this_db_key
  and   ofr.dbinc_key = di.dbinc_key

  -- The algorithm in krmkcofl (Construct Obsolete File List) depends on the
  -- following ordering.

  order by 2 asc,  -- file#
           4 desc, -- checkpoint_change#
           1 asc,  -- preference
          15 desc; -- completion_time, to break ties if all else is equal

------------------------------------------------
-- *** PRIVATE FUNCTION/PROCEDURE SECTION *** --
------------------------------------------------

---------------
-- Debugging --
---------------

------------------------------------- deb -------------------------------------
PROCEDURE deb(
   line IN varchar2)
IS
BEGIN
   IF (debug) THEN
      dbms_output.put_line(line);
   END IF;
END;

--------------------------
-- Database Translation --
--------------------------

-- Private procedure to check if a tablespace should be skipped.

-------------------------------- skipTableSpace -------------------------------
FUNCTION skipTableSpace(
   tsName IN varchar2)
RETURN boolean IS
BEGIN
   FOR i in 1..skipTablespaceCount LOOP
      IF (tsName = skipTablespaceList(i)) THEN
         RETURN TRUE;
      END IF;
   END LOOP;
   RETURN FALSE;
END;

------------------------------
-- Package State Validation --
------------------------------

-------------------------------- validateState --------------------------------
PROCEDURE validateState(
   anyCursor IN varchar2)
IS
BEGIN
   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;
   IF (anyCursor IS NOT NULL) THEN
      raise_application_error(-20203, 'Translation already started');
   END IF;
END;

---------------------
-- Query Filtering --
---------------------

-- Private procedure to find the highest SCN that is associated with a
-- timestamp less than or equal to timeStamp.  This will give us an
-- approximate untilSCN which we can use in the WHERE clause of the queries.
--
-- Scanning of v$datafile is done so that the name translation cursors
-- can rely on just the scnVar in their where clause.
--
-- Offline ranges are scanned so that ones whose online checkpoint time
-- is at or after the from time are skipped.

------------------------------ computeUntilSCN --------------------------------
PROCEDURE computeUntilSCN(
   timeStamp IN date
  ,scn       OUT number)
IS
   mySCN     	number;
BEGIN
   SELECT nvl(max(first_change#),0)
     INTO mySCN
     FROM rc_log_history
    WHERE dbinc_key = this_dbinc_key
      AND first_time <= timeStamp;

   SELECT greatest(nvl(max(low_scn), 0), mySCN)
     INTO mySCN
     FROM al
    WHERE dbinc_key = this_dbinc_key
      AND low_time <= timeStamp;

   SELECT greatest(nvl(max(checkpoint_change#),0), mySCN)
     INTO mySCN
     FROM rc_backup_datafile
    WHERE dbinc_key = this_dbinc_key
      AND checkpoint_time <= timeStamp;

   SELECT greatest(nvl(max(absolute_fuzzy_change#),0), mySCN)
     INTO mySCN
     FROM rc_backup_datafile
    WHERE dbinc_key = this_dbinc_key
      AND completion_time <= timeStamp;

   SELECT greatest(nvl(max(checkpoint_change#),0), mySCN)
     INTO mySCN
     FROM rc_backup_controlfile
    WHERE dbinc_key = this_dbinc_key
      AND checkpoint_time <= timeStamp;

   SELECT greatest(nvl(max(checkpoint_change#),0), mySCN)
     INTO mySCN
     FROM rc_datafile_copy
    WHERE dbinc_key = this_dbinc_key
      AND checkpoint_time <= timeStamp;

   SELECT greatest(nvl(max(absolute_fuzzy_change#),0), mySCN)
     INTO mySCN
     FROM rc_datafile_copy
    WHERE dbinc_key = this_dbinc_key
      AND completion_time <= timeStamp;

   SELECT greatest(nvl(max(recovery_fuzzy_change#),0), mySCN)
     INTO mySCN
     FROM rc_datafile_copy
    WHERE dbinc_key = this_dbinc_key
      AND recovery_fuzzy_time <= timeStamp;

   SELECT greatest(nvl(max(checkpoint_change#),0), mySCN)
     INTO mySCN
     FROM rc_controlfile_copy
    WHERE dbinc_key = this_dbinc_key
      AND checkpoint_time <= timeStamp;

   SELECT greatest(nvl(max(create_scn), 0), mySCN)
     INTO mySCN
     FROM df
    WHERE dbinc_key = this_dbinc_key
      AND create_time <= timeStamp;

   SELECT greatest(nvl(max(stop_scn), 0), mySCN)
     INTO mySCN
     FROM df
    WHERE dbinc_key = this_dbinc_key
      AND stop_time <= timeStamp;

   SELECT greatest(nvl(max(online_scn), 0), mySCN)
     INTO mySCN
     FROM offr
    WHERE dbinc_key = this_dbinc_key
      AND online_time <= timeStamp;

   scn := mySCN;
END computeUntilSCN;

---------------------------- isDeviceTypeAllocated ----------------------------
FUNCTION isDeviceTypeAllocated(
   deviceType IN varchar2)
RETURN boolean IS
BEGIN
   IF (anyDevice) THEN
      RETURN TRUE;
   END IF;

   FOR i IN 1..deviceCount LOOP
      IF deviceType = deviceList(i) THEN
         RETURN TRUE;
      END IF;
   END LOOP;

   RETURN FALSE;
END isDeviceTypeAllocated;

----------------------------
-- Backup Set Translation --
----------------------------

-- Lookup a backup set by primary key or recid/stamp

-------------------------------- findBackupSet --------------------------------
PROCEDURE findBackupSet(
   bsKey	IN     number DEFAULT NULL
  ,recid	IN     number DEFAULT NULL
  ,stamp	IN     number DEFAULT NULL
  ,bsRec	OUT    bsRec_t)
IS
BEGIN
   IF (bsKey IS NOT NULL) THEN
      SELECT bs_recid,
             bs_stamp,
             bs_key,
             set_stamp,
             set_count,
             bck_type,
             incr_level,
             completion_time,
             status,
             pieces
        INTO bsRec
        FROM bs
       WHERE db_key = this_db_key 
         AND findBackupSet.bsKey = bs_key;
   ELSE
      SELECT bs_recid,
             bs_stamp,
             bs_key,
             set_stamp,
             set_count,
             bck_type,
             incr_level,
             completion_time,
             status,
             pieces
        INTO bsRec
        FROM bs
       WHERE db_key = this_db_key 
         AND findBackupSet.recid = bs_recid
         AND findBackupSet.stamp = bs_stamp;
   END IF;

EXCEPTION
   WHEN no_data_found THEN
      raise_application_error(-20215, 'Backup set is missing');
END findBackupSet;

---------------------------
-- Backup Set Validation --
---------------------------

-- Validate a backup set has all pieces available according the the specified
-- arguments.  Returns only 1 validBackupSetRec.  There may be other sets of
-- pieces that match the criteria.

------------------------------ validateBackupSet ------------------------------
FUNCTION validateBackupSet0(
   tag			   IN     varchar2 DEFAULT NULL
  ,tagMatchRequired        IN     boolean  DEFAULT TRUE
  ,checkDeviceIsAllocated  IN     boolean  DEFAULT TRUE
  ,validRec                OUT    validBackupSetRec_t)
RETURN binary_integer IS
   local	validBackupSetRec_t;
   rc           binary_integer;
   gotRecord	number;
BEGIN
   <<validationLoop>>
   LOOP
      <<nextRow>>
      gotRecord := getValidBackupSet(validBackupSetRec      => local,
                                     checkDeviceIsAllocated => FALSE#);

      EXIT WHEN gotRecord = FALSE#;		-- cursor is closed already

      IF (checkDeviceIsAllocated) THEN
         IF (NOT isDeviceTypeAllocated(local.deviceType)) THEN
            deb('validateBackupSet0: device is not allocated');
            -- The required deviceType is not allocated.  Remember that
            -- we found an available backup set though.
            IF (rc IS NULL OR rc <> SUCCESS) THEN
               deb('validateBackupSet0: set rc to available');
               rc := dbms_rcvman.AVAILABLE;
            END IF;
            GOTO nextRow;
         END IF;
      END IF;

      validRec := local;			-- set OUT mode arg

      IF (tag IS NOT NULL AND NOT tagMatchRequired) THEN
         -- We are looking for backup sets with a particular tag, but
         -- we want to know about a backup set even if its tag does not match.
         -- In this case, the findValidBackupSet_c cursor did not do the
         -- tag checking for us, so we need to do it here.
         IF (tag = local.tag) THEN
            -- We've found a match for the tag, so we are done.  This
            -- backupset meets all of the validation criteria.  There may be
            -- another set of pieces with a different copy# or a different
            -- deviceType that also meet the criteria.  If the caller wants to
            -- find them, he will have to use the find/getValidBackupSet
            -- procedures to get the entire list, or call this routine
            -- again.
            deb('validateBackupSet0: tag matches, exiting loop');
            rc := SUCCESS;
            EXIT validationLoop;
         ELSE
            -- Even though the tag does not match, we want to remember
            -- about this set of pieces.  Keep looking for a match for our
            -- tag though.
            deb('validateBackupSet0: tag does not match, continuing search');
            rc := SUCCESS;
         END IF;
      ELSE
         -- Here, we know that the backupset is valid.  There may be multiple
         -- valid copies available, but we don't care.  The fact that one copy
         -- is available is good enough for us.
         deb('validateBackupSet0: set is valid, exiting loop');
         rc := SUCCESS;
         EXIT validationLoop;
      END IF;
   END LOOP;

   IF (rc IS NULL) THEN
      deb('validateBackupSet0: rc is null, setting to unavailable');
      rc := dbms_rcvman.UNAVAILABLE;
   END IF;

   deb('validateBackupSet0 returning: ' || to_char(rc));

   RETURN rc;

END validateBackupSet0;

FUNCTION validateBackupSet(
   backupSetRec		   IN     rcvRec_t
  ,tag			   IN     varchar2 DEFAULT NULL
  ,tagMatchRequired        IN     boolean  DEFAULT TRUE
  ,checkDeviceIsAllocated  IN     boolean  DEFAULT TRUE
  ,available               IN     number   DEFAULT TRUE#
  ,unavailable             IN     number   DEFAULT FALSE#
  ,deleted                 IN     number   DEFAULT FALSE#
  ,expired                 IN     number   DEFAULT FALSE#
  ,validRec                OUT    validBackupSetRec_t)
RETURN binary_integer IS
   findTag	bp.tag%TYPE;
BEGIN
   IF (tagMatchRequired) THEN
      findTag := tag;
   ELSE
      -- Caller wants to know about backup sets even if they don't match
      -- the tag he is looking for, so don't do any tag filtering in the
      -- cursor.
      findTag := NULL;
   END IF;

   -- Open cursor
   findValidBackupSet(backupSetRec => backupSetRec,
                      tag          => findTag,
                      available    => available,
                      unavailable  => unavailable,
                      deleted      => deleted,
                      expired      => expired);

   RETURN validateBackupSet0(
      tag                    => tag,
      tagMatchRequired       => tagMatchRequired,
      checkDeviceIsAllocated => checkDeviceIsAllocated,
      validRec               => validRec);
END validateBackupSet;

------------------------------
-- Backup Piece Translation --
------------------------------

------------------------------- findBackupPiece -------------------------------
PROCEDURE findBackupPiece(
   bpKey 	IN     number   DEFAULT NULL
  ,bsKey        IN     number   DEFAULT NULL
  ,tag          IN     varchar2 DEFAULT NULL
  ,handle	IN     varchar2 DEFAULT NULL
  ,deviceType   IN     varchar2 DEFAULT NULL
  ,copyNumber   IN     number   DEFAULT NULL
  ,available 	IN     number   DEFAULT TRUE#
  ,unavailable 	IN     number   DEFAULT FALSE#
  ,deleted 	IN     number   DEFAULT FALSE#
  ,expired 	IN     number   DEFAULT FALSE#)
IS
BEGIN
   validateState(getBackupPieceCursor);

   IF (bpKey IS NOT NULL) THEN
      OPEN findBackupPieceBpKey(bpKey       => bpKey,
                                tag         => tag,
                                handle      => handle,
                                deviceType  => deviceType,
                                copyNumber  => copyNumber,
                                available   => available,
                                unavailable => unavailable,
                                deleted     => deleted,
                                expired     => expired);
      getBackupPieceCursor := 'findBackupPieceBpKey';
   ELSIF (bsKey IS NOT NULL) THEN
      OPEN findBackupPieceBsKey(bsKey       => bsKey,
                                tag         => tag,
                                handle      => handle,
                                deviceType  => deviceType,
                                copyNumber  => copyNumber,
                                available   => available,
                                unavailable => unavailable,
                                deleted     => deleted,
                                expired     => expired);
      getBackupPieceCursor := 'findBackupPieceBsKey';
   ELSE
      OPEN findBackupPiece_c(   tag         => tag,
                                handle      => handle,
                                deviceType  => deviceType,
                                copyNumber  => copyNumber,
                                available   => available,
                                unavailable => unavailable,
                                deleted     => deleted,
                                expired     => expired);
      getBackupPieceCursor := 'findBackupPiece_c';
   END IF;

   -- Initialize all of the getBackupPiece variables to their default
   -- state.

   getBackupPieceNoRows.error	  := NULL;
   getBackupPieceDuplicates	  := TRUE#;
   getBackupPieceLast.pieceNumber := NULL;
   getBackupPieceDeviceType	  := deviceType;
   getBackupPieceExpectedPieces   := NULL;
   getBackupPiecePieceCount       := 0;
   getBackupPieceByHandle         := FALSE;
END findBackupPiece;

------------------------------------------
-- Compute Recovery Actions Subroutines --
------------------------------------------

--------------------------------- rcvRecPush ----------------------------------
PROCEDURE rcvRecPush(
   rcvRec IN rcvRec_t)
IS
BEGIN
   rcvRecStack.extend;
   rcvRecStack(rcvRecStack.last) := rcvRec;
END rcvRecPush;

---------------------------------- rcvRecGet ----------------------------------
PROCEDURE rcvRecGet(
   indx   IN  number
  ,rcvRec OUT rcvRec_t)
IS
BEGIN
   rcvRec := rcvRecStack(indx);
END rcvRecGet;

---------------------------------- rcvRecTop ----------------------------------
PROCEDURE rcvRecTop(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   IF (rcvRecStack.count = 0) THEN
      rcvRec := NULL;
   ELSE
      rcvRecGet(rcvRecStack.count, rcvRec);
   END IF;
END rcvRecTop;

---------------------------------- rcvRecPop ----------------------------------
PROCEDURE rcvRecPop(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   rcvRecTop(rcvRec);
   rcvRecStack.trim;
END rcvRecPop;

-------------------------------- rcvRecConvert --------------------------------
PROCEDURE rcvRecConvert(
   rcvRec IN OUT rcvRec_t)
IS
BEGIN
  -- Get rid of any nulls that would be unacceptable to the pre-8.2
  -- RMAN.  The pre-8.2 RMAN did not use null-indicators consistantly.

  rcvRec.recid_con      := nvl(rcvRec.recid_con, 0);
  rcvRec.stamp_con	:= nvl(rcvRec.stamp_con, 0);
  rcvRec.setStamp_con   := nvl(rcvRec.setStamp_con, 0);
  rcvRec.setCount_con   := nvl(rcvRec.setCount_con, 0);
  rcvRec.fileName_con   := nvl(rcvRec.fileName_con, 'NULL');
  rcvRec.blockSize_con  := nvl(rcvRec.blockSize_con, 0);
  rcvRec.blocks_con     := nvl(rcvRec.blocks_con, 0);
  rcvRec.deviceType_con := nvl(rcvRec.deviceType_con, 'NULL');
END rcvRecConvert;

--------------------------------- printRcvRec ---------------------------------
PROCEDURE printRcvRec(
   action IN rcvRec_t)
IS
   l              varchar2(600);
   cfcretime      varchar2(100);
   action_deleted boolean;
BEGIN

   ------------------------
   -- Recovery Container --
   ------------------------

   -- Print the containter type
   IF (action.type_con = backupSet_con_t) THEN
      IF (action.type_act = full_act_t) THEN
         deb('Full Backup Set');
      ELSE
         deb('Incremental Backup Set');
      END IF;
   ELSIF (action.type_con = proxyCopy_con_t) THEN
      deb('Proxy Backup');
   ELSIF (action.type_con = imageCopy_con_t) THEN
      deb('Datafile Copy');
   ELSIF (action.type_con = offlineRangeRec_con_t) THEN
      IF (action.type_act = offlineRange_act_t) THEN
         deb('Offline Range Record');
      ELSIF (action.type_act = cleanRange_act_t) THEN
         deb('Clean Range');
      ELSIF (action.type_act = implicitRange_act_t) THEN
         deb('Implicit Offline Range');
      ELSIF (action.type_act = spanningRange_act_t) THEN
         deb('Spanning Offline Range');
      ELSE
         deb('Unknown Offline Range Action Type');
      END IF;
   ELSE
      deb('Uknown recovery container type');
   END IF;

   -- Print the type-specific containter data
   IF (action.type_con = backupSet_con_t) THEN
      deb('   bsKey=' || to_char(action.bsKey_con) ||
          '  bsRecid=' || to_char(action.bsRecid_con) ||
          '  bsStamp=' || to_char(action.bsStamp_con) ||
          '  setStamp=' || to_char(action.setStamp_con) ||
          '  setCount=' || to_char(action.setCount_con));
      deb('   bsLevel=' || to_char(action.bsLevel_con) ||
          '  bsType=' || action.bsType_con ||
          '  pieceCount=' || to_char(action.pieceCount_con));
   ELSIF (action.type_con = proxyCopy_con_t OR
          action.type_con = imageCopy_con_t) THEN
      deb('   fileName=' || action.fileName_con);
   END IF;

   -- Print the remaining container data
   l := ' ';
   
   -- key_con
   IF (action.key_con is not null) THEN
      l := l || '  key=' || to_char(action.key_con);
   END IF;

   -- recid_con, stamp_con
   IF (action.recid_con is not null) THEN
      l := l || '  recid=' || to_char(action.recid_con) ||
                '  stamp=' || to_char(action.stamp_con);
   END IF; 
   
   -- status_con
   IF (action.status_con is not null) THEN
      l := l || '  status=' || action.status_con;
   END IF;

   IF (length(l) > 2) THEN
      deb(l);
   END IF;

   l := ' ';

   -- tag
   IF (action.tag_con is not null) THEN
      l := l || '  tag=' || action.tag_con;
   END IF;

   -- compTime
   IF (action.compTime_con is not null) THEN
      l := l || '  compTime=' || to_char(action.compTime_con);
   END IF;

   IF (length(l) > 2) THEN
      deb(l);
   END IF;

   l := ' ';

   IF (action.deviceType_con is not null) THEN
      l := l || '  deviceType=' || action.deviceType_con;
   END IF;

   IF (action.blocks_con is not null) THEN
      l := l || '  blocks=' || to_char(action.blocks_con) ||
           '  blockSize=' || to_char(action.blockSize_con);
   END IF;

   IF (action.cfCreationTime_con is not null) THEN
      l := l || '  cfCreationTime=' || to_char(action.cfCreationTime_con);
   END IF;

   IF (length(l) > 2) THEN
      deb(l);
   END IF;

   l := ' ';

   ---------------------
   -- Recovery Action --
   ---------------------

   -- fromSCN
   IF (action.fromSCN_act is not null) THEN
      l := l || '  fromSCN=' || to_char(action.fromSCN_act);
   END IF;

   -- toSCN toTime
   IF (action.toSCN_act is not null) THEN
      l := l || '  toSCN=' || to_char(action.toSCN_act) ||
           '  toTime=' || to_char(action.toTime_act);
   END IF;

   -- level
   IF (action.level_act is not null) THEN
      l := l || '  level=' || to_char(action.level_act);
   END IF;

   IF (length(l) > 2) THEN
      deb(l);
   END IF;

   l := ' ';

   IF (action.rlgSCN_act is not null) THEN
      l := l || '  rlgSCN=' || to_char(action.rlgSCN_act) ||
           '  rlgTime=' || to_char(action.rlgTime_act) ||
           '  dbincKey=' || to_char(action.dbincKey_act);
   END IF;

   IF (length(l) > 2) THEN
      deb(l);
   END IF;

   l := ' ';

   ---------------------
   -- Recovery Object --
   ---------------------

   IF (action.dfNumber_obj IS NOT NULL) THEN
      l := l || '  dfNumber=' || to_char(action.dfNumber_obj) ||
           '  creationSCN=' || to_char(action.dfCreationSCN_obj);
      deb(l);
   ELSIF (action.logSequence_obj IS NOT NULL) THEN
      l := l || '  logSequence=' || to_char(action.logSequence_obj);
      deb(l);
      l := ' ';
      l := l || '  logThread=' || to_char(action.logThread_obj);
      deb(l);
      l := ' ';
      l := l || '  logLowSCN=' || to_char(action.logLowSCN_obj);
      deb(l);
      l := ' ';
      l := l || '  logLowTime=' || to_char(action.logLowTime_obj);
      deb(l);
      l := ' ';
      l := l || '  logNextSCN=' || nvl(to_char(action.logNextSCN_obj), 'NULL');
      deb(l);
      l := ' ';
      l := l || '  logNextTime=' || to_char(action.logNextTime_obj);
      deb(l);
      l := ' ';
      l := l || '  logRlgSCN=' || to_char(action.logRlgSCN_obj);
      deb(l);
      l := ' ';
      l := l || '  logRlgTime=' || to_char(action.logRlgTime_obj);
      deb(l);
   ELSE
      deb('  Unknown Recovery Object');
   END IF;
EXCEPTION
   WHEN OTHERS THEN
   deb('printRcvRec: caught an exception, aborting print');
   RETURN;
END printRcvRec;

-- See if redo is needed to fill a gap between this action and the one
-- that follows it, which is already on the rcvRecStack and is pointed to
-- by rcvRecStackState.lowAction

---------------------------------- redoNeeded ---------------------------------
FUNCTION redoNeeded(
   action IN rcvRec_t)
RETURN boolean IS
BEGIN
   IF (rcvRecStackState.lowAction > 0 AND   -- Have a non-full_act_t on stack?
       action.toSCN_act <
       rcvRecStack(rcvRecStackState.lowAction).fromSCN_act) THEN
      RETURN TRUE;
   ELSE
      RETURN FALSE;
   END IF;
END redoNeeded;

---------------------------------- canAddRedo ---------------------------------
FUNCTION canAddRedo(
   isAncestor  IN boolean
  ,from_scn    IN number
  ,from_rlgscn IN number
  ,to_action   IN rcvRec_t
  ,partial_rcv IN boolean)
RETURN number IS
BEGIN
   IF (from_rlgscn = this_reset_scn) THEN
      IF (partial_rcv) THEN
         deb('canAddRedo: returning action_OK');
         RETURN action_OK;
      ELSE
         -- A partial media recovery can only be done if we have a
         -- current controlfile.  A partial recovery does not
         -- recover the controlfile.  This could be implemented,
         -- but it requires using an enqueue to ensure only
         -- 1 process tries to recover the confile.
         -- Since we aren't recovering the controlfile,
         -- the file header won't be handled properly when
         -- we hit controlfile redo.  That is why we are
         -- requiring a current controlfile.  Since we don't have
         -- one, we will have to do database recovery on this datafile.
         -- It is possible that we have a current controlfile here,
         -- but RMAN currently does not support partial media recovery.
         deb('canAddRedo: returning action_FAIL');
         RETURN action_FAIL;
      END IF;
   ELSE
      deb('canAddRedo: from_rlgscn=' || nvl(to_char(from_rlgscn), 'NULL') ||
          ' this_reset_scn=' || to_char(this_reset_scn));
      IF (isAncestor) THEN
         deb('canAddRedo: isAncestor is TRUE;');
         -- RMAN cannot yet apply redo from the non-current incarnation.
         -- Someday this will be implemented.  The idea is that RMAN will have
         -- the DB mount a backup controlfile from the incarnation containing
         -- the logs we need to apply, and then recovery should just work
         -- normally since the logs, controlfile, and datafile(s) will all be
         -- from the same incarnation.  We will be able to apply redo from a
         -- non-current incarnation only if isAncestor is TRUE.  We don't want
         -- to apply redo from an incarnation that we do not know with
         -- certainty is one of our ancestors.  Otherwise, we could be
         -- applying redo to reach the start of an offline range spanning
         -- resetlogs, which could certainly be the wrong thing to do.  An
         -- offline range should contain the reset SCN and timestamp of the
         -- incarnation in which the offline range started, but unfortunately
         -- it does not.
         deb('canAddRedo: returning action_OLD_REDO');
         RETURN action_OLD_REDO;
      ELSE
         deb('canAddRedo: isAncestor is FALSE;');
         -- We should never attempt to apply redo from an incartion that
         -- we don't know with certainty is one of our ancestors.
         deb('canAddRedo: returning action_OLD_REDO');
         RETURN action_OLD_REDO;
      END IF;
   END IF;
END canAddRedo;

----------------------------------- addRedo -----------------------------------
FUNCTION addRedo(
   isAncestor  IN boolean
  ,from_scn    IN number
  ,from_rlgscn IN number
  ,to_action   IN rcvRec_t
  ,partial_rcv IN boolean)
RETURN number IS
   canAdd 	number;
   redoRec	rcvRec_t;
BEGIN
   canAdd := canAddRedo(isAncestor, from_scn, from_rlgscn,
                        to_action, partial_rcv);
   IF (canAdd = action_FAIL) THEN
      -- Trim to the last save point.  If we are doing RESTORE or RECOVER,
      -- then this will discard all actions we have stacked so far.
      -- If we are doing LIST, this will discard all actions up to
      -- the last full/level 0/copy/proxy-copy that we found.  We never
      -- need to trim actions that come after the save point since we
      -- know they can be applied.

      deb('addRedo: trimming to save_point');

      -- Since we have a gap in the redo stream here, reset lowAction
      -- to 0.  No actions that may be coming after this one will cover
      -- this gap since their to_scn's must be <= this action's.
      -- It is possible that the lowAction is about to be trimmed, but
      -- even if it remains on the stack, we have a broken chain and
      -- need to start a new one.
      rcvRecStackState.lowAction := 0;

      rcvRecStack.trim(rcvRecStack.last - 
                       greatest(rcvRecStackState.savePoint,
                                rcvRecStackState.top));

      RETURN action_FAIL;
   ELSIF (canAdd = action_OK) THEN
      redoRec.type_act		:= redo_act_t;
      redoRec.fromSCN_act 	:= from_scn;
      redoRec.toSCN_act   	:= to_action.fromSCN_act;
      redoRec.toTime_act	:= to_date(null);
      redoRec.rlgSCN_act   	:= from_rlgscn;
      rcvRecPush(redoRec);
      -- ### Some other fields should be filled in, but this code path never
      -- taken currently, so doesn't matter yet.

      deb('addRedo: from_scn=' || to_char(from_scn) ||
          ' to_scn=' || to_char(to_action.fromSCN_act));

      RETURN action_OK;
   ELSE                                         -- ancestral incarnation
      -- Application of redo from an ancestral incarnation is not supported.
      -- However, we cannot trim these actions as we did in the case above
      -- because they are required to reach the offline-range that spans
      -- the resetlogs.  So we simply return an error.
      RETURN action_OLD_REDO;
   END IF;
END addRedo;

---------------------------------- addAction ----------------------------------
FUNCTION addAction(                             -- add to the rcvRecStack
   actionIN    IN     rcvRec_t			-- if a backup set, we fill
						-- in the tag and deviceType
  ,partial_rcv IN     boolean
  ,isAncestor  IN     boolean
  ,cf_scn      IN     number DEFAULT NULL
  ,cf_cretime  IN     date   DEFAULT NULL
  ,cf_offrrid  IN     number DEFAULT NULL
  ,allCopies   IN     boolean DEFAULT FALSE)
RETURN number IS
   action               rcvRec_t;
   validate_rc		number;
   cf_count         	number;
   addRedo_rc       	number;
   tagMatchRequired 	boolean;
   validationRec    	validBackupSetRec_t;
   lowAction		rcvRec_t;
BEGIN
   action := actionIN;			-- copy to local variable

   -- See if we have a gap that must be filled by redo.  We must make this
   -- check first because we want to return OLD_REDO if that condition
   -- applies.  We process actions in descending to_scn order, so it is OK to
   -- add a redo action to the stack even if we cannot apply this action
   -- because it fails validation or we don't have the right device type
   -- allocated.  The next action we see would require at least this much
   -- redo.  Note that this means we may have 2 adjacent redo actions on the
   -- stack.  getRecoveryAction will merge them.

   IF (redoNeeded(action)) THEN
      -- We need to do a partial media recovery from this to_scn to
      -- get all the way to the lowActions's from_scn.

      -- N.B:
      -- A clean range always belongs to the current incarnation since it
      -- comes from the controlfile and the controlfile defines the current
      -- incarnation.  Therefore, if this action is a clean range then we
      -- must be searching the current incarnation.  It is not possible for
      -- the recoveryActionCursor to return a clean range when searching
      -- recursively, even if we didn't know our parent incarnation.  If
      -- the clean range does not span the resetlogs SCN, then its from_scn
      -- will be greater than the target_scn for any recursive search, and
      -- the cursor will filter out the clean range.  If the clean range
      -- does span the resetlogs SCN, then the clean range's from_scn
      -- becomes the target SCN for the recursive searches, and the cursor
      -- will also filter it out.  Therefore, we know that the lowAction
      -- must be from the current incarnation.  So we can simply add a redo
      -- action to fill the gap between the clean range and the lowAction.
      rcvRecGet(rcvRecStackState.lowAction, lowAction);
      addRedo_rc := addRedo(isAncestor,
                            action.toSCN_act, action.rlgSCN_act,
                            lowAction,
                            partial_rcv);

      IF (addRedo_rc = action_OLD_REDO) THEN
         -- We cannot apply this action because there is an offline range
         -- in the future of this action that we must apply, and it would
         -- require redo to reach the offline range.
         RETURN addRedo_rc;
      ELSE
         -- The other possible return codes from addRedo are
         -- OK and FAIL.  In either case, we are allowed to add this
         -- action to the stack.  The FAIL case is when partial_rcv
         -- is false, so addRedo we just discards the actions previously
         -- stacked up to the save point, if any.
         NULL;
      END IF;
   ELSE					-- redo not needed
      -- If the lowAction is an offline range, then this action must
      -- have a to_scn <= the offline range start SCN.
      -- We don't actually need to check this though.  The cursor looks at
      -- only those actions with a to_scn <= the target_scn.
      NULL;
   END IF;

   -- If this is a backup set, validate that it is available and we have the
   -- right device type allocated to restore/list it, and also check that it
   -- is available on a set of pieces with the right tag if a restoreTag has
   -- been specified.

   IF (action.type_con = backupSet_con_t) THEN
      IF (computeRA_allRecords = TRUE# OR restoreTag IS NULL) THEN
         tagMatchRequired := FALSE;
      ELSE
         tagMatchRequired := TRUE;
      END IF;

      -- Validate this backup set.  This checks to see that a complete set of
      -- pieces are available on the same device type.  We also discover
      -- if the pieces are all available with the same tag, and if so, from
      -- the same copy#.  These values are then stuffed into the rcvRec.
      -- It is possible that a complete set of pieces is available on multiple
      -- device types if the backupset has been copied.  In this case, the
      -- other device types are ignored.  The ordering is simply the collating
      -- sequence order for the deviceType (alphabetic).  Since "DISK"
      -- comes before "SBT_TAPE", this has the effect of prefering backupsets
      -- on disk over those on tape, which is desired.  It would be a better
      -- implementation to have a preference column in the bp table, but
      -- that is not really needed until we have something other than
      -- SBT_TAPE and DISK.
      --
      -- If we are really performing a restore, then the underlying query
      -- (findValidBackupSet) will be run twice, once from here and then again
      -- later when RMAN obtains the backup pieces for this set.  This is
      -- unfortunate, but unavoidable.  This is the reason why the copy#
      -- tag and deviceType are stuffed into the rcvRec, so that the later
      -- call to get the pieces can use those values in the query, which may
      -- improve its performance.  Also note that if this backupset is used
      -- to restore multiple datafiles, then we validate it once for each
      -- datafile.  It may be a good idea to keep a list of backup set keys
      -- that have been validated to improve performance when many datafiles
      -- are being restored.
      --
      -- For RMAN commands such as LIST, the tag, deviceType, and copy#s
      -- will be ignored.  As of 8.2, those commands use the findValidBackupSet
      -- procedure directly to obtain complete information about the 
      -- availablity of the backup set.

      validate_rc :=
         validateBackupSet(backupSetRec           => action,
                           tag                    => restoreTag,
                           tagMatchRequired       => tagMatchRequired,
                           checkDeviceIsAllocated => TRUE,
                           available              => TRUE#,
                           unavailable            => FALSE#,
                           deleted                => FALSE#,
                           expired                => FALSE#,
                           validRec               => validationRec);

      IF (validate_rc = dbms_rcvman.UNAVAILABLE) THEN
         deb('addAction: backup set is unavailable, returning FAIL');
         RETURN action_FAIL;
      ELSIF (validate_rc = dbms_rcvman.AVAILABLE) THEN
         -- We cannot restore/list this backup set because we don't have the
         -- right device type allocated.  We want to remember this fact
         -- so that we can give the user a meaningful error message.
         computeRA_available := TRUE;
         deb('addAction: dont have required device type, returning FAIL');
         RETURN action_FAIL;
      ELSIF (validate_rc = SUCCESS) THEN
         -- IF computeRA_allRecords is true and restoreTag is not
         -- null, then we set tagMatchRequired to false, so it is possible
         -- that this backupset did not validate with the right tag.  Check
         -- that the tag in the validationRec matches the restoreTag.  If not,
         -- then leave the tag_con field in the action record null.  This will
         -- cause this action to be skipped when getRecoveryAction is called.
         IF (validationRec.tag = restoreTag OR
             restoreTag IS NULL) THEN
            action.tag_con := validationRec.tag;
         END IF;
         action.deviceType_con := validationRec.deviceType;
         action.copyNumber_con := validationRec.copyNumber;
         -- Since we only asked validateBackupSet for available backup sets,
         -- this one must have status 'A'.
         action.status_con := 'A';
      END IF;
   ELSIF (action.type_con = proxyCopy_con_t) THEN
      -- Check that we have the right deviceType allocated.
      IF (NOT isDeviceTypeAllocated(action.deviceType_con)) THEN
         -- We cannot restore/list this backup set because we don't have the
         -- right device type allocated.  We want to remember this fact
         -- so that we can give the user a meaningful error message.
         computeRA_available := TRUE;
         RETURN action_FAIL;
      END IF;
   ELSIF (action.type_con = imageCopy_con_t) THEN
      -- Check that we have a disk device allocated.
      IF (not diskDevice) THEN
         -- We don't have a disk device allocated, so we cannot
         -- use a datafile copy.
         computeRA_available := TRUE;
         RETURN action_FAIL;
      END IF;
   ELSIF (action.type_con = offlineRangeRec_con_t AND
          action.type_act = offlineRange_act_t) THEN
      -- First see if this offline range is in the current controlfile.
      -- If so, we are cool.  If not, then check that there exists at least
      -- one controlfile copy that contains this offline range.
      -- Note that if a controlfile contains no kccor records, then
      -- the oldest recid will be zero.  Both krmk.pc and krbc.c obey
      -- this convention.
      IF (cf_cretime = action.cfCreationTime_con AND
          action.recid_con >= cf_offrrid AND
          cf_offrrid > 0 and            -- contains at least 1 record
          cf_scn >= action.toSCN_act) THEN
         NULL;                          -- range is in current cf, we're cool
      ELSE
         SELECT count(*)
           INTO cf_count
           FROM ccf
          WHERE ccf.create_time = action.cfCreationTime_con AND
                ccf.min_offr_recid <= action.recid_con AND
                ccf.ckp_scn >= action.toSCN_act AND
                ccf.min_offr_recid > 0;	-- contains at least 1 record
         IF (cf_count = 0) THEN
           RETURN action_FAIL;
         END IF;
      END IF;
   END IF;

   <<addAnother>>

   rcvRecPush(action);                  -- add record for this action

   IF debug THEN
      deb('addAction: Added action:');
      printRcvRec(action);
   END IF;

   IF (allCopies AND action.type_con = backupSet_con_t) THEN
      -- Keep calling validateBackupSet0 until it fails to return SUCCESS.
      validate_rc :=
         validateBackupSet0(tag                    => restoreTag,
                            tagMatchRequired       => tagMatchRequired,
                            checkDeviceIsAllocated => TRUE,
                            validRec               => validationRec);
      IF (validate_rc <> SUCCESS) THEN
         GOTO done;
      END IF;

      IF (validationRec.tag = restoreTag OR
          restoreTag IS NULL) THEN
         action.tag_con := validationRec.tag;
      END IF;
      action.deviceType_con := validationRec.deviceType;
      action.copyNumber_con := validationRec.copyNumber;
      -- Since we only asked validateBackupSet for available backup sets,
      -- this one must have status 'A'.
      action.status_con := 'A';
      GOTO addAnother;
   END IF;

   <<done>>

   -- Update the lowAction pointer and the savePoint pointer if necessary.

   IF (action.type_act = full_act_t) THEN
      -- new full means new savePoint
      rcvRecStackState.savePoint := rcvRecStack.last;
   ELSIF (rcvRecStackState.lowAction = 0) THEN
      -- no curent lowAction, then set
      rcvRecStackState.lowAction := rcvRecStack.last;
   ELSIF (action.fromSCN_act <
          rcvRecStack(rcvRecStackState.lowAction).fromSCN_act) THEN
      rcvRecStackState.lowAction := rcvRecStack.last;   -- new lowAction
   END IF;

   RETURN action_OK;

END addAction;

----------------------------- fetchRecoveryAction -----------------------------
FUNCTION fetchRecoveryAction(
   cv		IN     rcvRecCursor_t
  ,df_ckpscn 	IN     number
  ,action       IN OUT rcvRec_t)
RETURN boolean IS
   top		rcvRec_t;
BEGIN

   <<retry>>

   FETCH cv
    INTO action;

   IF (cv%NOTFOUND) THEN
      action.type_con := NULL;
      action.type_act := NULL;
      return FALSE;
   END IF;

   IF (action.compTime_con IS NULL AND		-- was null in 8.0.2
       action.type_con = backupSet_con_t) THEN
      action.compTime_con := stamp2date(action.bsStamp_con);
   END IF;

   IF (computeRA_allRecords = TRUE#) THEN
      RETURN TRUE;
   END IF;

   -- Discard this action if its fromSCN is not less than the most recently
   -- stacked actions's fromSCN.  Also check that the resetlogs SCNs are the
   -- same.  If they differ, then keep this action.  This is necessary to
   -- avoid discarding a spanning offline range here.  The real offline range
   -- and the spanning range(s) have the same fromSCN.
   IF (rcvRecStack.count > 0) THEN
      rcvRecTop(top);
      IF (not (action.fromSCN_act < top.fromSCN_act) AND
          action.rlgSCN_act = top.rlgSCN_act) THEN
         deb('fetchRecoveryAction: discarding this action:');
         printRcvRec(action);
         GOTO retry;
      END IF;
   END IF;

   RETURN TRUE;

END fetchRecoveryAction;

--------------------------- openRecoveryActionCursor --------------------------
PROCEDURE openRecoveryActionCursor(
   rcvRecCursor  IN OUT rcvRecCursor_t
  ,dbincKey      IN     number
  ,fno           IN     number
  ,creSCN        IN     number
  ,dfCkpSCN      IN     number
  ,dbincRlgSCN   IN     number
  ,dbincRlgTime  IN     date
  ,offlSCN       IN     number
  ,onlSCN        IN     number
  ,onlTime       IN     date
  ,cleanSCN      IN     number
  ,clean2SCN     IN     number
  ,clean2Time    IN     date
  ,targetSCN     IN     number)
IS
BEGIN
   deb('openRecoveryActionCursor: target scn is ' ||
       nvl(to_char(targetSCN), 'NULL'));

   -- NOTES:
   --
   -- Procedure "setUntilTime" attempts to estimate the untilSCN by querying
   -- all the scn/timestamp pairs in the recovery catalog whose timestamp is
   -- strictly less than the until time.  The result is an estimated untilSCN
   -- that is very likely to be less than the actual incomplete recovery SCN
   -- when the RECOVER UNTIL TIME is done.  I.e. estimated untilSCN is
   -- conservative.  This calculation considers checkpoint SCN/timestamps for
   -- backup datafiles and datafile copies, as well as their absolute fuzzy
   -- SCNs/completion timestamps.  We treat the completion timestamp as a
   -- reasonable estimate of the absolute fuzzy time.
   -- Note that the absolute fuzzy SCN can be equal to the until SCN because
   -- it is higher than any SCN in the backup.  We also allow the checkpoint
   -- SCN and media recovery fuzzy SCN to be equal to the until SCN.

   OPEN rcvRecCursor FOR                    -- open the cursor

   -- Offline Ranges

   SELECT offlineRangeRec_con_t			type_con,
          offr_key				key_con,
          offr_recid				recid_con,
          offr_stamp               		stamp_con,
          to_number(null)			setStamp_con,
          to_number(null)			setCount_con,
          to_number(null)			bsRecid_con,
          to_number(null)			bsStamp_con,
          to_number(null)			bsKey_con,
          to_number(null)			bsLevel_con,
          to_char(null)                         bsType_con,
          to_number(null)			pieceCount_con,
          to_char(null)				fileName_con,
          to_char(null)				tag_con,
	  to_number(null)			copyNumber_con,
          to_char(null)				status_con,
          to_number(null)			blocks_con,
          to_number(null)			blockSize_con,
          to_char(null)				deviceType_con,
          to_date(null)				compTime_con,
          cf_create_time			cfCreationTime_con,

          offlineRange_act_t			type_act,
          offline_scn				fromSCN_act,
          online_scn				toSCN_act,
          online_time				toTime_act,
          openRecoveryActionCursor.dbincRlgSCN	rlgSCN_act,
          openRecoveryActionCursor.dbincRlgTime	rlgTime_act,
          dbinc_key				dbincKey_act,
          to_number(null)			level_act,

          openRecoveryActionCursor.fno		dfNumber_obj,
          openRecoveryActionCursor.creSCN	dfCreationSCN_obj,
          to_number(null)			logSequence_obj,
          to_number(null)			logThread_obj,
          to_number(null)			logRlgSCN_obj,
          to_date(null)				logRlgTime_obj,
          to_number(null)			logLowSCN_obj,
          to_date(null)				logLowTime_obj,
	  to_number(null)			logNextSCN_obj,
          to_date(null)				logNextTime_obj
     FROM offr
    WHERE dbinc_key  = openRecoveryActionCursor.dbincKey
      AND file#      = openRecoveryActionCursor.fno
      AND create_scn = openRecoveryActionCursor.creSCN
      AND (openRecoveryActionCursor.dfCkpSCN is null OR
           openRecoveryActionCursor.dfCkpSCN <= offline_scn)
      AND (openRecoveryActionCursor.targetSCN is null OR
           online_scn <= openRecoveryActionCursor.targetSCN)
      AND (untilSCN is null OR online_scn < untilSCN)
           				-- If online_scn = untilSCN, don't
                                        -- apply the offline range.  The
                                        -- dictionary txn that commits the
                                        -- online occurred at a higher scn,
                                        -- and recovery will probably stop
                                        -- before getting to commit redo.
      AND cf_create_time is not null    -- offr recs added before 8.0.3 useless
      AND offr_stamp <> 0               -- stamp = 0 -> from kccfe

   UNION ALL

   -- Datafile Copies

   SELECT imageCopy_con_t,			-- type_con
	  cdf_key,		  		-- key_con
	  cdf_recid,				-- recid_con
	  cdf_stamp, 				-- stamp_con
	  to_number(null),			-- setStamp_con
	  to_number(null),			-- setCount_con
	  to_number(null),			-- bsRecid_con
	  to_number(null),			-- bsStamp_con
	  to_number(null),			-- bsKey_con
	  to_number(null),			-- bsLevel_con
          to_char(null),                        -- bsType_con
          to_number(null),			-- pieceCount_con
	  fname,				-- fileName_con
	  tag,					-- tag_con
	  to_number(null),			-- copyNumber_con
	  status,				-- status_con
	  blocks,				-- blocks_con
	  block_size,				-- blockSize_con
	  'DISK',				-- deviceType_con
	  completion_time,			-- compTime_con
	  to_date(null),			-- cfCreationTime_con

	  full_act_t,	 			-- type_act
	  0,					-- fromSCN_act
	  ckp_scn,				-- toSCN_act
	  ckp_time,				-- toTime_act
          openRecoveryActionCursor.dbincRlgSCN, -- rlgSCN_act
          openRecoveryActionCursor.dbincRlgTime,-- rlgTime_act,
          dbinc_key,				-- dbincKey_act,
	  to_number(null),			-- level_act

	  openRecoveryActionCursor.fno,		-- dfNumber_obj
	  openRecoveryActionCursor.creSCN,	-- dfCreationSCN_obj
	  to_number(null),			-- logSequence_obj
	  to_number(null),			-- logThread_obj
	  to_number(null),			-- logRlgSCN_obj
	  to_date(null),			-- logRlgTime_obj
	  to_number(null),			-- logLowSCN_obj
	  to_date(null),			-- logLowTime_obj
	  to_number(null),			-- logNextSCN_obj
	  to_date(null)				-- logNextTime_obj
     FROM cdf
    WHERE dbinc_key  = openRecoveryActionCursor.dbincKey
      AND file#      = openRecoveryActionCursor.fno
      AND create_scn = openRecoveryActionCursor.crescn
      AND (openRecoveryActionCursor.dfCkpSCN is null OR
           openRecoveryActionCursor.dfCkpSCN < ckp_scn)
      AND (openRecoveryActionCursor.targetSCN is null OR
           ckp_scn <= openRecoveryActionCursor.targetSCN)
      AND (untilSCN is null OR
           greatest(ckp_scn, abs_fuzzy_scn, rcv_fuzzy_scn) <= untilSCN)
      AND status = 'A'
      AND (restoreTag is NULL OR
           tag = restoreTag OR
           computeRA_allRecords = TRUE#)
      AND (restoreSource is NULL OR restoreSource = COPY)

   UNION ALL

   -- Backup Sets

   SELECT backupSet_con_t,			-- type_con
	  bdf_key,				-- key_con
	  recid,				-- recid_con
	  stamp,				-- stamp_con
	  set_stamp,				-- setStamp_con
	  set_count,				-- setCount_con
	  bs_recid,				-- bsRecid_con
	  bs_stamp,				-- bsStamp_con
	  bs_key,				-- bsKey_con
	  bs_level,				-- bsLevel_con
          backup_type,                          -- bsType_con
          pieces,				-- pieceCount_con
	  to_char(null),			-- fileName_con
	  to_char(null),			-- tag_con
	  to_number(null),			-- copyNumber_con
	  to_char(null),			-- status_con
	  blocks,				-- blocks_con
	  block_size,				-- blockSize_con
	  to_char(null),			-- deviceType_con
	  completion_time,			-- compTime_con
	  to_date(null),			-- cfCreationTime_con

	  decode(incremental_change#,		-- type_act
		 0, full_act_t,
                 incremental_act_t),
	  incremental_change#,			-- fromSCN_act
	  checkpoint_change#,			-- toSCN_act
	  checkpoint_time,			-- toTime_act
	  openRecoveryActionCursor.dbincRlgSCN,	-- rlgSCN_act
          openRecoveryActionCursor.dbincRlgTime,-- rlgTime_act
	  dbinc_key,				-- dbincKey_act
	  incremental_level,			-- level_act

	  openRecoveryActionCursor.fno,		-- dfNumber_obj
	  openRecoveryActionCursor.crescn,	-- dfCreationSCN_obj
	  to_number(null),			-- logSequence_obj
	  to_number(null),			-- logThread_obj
	  to_number(null),			-- logRlgSCN_obj
	  to_date(null),			-- logRlgTime_obj
	  to_number(null),			-- logLowSCN_obj
	  to_date(null),			-- logLowTime_obj
	  to_number(null),			-- logNextSCN_obj
	  to_date(null)				-- logNextTime_obj
     FROM rc_backup_datafile
    WHERE dbinc_key        = openRecoveryActionCursor.dbincKey
      AND file#            = openRecoveryActionCursor.fno
      AND creation_change# = openRecoveryActionCursor.crescn
      AND (openRecoveryActionCursor.dfCkpSCN IS NULL OR
           openRecoveryActionCursor.dfCkpSCN < checkpoint_change#)
      AND (openRecoveryActionCursor.targetSCN IS NULL OR
           checkpoint_change# <= openRecoveryActionCursor.targetscn)
      AND (untilSCN IS NULL OR
           greatest(checkpoint_change#, absolute_fuzzy_change#) <= untilSCN)
      AND (restoreSource IS NULL OR restoreSource = BACKUP)
      AND status = 'A'

   UNION ALL

   -- Proxy Datafile Backups

   SELECT proxyCopy_con_t,			-- type_con
	  xdf_key,				-- key_con
	  xdf_recid,				-- recid_con
	  xdf_stamp,				-- stamp_con
	  to_number(null),			-- setStamp_con
	  to_number(null),			-- setCount_con
	  to_number(null),			-- bsRecid_con
	  to_number(null),			-- bsStamp_con
	  to_number(null),			-- bsKey_con
	  to_number(null),			-- bsLevel_con
          to_char(null),                        -- bsType_con
          to_number(null),			-- pieceCount_con
	  handle,				-- fileName_con
	  tag,					-- tag_con
	  to_number(null),			-- copyNumber_con
	  status,				-- status_con
	  blocks,				-- blocks_con
	  block_size,				-- blockSize_con
	  device_type,				-- deviceType_con
	  completion_time,			-- compTime_con
	  to_date(null),			-- cfCreationTime_con

      	  full_act_t,				-- type_act
	  0,					-- fromSCN_act
	  ckp_scn,				-- toSCN_act
	  ckp_time,				-- toTime_act
          openRecoveryActionCursor.dbincRlgSCN,	-- rlgSCN_act
          openRecoveryActionCursor.dbincRlgTime,-- rlgTime_act
	  dbinc_key,				-- dbincKey_act
	  to_number(null),			-- level_act

	  openRecoveryActionCursor.fno,		-- dfNumber_obj
	  openRecoveryActionCursor.crescn,	-- dfCreationSCN_obj
	  to_number(null),			-- logSequence_obj
	  to_number(null),			-- logThread_obj
	  to_number(null),			-- logRlgSCN_obj
	  to_date(null),			-- logRlgTime_obj
	  to_number(null),			-- logLowSCN_obj
	  to_date(null),			-- logLowTime_obj
	  to_number(null),			-- logNextSCN_obj
	  to_date(null)				-- logNextTime_obj
     FROM xdf
    WHERE dbinc_key  = openRecoveryActionCursor.dbincKey
      AND file#      = openRecoveryActionCursor.fno
      AND create_scn = openRecoveryActionCursor.crescn
      AND (openRecoveryActionCursor.dfCkpSCN IS NULL OR
           openRecoveryActionCursor.dfCkpSCN < ckp_scn)
      AND (openRecoveryActionCursor.targetSCN IS NULL OR
           ckp_scn <= openRecoveryActionCursor.targetscn)
      AND (untilSCN IS NULL OR
           greatest(ckp_scn, abs_fuzzy_scn, rcv_fuzzy_scn) <= untilSCN)
      AND status = 'A'
      AND (restoreTag is NULL OR
           tag = restoreTag OR
           computeRA_allRecords = TRUE#)
      AND (restoreSource is NULL OR restoreSource = BACKUP)

   UNION ALL

   -- Implicit offline Ranges

   SELECT offlineRangeRec_con_t,		-- type_con
	  to_number(null),			-- key_con
	  to_number(null),			-- recid_con
	  to_number(null),			-- stamp_con
	  to_number(null),			-- setStamp_con
	  to_number(null),			-- setCount_con
	  to_number(null),			-- bsRecid_con
	  to_number(null),			-- bsStamp_con
	  to_number(null),			-- bsKey_con
	  to_number(null),			-- bsLevel_con
          to_char(null),                        -- bsType_con
          to_number(null),			-- pieceCount_con
	  to_char(null),			-- fileName_con
	  to_char(null),			-- tag_con
	  to_number(null),			-- copyNumber_con
	  to_char(null),			-- status_con
	  to_number(null),			-- blocks_con
	  to_number(null),			-- blockSize_con
	  to_char(null),			-- deviceType_con
	  to_date(null),			-- compTime_con
	  to_date(null),			-- cfCreationTime_con

	  implicitRange_act_t,			-- type_act
	  openRecoveryActionCursor.offlSCN,	-- fromSCN_act
	  openRecoveryActionCursor.onlSCN,	-- toSCN_act
	  openRecoveryActionCursor.onlTime,	-- toTime_act
          openRecoveryActionCursor.dbincRlgSCN, -- rlgSCN_act
          openRecoveryActionCursor.dbincRlgTime,-- rlgTime_act
	  openRecoveryActionCursor.dbincKey,	-- dbincKey_act
	  to_number(null),			-- level_act

	  fno,					-- dfNumber_obj
	  crescn,				-- dfCreationSCN_obj
	  to_number(null),			-- logSequence_obj
	  to_number(null),			-- logThread_obj
	  to_number(null),			-- logRlgSCN_obj
	  to_date(null),			-- logRlgTime_obj
	  to_number(null),			-- logLowSCN_obj
	  to_date(null),			-- logLowTime_obj
	  to_number(null),			-- logNextSCN_obj
	  to_date(null)				-- logNextTime_obj
     FROM dual
    WHERE offlscn <> 0
      AND (openRecoveryActionCursor.dfCkpSCN is null OR
           openRecoveryActionCursor.dfCkpSCN <=
           openRecoveryActionCursor.offlSCN)
      AND (openRecoveryActionCursor.onlSCN >=	-- belongs to this incarnation
           openRecoveryActionCursor.dbincRlgSCN)
      AND (openRecoveryActionCursor.targetSCN is null OR
           openRecoveryActionCursor.onlscn <=	-- don't advance ckpt beyond
           openRecoveryActionCursor.targetSCN)	-- targetSCN
      AND (untilSCN is null OR        -- don't advance ckpt beyond until scn
           openRecoveryActionCursor.onlSCN < untilSCN)

   UNION ALL

   SELECT offlineRangeRec_con_t,		-- type_con
	  to_number(null),			-- key_con
	  to_number(null),			-- recid_con
	  to_number(null),			-- stamp_con
	  to_number(null),			-- setStamp_con
	  to_number(null),			-- setCount_con
	  to_number(null),			-- bsRecid_con
	  to_number(null),			-- bsStamp_con
	  to_number(null),			-- bsKey_con
	  to_number(null),			-- bsLevel_con
          to_char(null),                        -- bsType_con
          to_number(null),			-- pieceCount_con
	  to_char(null),			-- fileName_con
	  to_char(null),			-- tag_con
	  to_number(null),			-- copyNumber_con
	  to_char(null),			-- status_con
	  to_number(null),			-- blocks_con
	  to_number(null),			-- blockSize_con
	  to_char(null),			-- deviceType_con
	  to_date(null),			-- compTime_con
	  to_date(null),			-- cfCreationTime_con

	  cleanRange_act_t,			-- type_act
	  openRecoveryActionCursor.cleanSCN,	-- fromSCN_act
	  openRecoveryActionCursor.clean2SCN,	-- toSCN_act
	  openRecoveryActionCursor.clean2Time,	-- toTime_act
	  openRecoveryActionCursor.dbincRlgSCN,	-- rlgSCN_act
	  openRecoveryActionCursor.dbincRlgTime,-- rlgTime_act
	  openRecoveryActionCursor.dbincKey,	-- dbincKey_act
	  to_number(null),			-- level_act

	  fno,					-- dfNumber_obj
	  crescn,				-- dfCreationSCN_obj
	  to_number(null),			-- logSequence_obj
	  to_number(null),			-- logThread_obj
	  to_number(null),			-- logRlgSCN_obj
	  to_date(null),			-- logRlgTime_obj
	  to_number(null),			-- logLowSCN_obj
	  to_date(null),			-- logLowTime_obj
	  to_number(null),			-- logNextSCN_obj
	  to_date(null)				-- logNextTime_obj
     FROM dual
    WHERE openRecoveryActionCursor.cleanscn <> 0
      AND (openRecoveryActionCursor.dfCkpSCN is null OR
           openRecoveryActionCursor.dfCkpSCN <=
           openRecoveryActionCursor.cleanscn)
      AND -- belongs to this incarnation
          (openRecoveryActionCursor.clean2scn >=
           openRecoveryActionCursor.dbincRlgSCN)
      AND -- ignore if starts beyond target
          (openRecoveryActionCursor.targetscn is null OR
           openRecoveryActionCursor.cleanscn <
           openRecoveryActionCursor.targetSCN)
      AND -- If clean2scn is infinite, then we processed this when scanning
          -- the current incarnation,
          (openRecoveryActionCursor.targetSCN is null OR
           openRecoveryActionCursor.clean2SCN <=
           openRecoveryActionCursor.targetSCN)
      AND -- don't advance ckpt beyond until scn, unless we don't know
          -- where this offline range ends.
          (untilscn is null OR
           openRecoveryActionCursor.clean2SCN < untilSCN OR
           openRecoveryActionCursor.clean2SCN = 281474976710655)

   UNION ALL

   SELECT offlineRangeRec_con_t,		-- type_con
	  to_number(null),			-- key_con
	  to_number(null),			-- recid_con
	  to_number(null),			-- stamp_con
	  to_number(null),			-- setStamp_con
	  to_number(null),			-- setCount_con
	  to_number(null),			-- bsRecid_con
	  to_number(null),			-- bsStamp_con
	  to_number(null),			-- bsKey_con
	  to_number(null),			-- bsLevel_con
          to_char(null),                        -- bsType_con
          to_number(null),			-- pieceCount_con
	  to_char(null),			-- fileName_con
	  to_char(null),			-- tag_con
	  to_number(null),			-- copyNumber_con
	  to_char(null),			-- status_con
	  to_number(null),			-- blocks_con
	  to_number(null),			-- blockSize_con
	  to_char(null),			-- deviceType_con
	  to_date(null),			-- compTime_con
	  to_date(null),			-- cfCreationTime_con

	  spanningRange_act_t,			-- type_act
	  openRecoveryActionCursor.targetSCN,	-- fromSCN_act
	  to_number(null),			-- toSCN_act
	  to_date(null),			-- toTime_act
	  openRecoveryActionCursor.dbincRlgSCN,	-- rlgSCN_act
	  openRecoveryActionCursor.dbincRlgTime,-- rlgTime_act
	  openRecoveryActionCursor.dbincKey,	-- dbincKey_act
	  to_number(null),			-- level_act

	  fno,					-- dfNumber_obj
	  crescn,				-- dfCreationSCN_obj
	  to_number(null),			-- logSequence_obj
	  to_number(null),			-- logThread_obj
	  to_number(null),			-- logRlgSCN_obj
	  to_date(null),			-- logRlgTime_obj
	  to_number(null),			-- logLowSCN_obj
	  to_date(null),			-- logLowTime_obj
	  to_number(null),			-- logNextSCN_obj
	  to_date(null)				-- logNextTime_obj
     FROM dual
    WHERE -- a offline range which spans multiple resetlogs triggers
          -- this condition
          openRecoveryActionCursor.targetSCN <
          openRecoveryActionCursor.dbincRlgSCN

    ORDER BY 24 desc,     	-- toSCN_act
             23 asc,            -- fromSCN_act
             1 asc,             -- type_con
             4 desc;            -- stamp

END openRecoveryActionCursor;

---------------------------- computeRecoveryActions ---------------------------
FUNCTION computeRecoveryActions(
   fno         IN number	-- Datafile number.
  ,crescn      IN number	-- Datafile creation SCN.
  ,df_rlgscn   IN number	-- Datafile resetlogs SCN.
				-- Null if this is a RESTORE or LIST, else this
				-- is the value in the datafile header for the
				-- datafile we are RECOVERing.
  ,df_rlgtime  IN date		-- Datafile resetlogs time.
				-- Null if df_rlgscn is null, else value from
				-- datafile header.
  ,df_ckpscn   IN number	-- Datafile checkpoint SCN.
				-- Null if df_rlgscn is null, else value from
				-- datafile header.
  ,offlscn     IN number	-- kccfeofs (may be 0).
  ,onlscn      IN number	-- kccfeonc (0 if offlscn is 0).
  ,onltime     IN date		-- kccfeonc_time (ignored if kccfeofs is 0)
  ,cleanscn    IN number	-- kccfecps if either SOR or WCC set, else 0.
  ,clean2scn   IN number	-- CF ckpt SCN if WCC set.
                                -- Infinity if SOR bit set, else 0.
  ,clean2time  IN date		-- cf_checkpoint_time if WCC,
				-- SYSDATE if SOR bit set.
  ,allowfuzzy  IN boolean	-- TRUE if can be fuzzy at until SCN/time,
				-- FALSE if not.
  ,partial_rcv IN boolean
  ,target_scn  IN number	-- This is the SCN to which we want to recover
				-- the datafile.  Null unless we call
				-- ourselves recursively to search a prior
				-- incarnation.  We search prior incarnations
				-- only when there is an offline range
				-- spanning the resetlogs SCN.  So this SCN is
				-- really the offline range start SCN.
  ,dbinc_key   IN number	-- The key of the database incarnation to
				-- search in.  This may be different from
				-- df_rlgscn, and it may be different from the
				-- package var "this_dbinc_key".
  ,cf_scn      IN number
  ,cf_cretime  IN date		-- controlfile creation time.
				-- NULL if none mounted.
  ,cf_offrrid  IN number	-- recid of oldest offline range in controlfile
				-- NULL if none mounted.
  ,test_search IN boolean	-- if TRUE, then we have called ourself
				--  recursively and are not sure this is an
				-- ancestral incarnation
  ,done        IN OUT boolean	-- set to TRUE if successful.  (IN mode so
				-- we can test it).
  ,allCopies   IN boolean)	-- if true, then stack all copies of
                                -- a backup set if it was duplexed or copied.
-- NOTES:
-- If all copies is FALSE, then we should at least count the number of copies
-- of a backup set in addAction and save this number in the rcvRec.  This
-- would tell us how many copies of a backupset there are, which would be
-- useful for RMAN to know.  For example, if the customer wanted redundancy=2
-- but there was only 1 copy of an incremental backup, then this incremental
-- could not be considered as a substitute for archivelogs.

RETURN boolean IS

   ---------------------
   -- LOCAL VARIABLES --
   ---------------------

   r_act_cv 		rcvRecCursor_t;	-- cursor variable
   action 	 	rcvRec_t;       -- current row
   lastAction		rcvRec_t;
   parentDbincKey	number;         -- my parent dbinc's key

   dbinc_rlgscn 	number;         -- resetlogs scn for dbinc_key

   dbinc_rlgtime 	date;           -- resetlogs time for dbinc_key

   CURSOR dbinc_cursor(db_key number, rstscn number) IS
   SELECT dbinc_key
     FROM dbinc
    WHERE dbinc.db_key = dbinc_cursor.db_key
      AND dbinc.reset_scn < dbinc_cursor.rstscn;

   dbinc_row 		dbinc_cursor%ROWTYPE;

   savedrcvRecStackState rcvRecStackState_t;

   addAction_rc 	number;         -- return code

   canAddRedo_rc 	number;		-- return code

   addRedo_rc 		number;		-- return code

   isAncestor 		boolean;   	-- TRUE if we find an action we could
					-- apply with to_scn = target_scn

   rc 			boolean;	-- return code from recursive search
                                        -- of possible parent incarnation

   done_flag 		boolean;	-- for use in recursive calls
BEGIN

   done := FALSE;

   -- Compute this dbinc's resetlogs SCN and time --
   SELECT reset_scn, reset_time
     INTO dbinc_rlgscn, dbinc_rlgtime
     FROM dbinc
    WHERE dbinc.dbinc_key = computeRecoveryActions.dbinc_key;

   IF (df_rlgscn is not null) THEN                      -- doing RECOVER

      -- Check that this incarnation is reasonable for the
      -- datafile we are trying to recover.  We only want to look at
      -- incarnations with a resetlogs SCN >= the datafile's resetlogs SCN.

      IF (dbinc_rlgscn < df_rlgscn) THEN
         deb('dbinc_rlgscn < df_rlgscn (' || to_char(dbinc_rlgscn) ||
             ' < ' || to_char(df_rlgscn) || ')');
         RETURN FALSE;
      ELSIF (dbinc_rlgscn = df_rlgscn AND dbinc_rlgtime <> df_rlgtime) THEN
         deb('dbinc_rlgtime <> df_rlgtime');
         RETURN FALSE;
      END IF;
   END IF;

   IF (not test_search) THEN
      -- This is not test search of a prior incarnation, therefore
      -- we can set this flag right away.
      isAncestor := TRUE;
   ELSE
      isAncestor := FALSE;
   END IF;

   openRecoveryActionCursor(r_act_cv, dbinc_key,
                            fno, crescn, df_ckpscn,
                            dbinc_rlgscn, dbinc_rlgtime,
                            offlscn, onlscn, onltime,
                            cleanscn, clean2scn, clean2time, target_scn);

   ------------------------------------
   -- Process the rows from r_act_cv --
   ------------------------------------

<<action_loop>>
   LOOP
      <<next_row>>
      IF (fetchRecoveryAction(r_act_cv, df_ckpscn, action)) THEN

         -----------------------------
         -- Compute isAncestor flag --
         -----------------------------

         IF (bitand(action.type_con, backupMask_con_t) > 0 AND
             action.toSCN_act = target_scn) THEN
            isAncestor := TRUE;
         END IF;

         IF (action.type_con = offlineRangeRec_con_t) THEN
            -------------------
            -- OFFLINE RANGE --
            -------------------
            deb('found an offline range' ||
                ' from=' || to_char(action.fromSCN_act) ||
                ' to=' || to_char(action.toSCN_act));

            IF (action.type_act = spanningRange_act_t) THEN
               ----------------------------------------------
               -- OFFLINE RANGE spanning multiple resetlogs -
               ----------------------------------------------
               deb('offline range started before this resetlogs SCN');

               -- We don't need to add this action to the stack.  Just set
               -- addAction_rc to action_OK so that we will take the
               -- right path below and search our parent incarnation.
               addAction_rc := action_OK;
            ELSE
               addAction_rc := addAction(action, partial_rcv, isAncestor,
                 		         cf_scn, cf_cretime, cf_offrrid);
            END IF;
         ELSIF (action.type_con = backupSet_con_t AND
                action.type_act = incremental_act_t) THEN
            ----------------------------
            -- INCREMENTAL BACKUP SET --
            ----------------------------
            deb('found an incremental backup set');

            addAction_rc := addAction(actionIN    => action,
                                      partial_rcv => partial_rcv,
                                      isAncestor  => isAncestor,
                                      allCopies   => allCopies);
         ELSIF (action.type_act = full_act_t) THEN
            ------------------------------------------------------------
            -- DATAFILE COPY or FULL or LEVEL 0 BACKUP SET/PROXY COPY --
            ------------------------------------------------------------
            deb('found a copy/full/level 0/proxy copy');

            IF (df_ckpscn is not null) THEN	-- if doing a RECOVER
               -- Datafilecopies and full backups are not interesting when
               -- doing a RECOVER.  So don't stack this action, but do set the
               -- computeRA_restorable flag if we know this
               -- incarnation is an ancestor of the previous one or this is
               -- the current incarnation, and we can apply redo to this full
               -- backup if it would need some.
               IF (isAncestor) THEN
                  rcvRecTop(lastAction);
                  IF (not redoNeeded(action) OR
                      canAddRedo(isAncestor, 
                                 action.toSCN_act, action.rlgSCN_act,
                                 lastAction, partial_rcv) <>
                      action_OLD_REDO) THEN
                     computeRA_restorable := TRUE;
                  END IF;
               END IF;
               addAction_rc := action_SKIP;
            ELSE                                -- not doing a RECOVER
               addAction_rc := addAction(actionIN    => action,
                                         partial_rcv => partial_rcv,
                                         isAncestor  => isAncestor,
                                         allCopies   => allCopies);
            END IF;
         ELSE
            ------------------------
            -- UKNOWN ACTION KIND --
            ------------------------
            deb('unknown container type: ' || 
                 to_char(action.type_con) ||
                ' or action type: ' || to_char(action.type_act));

            -- We should signal an internal error here, but we don't have
            -- that ability from PLSQL.  So just raise 20999.
            raise_application_error(-20999, 'unknown action kind');
         END IF;                                -- "switch" on ACTION.KIND

         ---------------------------------------------
         -- Handle the return code from addAction() --
         ---------------------------------------------

         deb('addAction returned code ' || to_char(addAction_rc));

         IF (addAction_rc = action_OK) THEN	-- the action was added

            --------------------------------------
            -- Check for terminating conditions --
            --------------------------------------

            IF (rcvRecStackState.savePoint > 0 AND 
                computeRA_allRecords = FALSE#) THEN
               -- If we now have a fullKind action and we are not looking
               -- for all records, then we are done.  Note that this must
               -- be a RESTORE because savePoint is never non-zero if we are
               -- doing RECOVER because fullKind actions are discarded
               -- by RECOVER.
               done := TRUE;
               EXIT action_loop;
            END IF;

            IF (df_ckpscn is not null) THEN		-- if doing RECOVER
               IF (action.type_con = offlineRangeRec_con_t) THEN
                  -- For offline ranges, the datafile must be checkpointed
                  -- precisely at the offline range start SCN.
                  IF (df_ckpscn = action.fromSCN_act) THEN
                     done := TRUE;
                     EXIT action_loop;
                  END IF;
               ELSE
                  -- For any other action, it is sufficient if the datafile
                  -- is checkpointed >= the action's from_scn.
                  IF (df_ckpscn >= action.fromSCN_act) THEN
                     done := TRUE;
                     EXIT action_loop;
                  END IF;
               END IF;
            END IF;

            ----------------------------------------
            -- Offline Range Spanning a Resetlogs --
            ----------------------------------------

            -- If the action was an offline range and this offline range
            -- started before the current resetlogs, then we must search the
            -- previous incarnation.

            IF (action.type_con = offlineRangeRec_con_t AND
                action.fromSCN_act < dbinc_rlgscn) THEN

               -- This action is the last action we will fetch from this
               -- incarnation.  There cannot be any other actions from this
               -- incarnation with a lower toSCN.  Incremental backups cannot
               -- span resetlogs, and any full backup checkpointed at the end
               -- of the offline range would have been returned from the
               -- cursor already because full backups have a fromSCN of 0.

               -- Get parent's dbinc key --

               SELECT parent_dbinc_key
                 INTO parentDbincKey
                 FROM dbinc
                WHERE dbinc.dbinc_key = computeRecoveryActions.dbinc_key;

               IF (parentDbincKey is null) THEN -- we don't know our parent

                  -- Open a cursor to get all dbinc rows for this database
                  -- that could possibly be our parent incarnation.  We will
                  -- search them all.  If exactly one turns out to work,
                  -- then we assume it is our parent.  If more than one works,
                  -- then we cannot decide and will fail.  If none works, well
                  -- too bad.
                  OPEN dbinc_cursor(this_db_key, dbinc_rlgscn);

                  deb('doing scan of all possible parent incarnations, top=' ||
                      to_char(rcvRecStack.last));

                  <<dbinc_loop>>
                  LOOP
                     FETCH dbinc_cursor
                      INTO dbinc_row;
                     EXIT WHEN dbinc_cursor%NOTFOUND;
                     deb('starting test search of incarnation key=' ||
                         to_char(dbinc_row.dbinc_key));

                     savedrcvRecStackState := rcvRecStackState;
                     rcvRecStackState.top := rcvRecStack.last;

                     rc := computeRecoveryActions(fno, crescn,
                                                  df_rlgscn, df_rlgtime,
                                                  df_ckpscn,
                                                  offlscn,
                                                  onlscn, onltime,
                                                  cleanscn,
                                                  clean2scn, clean2time,
                                                  allowfuzzy, partial_rcv,
                                    rcvRecStack(rcvRecStack.count).fromSCN_act,
                                                  dbinc_row.dbinc_key,
                                                  cf_scn, cf_cretime,
                                                  cf_offrrid,
                                                  TRUE, done_flag, allCopies);

                     -- Trim any actions that may have gotten stacked in
                     -- the test search.  If rc was FALSE, then none should
                     -- have been stacked, but it doesn't hurt to do a
                     -- trim(0), so do it anyway.
                     deb('last=' || to_char(rcvRecStack.last) ||
                         ' trimming last ' ||
                         to_char(rcvRecStack.last - rcvRecStackState.top)
                        );
                     rcvRecStack.trim(rcvRecStack.last - rcvRecStackState.top);
                     rcvRecStackState := savedrcvRecStackState;
                     deb('count is now ' || to_char(rcvRecStack.count));

                     IF (rc) THEN
                        -- We found an action with a checkpoint SCN that
                        -- matched the offline range start SCN.  So we just
                        -- searched what was most likely our parent
                        -- incarnation.  It is possible that the datafile went
                        -- offline clean at the same SCN in two different
                        -- incarnations.  This is very unlikely, but if it
                        -- happens, then there is no way for us to decide
                        -- which incarnation was the correct one.  So save the
                        -- dbinc_key, trim back any actions we may have added,
                        -- and continue the search.  If we exhaust the
                        -- dbinc_cursor and find only 1 incarnation that
                        -- worked, we can assume it was the right one.
                        -- If we find more than one, then we have to give up.
                        IF (parentDbincKey is null) THEN
                           parentDbincKey := dbinc_row.dbinc_key;
                        ELSE
                           -- We've found a second incarnation that could be
                           -- our parent.  Since we cannot distintuish between
                           -- them, we are done.  
                           deb('aborting search due to ambiguous ancestory');
                           CLOSE dbinc_cursor;
                           EXIT action_loop;
                        END IF;
                     END IF;
                  END LOOP;	-- dbinc_loop
               END IF;		-- if we don't know our parent

               -- If we know our parent from the catalog or just found our
               -- parent in the search above, then search our parent.
               IF (parentDbincKey is not null) THEN
                  deb('starting search of parent incarnation key='||
                      to_char(parentDbincKey));
                  rc := computeRecoveryActions(fno, crescn,
                                               df_rlgscn, df_rlgtime,
                                               df_ckpscn,
                                               offlscn,
                                               onlscn, onltime,
                                               cleanscn,
                                               clean2scn, clean2time,
                                               allowfuzzy, partial_rcv,
                                     rcvRecStack(rcvRecStack.last).fromSCN_act,
                                               parentDbincKey,
                                               cf_scn, cf_cretime,
                                               cf_offrrid,
                                               FALSE, done_flag, allCopies);

                  IF (done_flag) THEN
                     done := TRUE;
                  END IF;

                  IF (action.type_act = spanningRange_act_t) THEN
                     -- We went recursive because of an offline range which
                     -- spanned multiple resetlogs.  In this case, we
                     -- propagate the return code from the recursive search.
                     -- The incarnation we are currently searching is an
                     -- ancestor if the incarnation we just searched
                     -- recursively is an ancestor.
                     isAncestor := rc;
                  END IF;
               END IF;		-- we know or found our parent
               EXIT action_loop;
            END IF;		-- offline range start SCN < dbinc_rlgscn
         ELSIF (addAction_rc = action_FAIL) THEN         -- FAIL
            -- Cannot apply this action for some reason.  That's OK,
            -- there may be other actions that we can apply.
            NULL;
         ELSIF (addAction_rc = action_SKIP) THEN	-- SKIP
            -- We don't want to add this action to the stack.
            NULL;
         ELSIF (addAction_rc = action_OLD_REDO) THEN	-- OLD_REDO
            -- If OLD_REDO, then the to_scn of this action does not reach the
            -- from_scn of the last stacked action, and we are searching an
            -- ancestral incarnation where we cannot apply redo.  We know we
            -- will not see any actions with a higher to_scn, so this stops
            -- our search.  No other actions from this incarnation are usable
            -- because we cannot reach the spanning offline range from them.
            EXIT action_loop;
         ELSE                                   -- unknown return code
            raise_application_error(-20999, 'unknown add action return code');
         END IF;
      ELSE                                      -- r_act_cv exhausted
         deb('end of cursor reached');
         EXIT action_loop;
      END IF;                                   -- if fetchRecoveryAction
   END LOOP;

   CLOSE r_act_cv;                              -- done with cursor

   IF (done) THEN
      RETURN isAncestor;
   END IF;

   -- There is nothing else in this incarnation that we can
   -- restore or apply.  If we are doing RECOVER (df_rlgscn not null),
   -- then we can just apply redo to this datafile.

   IF (df_rlgscn is not null) THEN              -- if doing RECOVER
      IF (rcvRecStack.count > 0) THEN      	-- if found some action
         -- Add a redo action to go from the datafile's checkpoint up to
         -- the from_scn of the last action we stacked.  If partial_rcv is
         -- FALSE, then addRedo() will trim the actions we stacked because we
         -- cannot use them.
         rcvRecTop(lastAction);
         addRedo_rc := addRedo(isAncestor, df_ckpscn, df_rlgscn,
                               lastAction, partial_rcv);
         IF (addRedo_rc = action_OK OR addRedo_rc = action_FAIL) THEN
            -- OK means we could do the partial recovery from the datafile
            -- checkpoint up to the first action we found.  FAIL means
            -- we could not add the redo, but we don't really need to apply
            -- any.  In either case, the file can be successfully recovered.
            done := TRUE;
         END IF;
      ELSE
         deb('computeRecoveryAction: no actions stacked');
         -- We didn't stack any actions.  So this datafile is recoverable only
         -- if is from the current incarnation.  Set the done flag accordingly.
         IF (df_rlgscn = this_reset_scn AND
             df_rlgtime = this_reset_time) THEN
            done := TRUE;
         END IF;
      END IF;
   ELSE                                         -- not doing a RECOVER
      -- We may decide to trim the stack here.  Never trim beyond the top
      -- of the stack.  The top is normally 0, but it is non-zero when
      -- we are making a test search to find our parent incarnation.
      IF (rcvRecStackState.savePoint = 0) THEN
         -- We did not find any fullKind actions that we could apply.
         -- This means we cannot restore this file or there is nothing
         -- interesting to list.  Note that we may have some actions
	 -- stacked at this point, but they are not useful.
         -- Here is where we could do a CREATE DATAFILE.
         deb('could try create datafile');
         rcvRecStack.trim(rcvRecStack.count - rcvRecStackState.top);
						-- purge all actions
      ELSE
         -- We found at least 1 fullKind action, so the file is
         -- definitly restorable or there is something interesting to LIST.
         -- No actions stacked after the savePoint are usable though because
         -- there is no fullKind backup to which we could apply these actions.
         -- So trim all actions up to the savePoint.
         rcvRecStack.trim(rcvRecStack.last -
                          greatest(rcvRecStackState.savePoint,
                                   rcvRecStackState.top));
         done := TRUE;
      END IF;
   END IF;                                      -- if doing recover

   IF (done) THEN
      deb('computeRecoveryActions: returning with done = TRUE');
   ELSE
      deb('computeRecoveryActions: returning with done = FALSE');
   END IF;

   RETURN isAncestor;

END computeRecoveryActions;

----------------------------- trimRecoveryActions -----------------------------

FUNCTION trimRecoveryActions(
   maxActions 		IN number
  ,containerMask 	IN number
  ,actionMask		IN number)
RETURN NUMBER IS
   dummy     rcvRec_t;
   remaining number;
BEGIN
   IF (rcvRecStack.count > 0) THEN
      rcvRecPop(dummy);
      remaining := trimRecoveryActions(maxActions, containerMask, actionMask);

      -- If this record is one that would be selected by the masks in effect,
      -- then we can keep it if the remaining records on the
      -- the stack after trimming are less than the maxActions limit.
      -- Note:  if other kinds of filtering are going to occur in
      -- getRecoveryAction, then that filtering should also be done here
      -- so that we don't count a record that won't be seen by the client.
      IF ((bitand(dummy.type_con, containerMask) <> 0) AND
          (bitand(dummy.type_act, actionMask) <> 0)) THEN
         IF (remaining < maxActions) THEN
            rcvRecPush(dummy);		-- put back on stack
            RETURN remaining + 1;
         ELSE
            -- Cannot keep it.
            IF (debug) THEN
               deb('trimRecoveryActions: deleting action:');
               printRcvRec(dummy);
            END IF;
            RETURN remaining;
         END IF;
      ELSE
         -- This record is not selected by the masks in effec, so we
         -- keep it on the stack.
         rcvRecPush(dummy);
         RETURN remaining;
      END IF;
   ELSE
      RETURN 0;
   END IF;
END trimRecoveryActions;

-----------------------------------------------
-- *** PUBLIC FUNCTION/PROCEDURE SECTION *** --
-----------------------------------------------

---------------
-- Debugging --
---------------

---------------------------------- dumpState ----------------------------------
FUNCTION dumpState(
   lineno IN number)
RETURN varchar2 IS 
BEGIN
   IF    lineno = 1 THEN RETURN 'this_db_key='||this_db_key;
   ELSIF lineno = 2 THEN RETURN 'this_dbinc_key='||this_dbinc_key;
   ELSIF lineno = 3 THEN RETURN 'this_reset_scn='||this_reset_scn;
   ELSIF lineno = 4 THEN RETURN 'this_reset_time='||this_reset_time;
   ELSIF lineno = 5 THEN RETURN 'untilSCN='||untilSCN;
   ELSIF lineno = 6 THEN RETURN 'untilTime='||untilTime;
   ELSIF lineno = 7 THEN RETURN 'getRA_completedAfter='||getRA_completedAfter;
   ELSIF lineno = 8 THEN RETURN 
      'getRA_completedBefore='||getRA_completedBefore;
   ELSIF lineno = 9 THEN RETURN 'getRA_likePattern='||getRA_likePattern;
   ELSIF lineno = 10 THEN RETURN 'getRA_containerMask='||getRA_containerMask;
   ELSIF lineno = 11 THEN RETURN 'getRA_actionMask='||getRA_actionMask;
   ELSIF lineno = 12 THEN RETURN 'computeRA_allRecords='||computeRA_allRecords;
   ELSIF lineno = 13 THEN RETURN 'allIncarnations='||allIncarnations;
   ELSE RETURN NULL;
   END IF;
END dumpState;

---------------------------------- setDebugOn ---------------------------------
PROCEDURE setDebugOn
IS
BEGIN
   debug := TRUE;
END setDebugOn;

--------------------------------- setDebugOff ---------------------------------
PROCEDURE setDebugOff
IS
BEGIN
   debug := FALSE;
END setDebugOff;

----------------------------
-- Package Initialization --
----------------------------

-- This is a vestigal function that was released to customers in 8.1.3 Beta.
-- It is no longer called, and is no longer needed, but must still be here
-- because this version of the package may be called by an 8.1.3 rman
-- executable.

---------------------------------- initialize ---------------------------------
PROCEDURE initialize(
   rman_vsn IN number)
IS
BEGIN
   NULL;
END initialize;

---------------------------- set_package_constants ----------------------------
PROCEDURE set_package_constants
IS
BEGIN
   -- This procedure exists only for backwards compatibility with RMAN 8.1.5.
   -- This package no longer cares what the values are.  The rddf cursor
   -- now implements the order-by for preference using hard-coded values
   -- that are not returned to the client.
   NULL;
END set_package_constants;

-----------------------
-- Utility functions --
-----------------------

---------------------------------- stamp2date ---------------------------------
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 stamp2date;

------------------------------
-- Set Database Incarnation --
------------------------------

--------------------------------- setDatabase ---------------------------------
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);
BEGIN
   this_db_key := NULL;                  -- clear in case exception raised
   this_dbinc_key := NULL;
   this_reset_scn := NULL;
   this_reset_time := NULL;

   -- verify that this package is compatible with the recovery catalog
   BEGIN
      SELECT null
        INTO local.db_key
        FROM rcver
       WHERE version = catalogVersion;
   EXCEPTION
      WHEN no_data_found THEN
         raise_application_error(-20299, 'Recovery catalog version mismatch');
   END;

   -- 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
      deb('setDatabase: db_id=' || to_char(db_id));
      BEGIN
         SELECT db.db_key, curr_dbinc_key, dbinc.reset_scn, dbinc.reset_time
           INTO local.db_key, local.dbinc_key,
                local.reset_scn, local.reset_time
           FROM db, dbinc
          WHERE db_id = setDatabase.db_id	-- should return 1 row
            AND dbinc.dbinc_key = db.curr_dbinc_key;
      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')
              INTO current_inc
              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;
      END IF;
   ELSIF (db_name is NOT NULL) THEN
      -- If db_id is unknown, try using db_name
      deb('setDatabase: db_id is null');
      BEGIN
         SELECT db.db_key, db.curr_dbinc_key, dbinc.reset_scn, dbinc.reset_time
           INTO local.db_key, local.dbinc_key,
                local.reset_scn, local.reset_time
           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;
   this_reset_scn := local.reset_scn;
   this_reset_time := local.reset_time;

END setDatabase;

--------------------------------- setDbincKey ---------------------------------
PROCEDURE setDbincKey(
   key IN number)
IS
BEGIN
   IF (key is not null) THEN
      this_dbinc_key := key;
   ELSE
      -- if this query gets more than 1 row, than you lose.  try again.
      SELECT curr_dbinc_key
        INTO this_dbinc_key
        FROM db;
   END IF;

   SELECT db_key, reset_scn
     INTO this_db_key, this_reset_scn
     FROM dbinc
    WHERE dbinc_key = this_dbinc_key;
END setDbincKey;

----------------------------- getParentIncarnation ----------------------------
FUNCTION getParentIncarnation(
   resetlogs_change# IN OUT number
  ,resetlogs_time    IN OUT date)
RETURN number IS
BEGIN
   -- If input is null, this is the first call, and we return the current
   -- incarnation's key.
   IF (resetlogs_change# is null) THEN
      getParentIncarnationKey := this_dbinc_key;
   END IF;
   SELECT resetlogs_change#, resetlogs_time, parent_dbinc_key
     INTO resetlogs_change#, resetlogs_time, getParentIncarnationKey
     FROM rc_database_incarnation where dbinc_key = getParentIncarnationKey;
   RETURN TRUE#;
EXCEPTION
   WHEN no_data_found THEN
      RETURN FALSE#;
END getParentIncarnation;

-------------------------------- getCheckpoint --------------------------------
PROCEDURE getCheckpoint(
   scn OUT number
  ,seq OUT number)
IS
BEGIN
   IF (this_dbinc_key is NULL) THEN
     raise_application_error(-20020, 'Database incarnation not set');
   END IF;

   -- This query deserves an explanation.
   -- It must return exactly 1 row.  Its purpose is to find the ckp row
   -- with the greatest ckp_scn and same high_df_recid as the most recent FULL
   -- checkpoint.
   -- It works as follows:
   -- Join the ckp table to itself, using as join keys:
   --    high_df_recid
   --    cf_create_time (recid makes sense only within a particular cfile)
   --    dbinc_key (obvious)
   -- The first "not exists" subquery is there to find the FULL ckp with
   -- the greatest ckp_scn.  If there are 2 FULL checkpoints with the
   -- same ckp_scn, we choose the one with the highest cf_seq or the highest
   -- cf_create_time.  Note that we depend on the ckp_u1 constraint to
   -- ensure that this subquery causes the "full" side of the join to have
   -- only 1 row.
   --
   -- The second "not exists" subquery is there to find the set of ckp_rows
   -- of either ckp_type with the highest ckp_scn for a given high_df_recid
   -- and cf_create_time.  Again, we rely on the ckp_u1 constraint to
   -- ensure that for a given high_df_recid and cf_create_time we have exactly
   -- 1 row.  If there are 2 rows with the same ckp_scn for a given
   -- recid and create_time, then we choose the one with the highest cf_seq.
   -- If the cf_seq is the same, then the ckp_type columns *must* differ
   -- because of the ckp_u1 constraint.  In this unlikely case, we choose
   -- the 'FULL' one ('FULL' > 'PARTIAL')

   SELECT either.ckp_scn, either.ckp_cf_seq
     INTO scn, seq
     FROM ckp either, ckp full
    WHERE full.dbinc_key = this_dbinc_key
      AND full.ckp_type = 'FULL'
      AND either.dbinc_key = this_dbinc_key
      AND either.high_df_recid = full.high_df_recid
      AND either.cf_create_time = full.cf_create_time
      AND NOT EXISTS (SELECT 1
                        FROM ckp full2
                       WHERE full2.dbinc_key = this_dbinc_key
                         AND full2.ckp_type = 'FULL'
                         AND ((full2.ckp_scn > full.ckp_scn) OR
                              ((full2.ckp_scn = full.ckp_scn) AND
                               ((full2.cf_create_time > full.cf_create_time) OR
                                (full2.cf_create_time = full.cf_create_time AND
                                 full2.ckp_cf_seq > full.ckp_cf_seq)))))
      AND NOT EXISTS (SELECT 1
                        FROM ckp either2
                       WHERE either2.dbinc_key = this_dbinc_key
                         AND either2.high_df_recid = either.high_df_recid
                         AND either2.cf_create_time = either.cf_create_time
                         AND ((either2.ckp_scn > either.ckp_scn) OR
                              ((either2.ckp_scn = either.ckp_scn) AND
                               ((either2.ckp_cf_seq > either.ckp_cf_seq) OR
                                (either2.ckp_cf_seq = either.ckp_cf_seq AND
                                 either2.ckp_type > either.ckp_type)))));

EXCEPTION
   WHEN no_data_found THEN
      scn := 0;
      seq := 0;
END getCheckpoint;

-------------------
-- Query Filters --
-------------------

------------------------------ setCompletedRange ------------------------------
PROCEDURE setCompletedRange(
   after  IN date
  ,before IN date)
IS
BEGIN
   getRA_completedAfter  := after;
   getRA_completedBefore := before;
END setCompletedRange;

-------------------------------- setLikePattern -------------------------------
PROCEDURE setLikePattern(
   pattern IN varchar2)
IS
BEGIN
   getRA_likePattern := pattern;
END setLikePattern;

------------------------------ setAllIncarnations -----------------------------
PROCEDURE setAllIncarnations(
   flag IN boolean)
IS
BEGIN
   IF (flag) THEN
      allIncarnations := TRUE#;
   ELSE
      allIncarnations := FALSE#;
   END IF;
END setAllIncarnations;

-- Obsolete as of 8.2

---------------------------------- setAllFlag ---------------------------------
PROCEDURE setAllFlag(
   flag IN boolean)
IS
BEGIN
   setAllIncarnations(flag);
   IF (flag) THEN
      ignoreCreationSCN := TRUE#;
   ELSE
      ignoreCreationSCN := FALSE#;
   END IF;
END setAllFlag;

--------------------------------- setUntilTime --------------------------------
PROCEDURE setUntilTime(
   unttime IN date)
IS
BEGIN
   IF (this_dbinc_key is NULL) THEN
      raise_application_error(-20020, 'Database incarnation not set');
   END IF;

   untilSCN  := NULL;
   untilTime := unttime;

   BEGIN
      SELECT resetlogs_change#
        INTO untilSCN
        FROM rc_database
       WHERE dbinc_key = this_dbinc_key
         AND resetlogs_time <= untilTime;
   EXCEPTION
      WHEN no_data_found THEN
         raise_application_error(-20207,
                                 'until time is before resetlogs time');
   END;

   -- find the highest SCN that is associated with a timestamp less than or
   -- equal to the untilTime. This will give us an approximation of the SCN
   -- where point-in-time recovery will stop and we use it to limit the
   -- candidates for restore to backups and copies that are not fuzzy at the
   -- SCN.
   computeUntilSCN(untilTime, untilSCN);
END setUntilTime;

--------------------------------- setUntilScn ---------------------------------
PROCEDURE setUntilScn(
   scn IN number)
IS
BEGIN
   untilSCN := scn;
   untilTime := NULL;

   IF (this_dbinc_key is NULL) THEN
      raise_application_error(-20020, 'Database incarnation not set');
   END IF;

   BEGIN
      SELECT untilSCN
        INTO untilSCN
        FROM rc_database
       WHERE dbinc_key = this_dbinc_key
         AND resetlogs_change# <= untilSCN;
   EXCEPTION
      WHEN no_data_found THEN
         raise_application_error(-20208, 'until SCN is before resetlogs SCN');
   END;
END setUntilScn;

--------------------------------- setUntilLog ---------------------------------
PROCEDURE setUntilLog(
   sequence# IN number
  ,thread# IN number)
IS
BEGIN
   untilTime := NULL;
   untilSCN := NULL;

   IF (this_dbinc_key is NULL) THEN
      raise_application_error(-20020, 'Database incarnation not set');
   END IF;

   IF (sequence# is NULL) THEN
      raise_application_error(-20205, 'Incomplete UNTIL clause');
   END IF;

   BEGIN
      -- set untilSCN to the low SCN of the specified log
      SELECT first_change#
        INTO untilSCN
        FROM rc_log_history
       WHERE dbinc_key = this_dbinc_key
         AND thread# = nvl(setUntilLog.thread#, 1)  -- default thread# is 1
         AND sequence# = setUntilLog.sequence#;
   EXCEPTION
      WHEN no_data_found THEN
         BEGIN
            -- the specified log is not (yet?) in the recovery catalog.
            -- try setting untilSCN to the next SCN of the previous log
            SELECT next_change#
              INTO untilSCN
              FROM rc_log_history
             WHERE dbinc_key = this_dbinc_key
               -- default thread# is 1
               AND thread# = nvl(setUntilLog.thread#, 1)
               AND sequence# = setUntilLog.sequence# - 1;
         EXCEPTION
            WHEN no_data_found THEN
               raise_application_error(-20206, 'Specified log does not exist');
         END;
   END;
END setUntilLog;

--------------------------------- getUntilTime --------------------------------
FUNCTION getUntilTime
RETURN date IS
BEGIN
   RETURN untilTime;
END getUntilTime;

--------------------------------- getUntilScn ---------------------------------
FUNCTION getUntilScn
RETURN number IS
BEGIN
   RETURN untilScn;
END getUntilScn;

---------------------------------- resetUntil ---------------------------------
PROCEDURE resetUntil
IS
BEGIN
   untilSCN := NULL;
   untilTime := NULL;
END resetUntil;

----------------------------------- setFrom -----------------------------------
PROCEDURE setFrom(
   restorefrom IN number DEFAULT NULL)
IS
BEGIN
   IF (restorefrom in (BACKUP, COPY) OR restorefrom is NULL) THEN
      restoreSource := restorefrom;
   ELSE
      raise_application_error(-20200, 'Invalid restore source');
   END IF;
END setFrom;

-------------------------------- setDeviceType --------------------------------
PROCEDURE setDeviceType(
   type IN varchar2)
IS
BEGIN
   IF (deviceCount >= 8) THEN
      raise_application_error(-20280, 'Too many device types');
   END IF;
   deviceCount := deviceCount + 1;
   deviceList(deviceCount) := type;
   IF (type = 'DISK') THEN
      diskDevice := TRUE;
   END IF;
END setDeviceType;

------------------------------- setDeviceTypeAny ------------------------------
PROCEDURE setDeviceTypeAny
IS
BEGIN
   diskDevice := true;
   anyDevice := true;
   deviceCount := 0;
END setDeviceTypeAny;

------------------------------- resetDeviceType -------------------------------
PROCEDURE resetDeviceType
IS
BEGIN
   FOR i in 1..8 LOOP
      deviceList(i) := NULL;
   END LOOP;
   deviceCount := 0;
   diskDevice := FALSE;
   anyDevice := FALSE;
END resetDeviceType;

------------------------------------ setTag -----------------------------------
PROCEDURE setTag(
   tag IN varchar2 DEFAULT NULL)
IS
BEGIN
   restoreTag := tag;
END setTag;

----------------------------------- resetAll ----------------------------------
PROCEDURE resetAll
IS
BEGIN
   -- reset to defaults
   setRAflags(kindMask   => allKind,
              allRecords => FALSE);
   setAllFlag(FALSE);
   setLikePattern(NULL);
   setCompletedRange(after => NULL, before => NULL);
   resetUntil;
   setFrom(NULL);
   resetDeviceType;
   setTag(NULL);                        -- restoreTag := NULL
   versionCounter := 1;			-- for getPackageVersion

END resetAll;

---------------------------
-- Backup Set Validation --
---------------------------

------------------------------ findValidBackupSet -----------------------------
PROCEDURE findValidBackupSet(
   backupSetRec		   IN     rcvRec_t
  ,deviceType		   IN	  varchar2 DEFAULT NULL
  ,tag			   IN     varchar2 DEFAULT NULL
  ,available               IN     number DEFAULT TRUE#
  ,unavailable             IN     number DEFAULT FALSE#
  ,deleted                 IN     number DEFAULT FALSE#
  ,expired                 IN     number DEFAULT FALSE#)
IS
BEGIN
   IF (findValidBackupSet_c%ISOPEN) THEN
      CLOSE findValidBackupSet_c;
   END IF;
   OPEN findValidBackupSet_c(bsKey	 => backupSetRec.bsKey_con,
                             pieceCount  => backupSetRec.pieceCount_con,
                             deviceType	 => deviceType,
                             tag         => tag,
                             available   => available,
			     unavailable => unavailable,
			     deleted     => deleted,
                             expired     => expired);

   deb('findValidBackupSet: bsKey=' || to_char(backupSetRec.bsKey_con) ||
       ' pieceCount=' || to_char(backupSetRec.pieceCount_con) ||
       ' tag=' || nvl(tag, 'NULL') ||
       ' deviceType=' || nvl(deviceType, 'NULL'));
   getValidBackupSetLast.code       := 99;	-- init for getValidBackupSet
END findValidBackupSet;

-- This version of findValidBackupSet takes a bsRec_t instead of a rcvRec_t.

------------------------------ findValidBackupSet -----------------------------
PROCEDURE findValidBackupSet(
   backupSetRec		   IN     bsRec_t
  ,deviceType		   IN	  varchar2 DEFAULT NULL
  ,tag			   IN     varchar2 DEFAULT NULL
  ,available               IN     number   DEFAULT TRUE#
  ,unavailable             IN     number   DEFAULT FALSE#
  ,deleted                 IN     number   DEFAULT FALSE#
  ,expired                 IN     number   DEFAULT FALSE#)
IS
BEGIN
   deb('findValidBackupSet: bsKey=' || to_char(backupSetRec.key));
   IF (findValidBackupSet_c%ISOPEN) THEN
      CLOSE findValidBackupSet_c;
   END IF;
   OPEN findValidBackupSet_c(bsKey	 => backupSetRec.key,
                             pieceCount  => backupSetRec.pieceCount,
                             deviceType	 => deviceType,
                             tag         => tag,
                             available   => available,
			     unavailable => unavailable,
			     deleted     => deleted,
                             expired     => expired);

   getValidBackupSetLast.code       := 99;	-- init for getValidBackupSet
END findValidBackupSet;

------------------------------ getValidBackupSet ------------------------------
FUNCTION getValidBackupSet(
   validBackupSetRec		OUT validBackupSetRec_t
  ,checkDeviceIsAllocated  	IN  number DEFAULT FALSE#)
RETURN number IS
   lastCode	number;
   checkRc	number;
   local        validBackupSetRec_t;
BEGIN
<<nextRow>>
   FETCH findValidBackupSet_c
    INTO local;

   IF (findValidBackupSet_c%NOTFOUND) THEN
      deb('getValidBackupSet:  notfound');
      CLOSE findValidBackupSet_c;
      RETURN FALSE#;
   END IF;

   lastCode := getValidBackupSetLast.code;	-- save for test below
   getValidBackupSetLast := local;		-- save for next time here

   IF (local.code <= lastCode) THEN
      -- The findValidBackupSet cursor returns 4 colums: deviceType, tag,
      -- copy#, and a code (1,2,3).  If the code is 1, it means we found a
      -- complete set of pieces with the same deviceType, tag, and copy#.  We
      -- always want to process records of type 1.  If the code is 2, it means
      -- we ignored copy#, and found a complete set of pieces with the same
      -- deviceType and tag.  We only want to process records of type 2 if
      -- we did not see this deviceType/tag combination with code 1.

      -- A code 3 record means we ignored copy# and tag, and found a complete
      -- set of pieces with the same deviceType.  We only want to process
      -- records of code 3 if we did not see any records of type 1 or 2
      -- for this device type.

      -- The order by allows us to implement this very easily.  We return a
      -- record only if its code is <= the code for the last record we
      -- fetched.  This works because if there is a code 2 record, then it
      -- will be followed by a code 3 record.  If there is a code 1 record, it
      -- will be followed by a code 2 and then code 3 record.

      -- Since this record's code is <= the previous record's code, we
      -- must be looking at a different deviceType, tag, and/or copy# than
      -- before, or this is the first record.  In either case, we want to
      -- process the record.

      IF (checkDeviceIsAllocated = TRUE#) THEN
         IF (NOT isDeviceTypeAllocated(local.deviceType)) THEN
            deb('getValidBackupSet: device type not allocated: ' ||
                local.deviceType);
            GOTO nextRow;
         END IF;
      END IF;
      validBackupSetRec := local;		-- set OUT mode arg
      deb('getValidBackupSet: returning valid rec deviceType=' ||
          local.deviceType || ' tag=' || local.tag || ' copyNumber=' ||
          to_char(local.copyNumber));
      RETURN TRUE#;
   ELSE
      deb('getValidBackupSet: local.code=' || to_char(local.code) ||
          ' lastCode=' || to_char(lastCode));
      GOTO nextRow;
   END IF;
END getValidBackupSet;

---------------------
-- Get an rcvRec_t --
---------------------

---------------------------------- getRcvRec ----------------------------------
FUNCTION getRcvRec(
   funCode	IN number
  ,rcvRec       OUT rcvRec_t
  ,callAgain    OUT number)
RETURN number IS
   rc		number;
BEGIN
   rc := 0;					-- init for procedures
   callAgain := TRUE#;

   IF (funCode = getCfCopy) THEN
      getControlfileCopy(rcvRec);
   ELSIF (funCode = getDfCopy) THEN
      getDatafileCopy(rcvRec);
   ELSIF (funCode = getAnyProxy) THEN
      getProxyCopy(rcvRec);
   ELSIF (funCode = getCfBackup) THEN
      rc := getControlfileBackup(rcvRec);
      callAgain := FALSE#;
   ELSIF (funCode = listCfCopy) THEN
      listGetControlfileCopy(rcvRec);
   ELSIF (funCode = listDfCopy) THEN
      listGetDatafileCopy(rcvRec);
   ELSIF (funCode = listCfBackup) THEN
      listGetControlfileBackup(rcvRec);
   ELSIF (funCode = listDfBackup) THEN
      listGetDatafileBackup(rcvRec);
   ELSIF (funCode = listAlBackup) THEN
      listGetArchivedLogBackup(rcvRec);
   ELSIF (funCode = listDfProxy) THEN
      listGetProxyDatafile(rcvRec);
   ELSIF (funCode = getRecovAction) THEN
      callAgain := getRecoveryAction(rcvRec);
   ELSIF (funCode = getAlBackup) THEN
      rc := getArchivedLogBackup(rcvRec);
      callAgain := FALSE#;
   ELSIF (funCode = listAlCopy) THEN
      listGetArchivedLogCopy(rcvRec);
   ELSE
      raise_application_error(-20999, 'getRcvRec: unknown funCode: ' ||
         to_char(funCode));
   END IF;

   IF (debug) THEN
      printRcvRec(rcvRec);
   END IF;   

   RETURN rc;
END getRcvRec;

--------------------------
-- Datafile Translation --
--------------------------

------------------------------ translateDatabase ------------------------------
PROCEDURE translateDatabase(
   sinceUntilSCN IN number DEFAULT NULL)
IS
   fromSCN    number;
   toSCN      number;
BEGIN
   validateState(getDatafileCursor);

   IF (untilSCN is NULL) THEN
      -- This range means:  all datafiles that exist now
      fromSCN := MAXSCNVAL;
      toSCN   := MAXSCNVAL;
   ELSE					-- an until clause is in effect
      fromSCN := untilSCN;
      IF (sinceUntilSCN = TRUE#) THEN
         -- This flag means the caller wants datafiles that existed
         -- since the until SCN.  So the translation range is:
         -- untilSCN...MAXSCNVAL
         toSCN := MAXSCNVAL;
      ELSE
         -- This is the normal case when an until clause is in effect.
         -- We want the datafiles that existed at the until SCN, but not
         -- any that were created after that.
         toSCN := fromSCN;
      END IF;
   END IF;

   OPEN translateDatabase_c(fromSCN, toSCN);

   getDatafileCursor       := 'translateDatabase';
   getDatafileNoRows.error := NULL;		-- error not possible
   skipTablespaceCount     := 0;
   getDatafileLast.dfNumber := NULL;		-- no last row yet
END translateDatabase;

-- Public procedure to add a tablespace to the skip list.

-------------------------------- skipTableSpace -------------------------------
PROCEDURE skipTableSpace(
   ts_name IN varchar2)
IS
BEGIN
   skipTablespaceCount := skipTablespaceCount + 1;
   skipTablespaceList(skipTablespaceCount) := ts_name;
END skipTableSpace;

----------------------------- translateTablespace -----------------------------
PROCEDURE translateTablespace(
   ts_name IN varchar2)
IS
BEGIN
   validateState(getDatafileCursor);

   OPEN translateTablespace_c(tsName => ts_name);

   getDatafileCursor       := 'translateTablespace';
   getDatafileNoRows.error := -20202;
   getDatafileNoRows.msg   := 'Tablespace does not exist';
   skipTablespaceCount     := 0;   
   getDatafileLast.dfNumber := NULL;		-- no last row yet
END translateTablespace;

------------------------------ translateDataFile ------------------------------
PROCEDURE translateDataFile(
   fname IN varchar2)
IS
BEGIN
   validateState(getDatafileCursor);

   OPEN translateDatafileName(fileName => fname);

   IF (untilSCN is NULL and untilTime is NULL) THEN
      getDatafileNoRows.error := -20201;
      getDatafileNoRows.msg   := 'Datafile does not exist';
   ELSE
      getDatafileNoRows.error := -20222;
      getDatafileNoRows.msg   := 
         'Datafile name does not exist or is ambiguous';
   END IF;

   getDatafileCursor   := 'translateDatafileName';
   skipTablespaceCount := 0;
   getDatafileLast.dfNumber := NULL;		-- no last row yet
END translateDatafile;

------------------------------ translateDataFile ------------------------------
PROCEDURE translateDataFile(
   fno IN number)
IS
BEGIN
   validateState(getDatafileCursor);

   OPEN translateDatafileNumber(fno => fno);

   getDatafileCursor   	   := 'translateDatafileNumber';
   getDatafileNoRows.error :=  -20201;
   getDatafileNoRows.msg   := 'Datafile does not exist';
   skipTablespaceCount     := 0;   
   getDatafileLast.dfNumber := NULL;		-- no last row yet
END translateDatafile;

------------------------------ translateDataFile ------------------------------
PROCEDURE translateDataFile(
   fno    IN number
  ,ckpscn IN number)
IS
BEGIN
   validateState(getDatafileCursor);

   OPEN translateDatafileCheckpoint(fno	   => fno,
				    ckpSCN => ckpscn);

   getDatafileCursor       := 'translateDatafileCheckpoint';
   getDatafileNoRows.error :=  -20201;
   getDatafileNoRows.msg   := 'Datafile does not exist';
   skipTablespaceCount     := 0;   
   getDatafileLast.dfNumber := NULL;		-- no last row yet
END translateDatafile;

----------------------------- translateAllDataFile ----------------------------
PROCEDURE translateAllDatafile
IS
BEGIN
   IF (translateAllDatafile_c%ISOPEN) THEN
      CLOSE translateAllDatafile_c;
   END IF;

   OPEN translateAllDatafile_c;

   getDatafileCursor       := 'translateAllDatafile';
   getDatafileNoRows.error := NULL;		-- error not possible
   skipTablespaceCount     := 0;
   getDatafileLast.dfNumber := NULL;		-- no last row yet
END;

-- Main getDatafile routine

--------------------------------- getDatafile ---------------------------------
PROCEDURE getDatafile(
   dfRec     OUT dfRec_t
  ,oldClient IN  boolean DEFAULT FALSE)
IS
   getDatafileRowcount	number;
   local		dfRec_t;
BEGIN
<<nextRow>>
   IF (getDatafileCursor = 'translateDatabase') THEN
      FETCH translateDatabase_c
       INTO local;

      IF (translateDatabase_c%NOTFOUND) THEN
         -- Save rowcount before closing cursor
         getDatafileRowcount := translateDatabase_c%ROWCOUNT;
         CLOSE translateDatabase_c;
      END IF;
   ELSIF (getDatafileCursor = 'translateAllDatafile') THEN
      FETCH translateAllDatafile_c
       INTO local;

      IF (translateAllDatafile_c%NOTFOUND) THEN
         -- Save rowcount before closing cursor
         getDatafileRowcount := translateAllDatafile_c%ROWCOUNT;
         CLOSE translateAllDatafile_c;
      END IF;
   ELSIF (getDatafileCursor = 'translateTablespace') THEN
      FETCH translateTablespace_c
       INTO local;

      IF (translateTablespace_c%NOTFOUND) THEN
         -- Save rowcount before closing cursor
         getDatafileRowcount := translateTablespace_c%ROWCOUNT;
         CLOSE translateTablespace_c;
      END IF;
   ELSIF (getDatafileCursor = 'translateDatafileName') THEN
      FETCH translateDatafileName
       INTO local;

      IF (translateDatafileName%NOTFOUND) THEN
         -- Save rowcount before closing cursor
         getDatafileRowcount := translateDatafileName%ROWCOUNT;
         CLOSE translateDatafileName;
      END IF;

      IF (oldClient) THEN
         -- We won't be called again, so close the cursor now.
         IF (translateDatafileName%ISOPEN) THEN
            CLOSE translateDatafileName;
         END IF;
         getDatafileCursor := NULL;
      END IF;   
   ELSIF (getDatafileCursor = 'translateDatafileNumber') THEN
      FETCH translateDatafileNumber
       INTO local;

      IF (translateDatafileNumber%NOTFOUND) THEN
         -- Save rowcount before closing cursor
         getDatafileRowcount := translateDatafileNumber%ROWCOUNT;
         CLOSE translateDatafileNumber;
      END IF;

      IF (oldClient) THEN
         -- We won't be called again, so close the cursor now.
         IF (translateDatafileNumber%ISOPEN) THEN
            CLOSE translateDatafileNumber;
         END IF;
         getDatafileCursor := NULL;
      END IF;   
   ELSIF (getDatafileCursor = 'translateDatafileCheckpoint') THEN
      FETCH translateDatafileCheckpoint
       INTO local;

      IF (translateDatafileCheckpoint%NOTFOUND) THEN
         -- Save rowcount before closing cursor
         getDatafileRowcount := translateDatafileCheckpoint%ROWCOUNT;
         CLOSE translateDatafileCheckpoint;
      END IF;

      IF (oldClient) THEN
         -- We won't be called again, so close the cursor now.
         IF (translateDatafileCheckpoint%ISOPEN) THEN
            CLOSE translateDatafileCheckpoint;
         END IF;
         getDatafileCursor := NULL;
      END IF;   
   ELSE
      raise_application_error(-20204, 'Translation not started');
   END IF;

   IF (getDatafileRowcount IS NOT NULL) THEN	-- if %NOTFOUND
      getDatafileCursor := NULL;		-- we closed it above
      IF (getDatafileRowcount = 0 AND
          getDatafileNoRows.error IS NOT NULL) THEN
         -- Signal the appropriate error.
         raise_application_error(getDatafileNoRows.error,
                                 getDatafileNoRows.msg);
      ELSE
         RAISE no_data_found;			-- signal end-of-fetch
      END IF;
   END IF;

   IF (skipTablespace(local.tsName)) THEN
      GOTO nextRow;
   END IF;

   -- This isn't needed right now, but I am leaving it in because
   -- it can't hurt.

   IF (getDatafileLast.dfNumber = local.dfNumber AND
       getDatafileLast.dfCreationSCN = local.dfCreationSCN) THEN
      GOTO nextRow;
   END IF;
   getDatafileLast := local;

   dfRec := local;				-- set OUT mode arg
END getDatafile;

-- Public procedure for 8.1.x and prior releases.

--------------------------------- getDatafile ---------------------------------
PROCEDURE getDatafile(
   file#         		OUT number
  ,crescn        		OUT number
  ,creation_time 		OUT date
  ,fname         		OUT varchar2
  ,ts_name       		OUT varchar2
  ,status        		OUT number
  ,blksize       		OUT number
  ,kbytes        		OUT number
  ,blocks			OUT number
  ,unrecoverable_change# 	OUT number
  ,stop_change# 		OUT number
  ,read_only 			OUT number)
IS
   dfRec	dfRec_t;
BEGIN
   getDatafile(dfRec, oldClient => TRUE);

   file#                 := dfRec.dfNumber;
   crescn                := dfRec.dfCreationSCN;
   creation_time         := dfRec.dfCreationTime;
   fname                 := dfRec.fileName;
   ts_name               := dfRec.tsName;
   status                := 0; -- this is kccfesta, which we don't have
   blksize               := dfRec.blockSize;
   kbytes                := dfRec.kbytes;
   blocks                := dfRec.blocks;
   unrecoverable_change# := 0; -- this is kccfeurs which isn't kept in rcvcat
   stop_change#          := dfRec.stopSCN;
   read_only             := dfRec.readOnly;
EXCEPTION
   WHEN no_data_found THEN
      -- This is end-of-fetch.
      file# := NULL;
END getDatafile;

----------------------------
-- Online Log Translation --
----------------------------

----------------------------- translateOnlineLogs -----------------------------
PROCEDURE translateOnlineLogs
IS
BEGIN
   IF (translateOnlineLogs_c%ISOPEN) THEN
      validateState('you bozo');		-- raise the error
   END IF;

   OPEN translateOnlineLogs_c;

END translateOnlineLogs;

--------------------------------- getOnlineLog --------------------------------
PROCEDURE getOnlineLog(
   fname	OUT varchar2
  ,thread# 	OUT number
  ,group# 	OUT number)
IS
BEGIN
   FETCH translateOnlineLogs_c
    INTO thread#, group#, fname;
   IF (translateOnlineLogs_c%NOTFOUND) THEN
      CLOSE translateOnlineLogs_c;
      fname := NULL;				-- indicate end-of-fetch
      RETURN;
   END IF;
END getOnlineLog;

------------------------------
-- Archived Log Translation --
------------------------------

-------------------------------- getArchivedLog -------------------------------
PROCEDURE getArchivedLog(
   alRec       OUT alRec_t,
   closeCursor IN  boolean DEFAULT FALSE)
IS
   getArchivedLogRowcount	number;
   local			alRec_t;
BEGIN

<<nextRow>>

   IF (getArchivedLogCursor = 'translateArcLogKey') THEN
      FETCH translateArcLogKey
       INTO local;
      IF (translateArcLogKey%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogKey%ROWCOUNT;
         CLOSE translateArcLogKey;
      END IF;
      IF (closeCursor AND translateArcLogKey%ISOPEN) THEN
         CLOSE translateArcLogKey;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogName') THEN
      FETCH translateArcLogName
       INTO local;
      IF (translateArcLogName%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogName%ROWCOUNT;
         CLOSE translateArcLogName;
      END IF;
      IF (closeCursor AND translateArcLogName%ISOPEN) THEN
         CLOSE translateArcLogName;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogSeqRange') THEN
      FETCH translateArcLogSeqRange
       INTO local;
      IF (translateArcLogSeqRange%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogSeqRange%ROWCOUNT;
         CLOSE translateArcLogSeqRange;
      END IF;
      IF (closeCursor AND translateArcLogSeqRange%ISOPEN) THEN
         CLOSE translateArcLogSeqRange;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogSeqRange2') THEN
      FETCH translateArcLogSeqRange2
       INTO local;
      IF (translateArcLogSeqRange2%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogSeqRange2%ROWCOUNT;
         CLOSE translateArcLogSeqRange2;
      END IF;
      IF (closeCursor AND translateArcLogSeqRange2%ISOPEN) THEN
         CLOSE translateArcLogSeqRange2;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogTimeRange') THEN
      FETCH translateArcLogTimeRange
       INTO local;
      IF (translateArcLogTimeRange%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogTimeRange%ROWCOUNT;
         CLOSE translateArcLogTimeRange;
      END IF;
      IF (closeCursor AND translateArcLogTimeRange%ISOPEN) THEN
         CLOSE translateArcLogTimeRange;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogTimeRange2') THEN
      FETCH translateArcLogTimeRange2
       INTO local;
      IF (translateArcLogTimeRange2%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogTimeRange2%ROWCOUNT;
         CLOSE translateArcLogTimeRange2;
      END IF;
      IF (closeCursor AND translateArcLogTimeRange2%ISOPEN) THEN
         CLOSE translateArcLogTimeRange2;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogSCNRange') THEN
      FETCH translateArcLogSCNRange
       INTO local;
      IF (translateArcLogSCNRange%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogSCNRange%ROWCOUNT;
         CLOSE translateArcLogSCNRange;
      END IF;
      IF (closeCursor AND translateArcLogSCNRange%ISOPEN) THEN
         CLOSE translateArcLogSCNRange;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogSCNRange2') THEN
      FETCH translateArcLogSCNRange2
       INTO local;
      IF (translateArcLogSCNRange2%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogSCNRange2%ROWCOUNT;
         CLOSE translateArcLogSCNRange2;
      END IF;
      IF (closeCursor AND translateArcLogSCNRange2%ISOPEN) THEN
         CLOSE translateArcLogSCNRange2;
      END IF;
   ELSIF (getArchivedLogCursor = 'translateArcLogPattern') THEN
      FETCH translateArcLogPattern
       INTO local;
      IF (translateArcLogPattern%NOTFOUND) THEN
         getArchivedLogRowcount := translateArcLogPattern%ROWCOUNT;
         CLOSE translateArcLogPattern;
      END IF;
      IF (closeCursor AND translateArcLogPattern%ISOPEN) THEN
         CLOSE translateArcLogPattern;
      END IF;
   ELSE
      raise_application_error(-20204, 'Translation not started');
   END IF;

   IF (closeCursor) THEN
      getArchivedLogCursor := NULL;
   END IF;

   IF (getArchivedLogRowcount IS NOT NULL) THEN
      getArchivedLogCursor := NULL;		-- we closed it above
      getArchivedLogLast   := NULL;		-- clear for next time
      IF (getArchivedLogRowcount = 0 AND
          getArchivedLogNoRows.error IS NOT NULL) THEN
	 -- Signal the appropriate error.
         raise_application_error(getArchivedLogNoRows.error,
			         getArchivedLogNoRows.msg);
      ELSE
         RAISE no_data_found;			-- signal end-of-fetch
      END IF;
   END IF;
   
  -- Eliminate duplicate archived logs unless getArchivedLogDuplicates is true

   IF (getArchivedLogDuplicates = FALSE# AND	-- if don't want duplicates
       local.thread   = getArchivedLogLast.thread AND
       local.sequence = getArchivedLogLast.sequence AND
       local.lowSCN   = getArchivedLogLast.lowSCN) THEN
      GOTO nextRow;
   END IF;

   -- If stamp is -1, then this came from the brl table.  Change stamp to
   -- be null since -1 is not a legal stamp value.
   IF (local.stamp = -1) THEN
      local.stamp := NULL;
   END IF;

   getArchivedLogLast := local;
   alRec := local;				-- set OUT mode arg
END getArchivedLog;

--------------------------- translateArchivedLogKey ---------------------------
PROCEDURE translateArchivedLogKey(
   alKey 	IN number)
IS
BEGIN
   validateState(getArchivedLogCursor);

   OPEN translateArcLogKey(alKey => alKey);

   getArchivedLogCursor       := 'translateArcLogKey';
   getArchivedLogDuplicates   := NULL;
   getArchivedLogNoRows.error := -20240;
   getArchivedLogNoRows.msg   := 'Archived log does not exist';
END translateArchivedLogKey;

-- Obsolete as of 8.2

--------------------------- translateArchivedLogKey ---------------------------
PROCEDURE translateArchivedLogKey(
   al_key 	IN number
  ,available 	IN number		-- ignored
  ,unavailable 	IN number		-- ignored
  ,deleted 	IN number		-- ignored
  ,online 	IN number		-- ignored
  ,recid 	OUT number
  ,stamp 	OUT number
  ,thread# 	OUT number
  ,sequence# 	OUT number
  ,low_scn 	OUT number
  ,reset_scn 	OUT number
  ,block_size 	OUT number
  ,fname 	OUT varchar2)
IS
   alRec	alRec_t;
BEGIN
   translateArchivedLogKey(alKey => al_key);

   getArchivedLog(alRec       => alRec,
                  closeCursor => TRUE);

   recid	:= alRec.recid;
   stamp	:= alRec.stamp;
   thread#	:= alRec.thread;
   sequence#	:= alRec.sequence;
   low_scn	:= alRec.lowSCN;
   reset_scn	:= alRec.rlgSCN;
   block_size	:= alRec.blockSize;
   fname	:= alRec.fileName;
END translateArchivedLogKey;

-- Translate archived log name.  The archived logs are ordered by
-- stamp in order to return most recently created log first, because it
-- most likely to exist.  Note that archived logs that belong to any
-- incarnation of the current database are returned.

--------------------------- translateArchivedLogName --------------------------
PROCEDURE translateArchivedLogName(
   fname 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number)
IS
BEGIN
   validateState(getArchivedLogCursor);

   OPEN translateArcLogName(fname	=> fname,
        		    available   => available,
			    unavailable => unavailable,
			    deleted     => deleted,
			    online	=> online);

   getARchivedLogCursor       := 'translateArcLogName';
   getArchivedLogDuplicates   := duplicates;
   getArchivedLogNoRows.error := -20240;
   getArchivedLogNoRows.msg   := 'Archived log does not exist';
END translateArchivedLogName;

-- Translate an archived log sequence range.  The archived logs are ordered
-- by thread#, first_change#, sequence# to make detection of "missing"
-- archived logs in the range easy and to return the logs in the order that
-- recovery will apply them.  Note that sequence number is reset if the
-- controlfile is recreated so first change must be before sequence# number
-- in the order by clause.

------------------------- translateArchivedLogSeqRange ------------------------
PROCEDURE translateArchivedLogSeqRange(
   thread# 	IN number
  ,fromseq# 	IN number
  ,toseq# 	IN number
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number) IS
BEGIN
   validateState(getArchivedLogCursor);

   IF (thread# is NULL) THEN
      raise_application_error(-20210, 'Thread# is missing');
   END IF;

   IF (deleted = TRUE# AND pattern IS NULL) THEN
      -- Use the cursor that also looks at backup redo logs.
      -- The online parameter is assumed to be FALSE#.  Available and
      -- unavailable are ignored.  Since we are looking at backup redo logs,
      -- pattern cannot be implemented here, so we use this cursor only if
      -- pattern is null.
      OPEN translateArcLogSeqRange2(thread#     => thread#,
                                    fromseq#    => fromseq#,
	   			    toseq#      => toseq#);
      getArchivedLogCursor       := 'translateArcLogSeqRange2';
   ELSE
      OPEN translateArcLogSeqRange(thread#     => thread#,
                                   fromseq#    => fromseq#,
	   			   toseq#      => toseq#,
		         	   pattern     => pattern,
				   available   => available,
				   unavailable => unavailable,
				   deleted     => deleted,
				   online      => online);
      getArchivedLogCursor       := 'translateArcLogSeqRange';
   END IF;

   getArchivedLogDuplicates   := duplicates;
   getArchivedLogNoRows.error := -20242;
   getArchivedLogNoRows.msg   := 'No archived logs in the range specified';
END translateArchivedLogSeqRange;

-- Translate an archived log time range

------------------------ translateArchivedLogTimeRange ------------------------
PROCEDURE translateArchivedLogTimeRange(
   thread# 	IN number
  ,fromTime 	IN date
  ,toTime 	IN date
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number) IS
BEGIN
   validateState(getArchivedLogCursor);

   IF (deleted = TRUE# AND pattern IS NULL) THEN
      -- Use the cursor that also looks at backup redo logs.
      -- The online parameter is assumed to be FALSE#.  Available and
      -- unavailable are ignored.  Since we are looking at backup redo logs,
      -- pattern cannot be implemented here, so we use this cursor only if
      -- pattern is null.
      OPEN translateArcLogTimeRange2(thread#     => thread#,
                                     fromTime    => fromTime,
                                     toTime      => toTime);
      getArchivedLogCursor       := 'translateArcLogTimeRange2';
   ELSE
      OPEN translateArcLogTimeRange(thread#     => thread#,
                                    fromTime    => fromTime,
                                    toTime      => toTime,
                                    pattern     => pattern,
                                    available   => available,
                                    unavailable => unavailable,
                                    deleted     => deleted,
                                    online      => online);
      getArchivedLogCursor       := 'translateArcLogTimeRange';
   END IF;

   getArchivedLogDuplicates   := duplicates;
   getArchivedLogNoRows.error := -20242;
   getArchivedLogNoRows.msg   := 'No archived logs in the range specified';
END translateArchivedLogTimeRange;

-- Translate an archived log scn range

------------------------- translateArchivedLogSCNRange ------------------------
PROCEDURE translateArchivedLogSCNRange(
   thread# 	IN number
  ,fromSCN 	IN number
  ,toSCN 	IN number
  ,pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number)
IS
   adjusted_toSCN number;
BEGIN
   validateState(getArchivedLogCursor);

   IF (untilTime IS NULL) THEN
      -- Adjust the toSCN to take the until SCN into account.  The untilSCN
      -- was either specified directly by the user, or it was computed from
      -- the until logseq.  In either case, it is the exact SCN at which 
      -- recovery will stop.
      adjusted_toSCN := least(toSCN, nvl(untilSCN, toSCN));
   ELSE
      -- Leave the toSCN alone.  We cannot adjust the toSCN using
      -- the untilSCN here because the until SCN was estimated by
      -- computeUntilSCN(), and this procedure deliberately estimates the
      -- SCN low.  We cannot tolerate a low estimated SCN here because
      -- then we might filter out a log that will be needed by recovery.
      -- So instead, we pass the untilTime to the cursor.
      adjusted_toSCN := toSCN;
   END IF;

   -- 8.1.6 and later always make sure that the toSCN is greater than the
   -- fromSCN, but 8.1.5 and earlier will call us with fromSCN==toSCN.
   -- The query will fail if they are equal, so make the toSCN greater than
   -- the fromSCN.  It is also possible that adjusting by the untilSCN above
   -- made the fromSCN==toSCN.
   IF (adjusted_toSCN <= fromSCN) THEN
      adjusted_toSCN := fromSCN+1;
   END IF;

   IF (deleted = TRUE# AND pattern IS NULL) THEN
      -- Use the cursor that also looks at backup redo logs.
      -- The online parameter is assumed to be TRUE#.  Available and
      -- unavailable are ignored.  Since we are looking at backup redo logs,
      -- pattern cannot be implemented here, so we use this cursor only if
      -- pattern is null.
      OPEN translateArcLogSCNRange2(thread#     => thread#,
                                    fromSCN     => fromSCN,
                                    toSCN       => adjusted_toSCN,
                                    toTime      => untilTime);
      getArchivedLogCursor       := 'translateArcLogSCNRange2';
      deb('translateArchivedLogSCNRange: using cursor 2 fromSCN=' ||
          to_char(fromSCN) || ' toSCN=' || to_char(adjusted_toSCN));
   ELSE
      OPEN translateArcLogSCNRange(thread#     => thread#,
                                   fromSCN     => fromSCN,
                                   toSCN       => adjusted_toSCN,
                                   pattern     => pattern,
                                   available   => available,
                                   unavailable => unavailable,
                                   deleted     => deleted,
                                   online      => online);
      getArchivedLogCursor       := 'translateArcLogSCNRange';
   END IF;

   getArchivedLogDuplicates   := duplicates;
   getArchivedLogNoRows.error := -20242;
   getArchivedLogNoRows.msg   := 'No archived logs in the range specified';
END translateArchivedLogSCNRange;

-- Translate an archived log name pattern

------------------------- translateArchivedLogPattern -------------------------
PROCEDURE translateArchivedLogPattern(
   pattern 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,online 	IN number
  ,duplicates 	IN number) IS
BEGIN
   validateState(getArchivedLogCursor);

   OPEN translateArcLogPattern(pattern     => pattern,
                               available   => available,
                               unavailable => unavailable,
                               deleted     => deleted,
                               online      => online);

   getArchivedLogCursor       := 'translateArcLogPattern';
   getArchivedLogDuplicates   := duplicates;
   getArchivedLogNoRows.error := -20242;
   getArchivedLogNoRows.msg   := 'No archived logs in the range specified';
END translateArchivedLogPattern;

-------------------------- translateArchivedLogCancel -------------------------
PROCEDURE translateArchivedLogCancel IS
BEGIN
   IF (getArchivedLogCursor = 'translateArcLogKey') THEN
      CLOSE translateArcLogKey;
   ELSIF (getArchivedLogCursor = 'translateArcLogName') THEN
      CLOSE translateArcLogName;
   ELSIF (getArchivedLogCursor = 'translateArcLogSeqRange') THEN
      CLOSE translateArcLogSeqRange;
   ELSIF (getArchivedLogCursor = 'translateArcLogTimeRange') THEN
      CLOSE translateArcLogTimeRange;
   ELSIF (getArchivedLogCursor = 'translateArcLogSCNRange') THEN
      CLOSE translateArcLogSCNRange;
   ELSIF (getArchivedLogCursor = 'translateArcLogPattern') THEN
      CLOSE translateArcLogPattern;
   END IF;
   getArchivedLogCursor := NULL;		-- we closed it above
END translateArchivedLogCancel;

-- This public procedure for 8.1.5 and previous releases.  Recid will be
-- null to indicate end-of-fetch.  That was a bad interface design, but
-- we are stuck with it now.

-------------------------------- getArchivedLog -------------------------------
PROCEDURE getArchivedLog(
   recid	OUT number
  ,stamp	OUT number
  ,thread# 	OUT number
  ,sequence#	OUT number
  ,low_scn	OUT number
  ,nxt_scn	OUT number
  ,fname	OUT varchar2
  ,reset_scn	OUT number
  ,block_size	OUT number
  ,blocks	OUT number) IS

   alRec alRec_t;
BEGIN
<<retry>>
   getArchivedLog(alRec);

   -- A null recid means this record came from a cursor which looked at the brl
   -- table, which happens when the DELETED parmameter is TRUE#.  There is
   -- no recid, stamp, or filename in this alRec.  So even though they asked
   -- for deleted logs, do not pass this one back as it would cause problems.
   IF (alRec.recid IS NULL) THEN
      GOTO retry;
   END IF;

   -- Return OUT mode args
   recid      := alRec.recid;
   stamp      := nvl(alRec.stamp, 0);	-- no null indicator before 8.1.6
   thread#    := alRec.thread;
   sequence#  := alRec.sequence;
   low_scn    := alRec.lowSCN;
   nxt_scn    := alRec.nextSCN;
   fname      := nvl(alRec.fileName, 'null');	-- no null indicator
   reset_scn  := alRec.rlgSCN;
   block_size := alRec.blockSize;
   blocks     := alRec.blocks;

EXCEPTION
   WHEN no_data_found THEN
      recid := NULL;			-- indicate end-of-fetch
      stamp := NULL;
END getArchivedLog;

---------------------------------
-- Controlfilecopy Translation --
---------------------------------

-- Translate a controlfilecopy name.

------------------------- translateControlFileCopyName ------------------------
PROCEDURE translateControlFileCopyName(
   fname 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,duplicates 	IN number) IS
BEGIN
   -- Replaces ccf_name
   OPEN findControlfileCopy(pattern            => fname,
                            currentIncarnation => FALSE#,
                            available          => available,
                            unavailable        => unavailable);

   -- Initialize singleRow variable
   IF (duplicates = FALSE#) THEN
      getControlFileCopySingleRow := TRUE;
   ELSE
      getControlFileCopySingleRow := FALSE;
   END IF;
END translateControlFileCopyName;

------------------------------ getControlFileCopy -----------------------------
PROCEDURE getControlFileCopy(
   rcvRec	IN OUT rcvRec_t)
IS
   getControlFileCopyRowcount	number;
BEGIN
   IF (NOT findControlfileCopy%ISOPEN) THEN
      raise_application_error(-20204, 'Translation not started');
   END IF;

<<nextRow>>
   FETCH findControlfileCopy
    INTO rcvRec;

   IF (findControlfileCopy%NOTFOUND) THEN

      -- Save rowcount before closing cursor
      getControlFileCopyRowcount := findControlfileCopy%ROWCOUNT;
      CLOSE findControlfileCopy;

      IF (getControlFileCopyRowcount = 0) THEN
         raise_application_error(-20220, 'Controlfile copy does not exist');
      ELSE
         RAISE no_data_found;			-- signal end-of-fetch
      END IF;
   END IF;

   IF (getControlFileCopySingleRow = TRUE AND
       findControlfileCopy%ROWCOUNT > 1) THEN
      -- We only want a single row, and we've already returned 1 row, so
      -- just treat this as end-of-fetch
      CLOSE findControlfileCopy;
      RAISE no_data_found;
   END IF;
END getControlFileCopy;

-- Obsolete as of 8.1.6

------------------------------ getControlFileCopy -----------------------------
PROCEDURE getControlFileCopy(
   recid 	OUT number
  ,stamp 	OUT number
  ,reset_scn 	OUT number
  ,ckp_scn  	OUT number
  ,block_size	OUT number)
IS
   rcvRec	 		rcvRec_t;
BEGIN
   getControlFileCopy(rcvRec);

   recid      := rcvRec.recid_con;
   stamp      := rcvRec.stamp_con;
   reset_scn  := rcvRec.rlgSCN_act;
   ckp_scn    := rcvRec.toSCN_act;
   block_size := rcvRec.blockSize_con;
EXCEPTION
   WHEN no_data_found THEN
      -- This just means end-of-fetch
      recid := NULL;			-- signal end-of-fetch to caller
END getControlFileCopy;

------------------------------
-- Datafilecopy Translation --
------------------------------

------------------------------- getDataFileCopy -------------------------------
PROCEDURE getDataFileCopy(
   rcvRec       OUT rcvRec_t
  ,closeCursor IN  boolean DEFAULT FALSE)
IS
   getDataFileCopyRowcount	number;
   local			rcvRec_t;
BEGIN

<<nextRow>>

   IF (getDatafileCopyCursor = 'findDatafileCopyKey') THEN
      FETCH findDatafileCopyKey
       INTO local;

      IF (findDatafileCopyKey%NOTFOUND) THEN
         getDataFileCopyRowcount := findDatafileCopyKey%ROWCOUNT;
         CLOSE findDatafileCopyKey;
      END IF;

      IF (closeCursor AND findDatafileCopyKey%ISOPEN) THEN
         CLOSE findDatafileCopyKey;
      END IF;
   ELSIF (getDatafileCopyCursor = 'findDatafileCopy') THEN
      IF (getDataFileCopySingleRow = TRUE AND
          findDatafileCopy%ROWCOUNT > 1) THEN
         -- We only want a single row, and we've already returned 1 row, so
         -- just treat this as end-of-fetch
         CLOSE findDatafileCopy;
         RAISE no_data_found;
      END IF;

      FETCH findDatafileCopy
       INTO local;

      IF (findDatafileCopy%NOTFOUND) THEN
         getDataFileCopyRowcount := findDatafileCopy%ROWCOUNT;
         CLOSE findDatafileCopy;
      END IF;

      IF (closeCursor AND findDatafileCopy%ISOPEN) THEN
         CLOSE findDatafileCopy;
      END IF;
   ELSE
      raise_application_error(-20204, 'Translation not started');
   END IF;

   IF (closeCursor) THEN
      getDatafileCopyCursor := NULL;
   END IF;

   IF (getDataFileCopyRowcount IS NOT NULL) THEN
      getDatafileCopyCursor := NULL;
      IF (getDataFileCopyRowcount = 0 AND
          getDatafileCopyNoRows.error IS NOT NULL) THEN
         raise_application_error(getDatafileCopyNoRows.error,
                                 getDatafileCopyNoRows.msg);
      ELSE
         RAISE no_data_found;			-- signal end-of-fetch
      END IF;
   END IF;

   IF (getDataFileCopyDuplicates = FALSE# AND
       getDataFileCopyLast.dfNumber_obj = local.dfNumber_obj) THEN
      GOTO nextRow;
   END IF;

   getDataFileCopyLast := local;		-- save for duplicate filtering
   rcvRec := local;				-- set OUT mode arg
END getDataFileCopy;

--------------------------- translateDataFileCopyKey --------------------------
PROCEDURE translateDataFileCopyKey(
   cdf_key	IN number
  ,available	IN number
  ,unavailable 	IN number)
IS
BEGIN
   validateState(getDatafileCopyCursor);

   -- Replaces cursor that used to be in translateDataFileCopyKey
   OPEN findDataFileCopyKey(copyKey            => cdf_key,
                            available          => available,
                            unavailable        => unavailable);

   getDatafileCopyCursor       := 'findDatafileCopyKey';
   getDataFileCopyNoRows.error := -20230;
   getDataFileCopyNoRows.msg   := 'Datafile copy does not exist';
   getDataFileCopyDuplicates   := NULL;
   getDataFileCopySingleRow    := NULL;
END translateDatafileCopyKey;

-- Obsolete as of 8.1.6

--------------------------- translateDataFileCopyKey --------------------------
PROCEDURE translateDataFileCopyKey(
   cdf_key	IN number
  ,available	IN number
  ,unavailable 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,file# 	OUT number
  ,fname 	OUT varchar2
  ,reset_scn 	OUT number
  ,create_scn 	OUT number
  ,ckp_scn 	OUT number
  ,block_size 	OUT number
  ,blocks 	OUT number)
IS
   rcvRec	rcvRec_t;
BEGIN
   translateDataFileCopyKey(cdf_key	=> cdf_key,
                            available 	=> available,
			    unavailable	=> unavailable);

   getDataFileCopy(rcvRec      => rcvRec,
                   closeCursor => TRUE);

   recid	:= rcvRec.recid_con;
   stamp	:= rcvRec.stamp_con;
   file#	:= rcvRec.dfNumber_obj;
   fname	:= rcvRec.fileName_con;
   reset_scn	:= rcvRec.rlgSCN_act;
   create_scn	:= rcvRec.dfCreationSCN_obj;
   ckp_scn	:= rcvRec.toSCN_act;
   block_size	:= rcvRec.blockSize_con;
   blocks	:= rcvRec.blocks_con;
END translateDataFileCopyKey;

-- Translate a datafilecopy name.  The datafile copies are ordered by
-- stamp in order to return most recently created copy first, because it
-- most likely to exist.  Note that datafilecopies that belong to any
-- incarnation of the current database are returned.

-------------------------- translateDatafileCopyName --------------------------
PROCEDURE translateDatafileCopyName(
   fname       IN varchar2
  ,available   IN number
  ,unavailable IN number
  ,duplicates  IN number) IS
BEGIN
   validateState(getDatafileCopyCursor);

   -- Replaces cdf_name
   OPEN findDatafileCopy(pattern            => fname,
                         currentIncarnation => FALSE#,
                         available          => available,
                         unavailable        => unavailable);

   getDatafileCopyCursor            := 'findDatafileCopy';
   getDatafileCopyNoRows.error      := -20230;
   getDatafileCopyNoRows.msg        := 'Datafile copy does not exist';
   getDatafileCopyDuplicates        := duplicates;
   getDatafileCopyLast.dfNumber_obj := NULL;
   IF (duplicates = FALSE#) THEN
      getDatafileCopySingleRow := TRUE;
   ELSE
      getDatafileCopySingleRow := FALSE;
   END IF;
END translateDatafileCopyName;

-- Translate a datafilecopy tag.  The datafilecopies are ordered by file#
-- in order to make the duplicate elimination work and by descending
-- checkpoint scn to return most recent copy first.
-- ### only copies that belong to current database incarnation are returned,
-- We should change this in future to return datafile copies that belong
-- previous incarnations of the database.  The difficulty is that the recovery
-- catalog may also contain datafile copies that belong to "orphaned"
-- incarnation of the database.

--------------------------- translateDataFileCopyTag --------------------------
PROCEDURE translateDataFileCopyTag(
   tag 		IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,duplicates 	IN number) IS
BEGIN
   validateState(getDatafileCopyCursor);

   -- Replaces cdf_tag.  Note that we look only for ones in the current
   -- incarnation because that is what cdf_tag did.  If access to
   -- datafilecopies in other incarnations is required, it must be done by
   -- key or filename.
   OPEN findDataFileCopy(tag                => tag,
                         currentIncarnation => TRUE#,
                         available          => available,
                         unavailable        => unavailable);

   getDatafileCopyCursor            := 'findDatafileCopy';
   getDataFileCopyNoRows.error      := -20232;
   getDataFileCopyNoRows.msg        := 'Datafile copy tag does not match';
   getDataFileCopyDuplicates        := duplicates;
   getDataFileCopyLast.dfNumber_obj := NULL;
   getDataFileCopySingleRow         := FALSE;
END translateDataFileCopyTag;

-- Obsolete as of 8.1.6

------------------------------- getDataFileCopy -------------------------------
PROCEDURE getDataFileCopy(
   recid OUT number
  ,stamp OUT number
  ,file# OUT number
  ,fname OUT varchar2
  ,reset_scn OUT number
  ,create_scn OUT number
  ,ckp_scn OUT number
  ,block_size OUT number
  ,blocks OUT number)
IS
   rcvRec rcvRec_t;
BEGIN
   getDataFileCopy(rcvRec);

   recid	:= rcvRec.recid_con;
   stamp	:= rcvRec.stamp_con;
   file#        := rcvRec.dfNumber_obj;
   fname        := rcvRec.fileName_con;
   reset_scn	:= rcvRec.rlgSCN_act;
   create_scn   := rcvRec.dfCreationSCN_obj;
   ckp_scn	:= rcvRec.toSCN_act;
   block_size	:= rcvRec.blockSize_con;
   blocks       := rcvRec.blocks_con;
EXCEPTION
   WHEN no_data_found THEN
      recid := NULL;			-- signal end-of-fetch to client
END getDataFileCopy;

----------------------------
-- Proxy Copy Translation --
----------------------------

--------------------------------- getProxyCopy --------------------------------
PROCEDURE getProxyCopy(
   rcvRec      OUT rcvRec_t
  ,closeCursor IN  boolean DEFAULT FALSE)
IS
   getProxyCopyRowcount	number;
   dummy		rcvRec_t;
BEGIN
   IF (getProxyCopyCursor = 'findProxyCopy') THEN
      FETCH findProxyCopy
       INTO rcvRec;

      IF (findProxyCopy%NOTFOUND) THEN
         getProxyCopyRowcount := findProxyCopy%ROWCOUNT;
         CLOSE findProxyCopy;
      ELSE
         IF (getProxyCopyByHandle) THEN
            -- Make sure there is only 1 row
            FETCH findProxyCopy
             INTO dummy;
            IF (NOT findProxyCopy%NOTFOUND) THEN
               CLOSE findProxyCopy;
               raise_application_error(-20311, 'Ambiguous proxy copy handle');
            END IF;
         END IF;
      END IF;

      IF (closeCursor AND findProxyCopy%ISOPEN) THEN
         CLOSE findProxyCopy;
      END IF;
   ELSIF (getProxyCopyCursor = 'findProxyCopyKey') THEN
      FETCH findProxyCopyKey
       INTO rcvRec;

      IF (findProxyCopyKey%NOTFOUND) THEN
         getProxyCopyRowcount := findProxyCopyKey%ROWCOUNT;
         CLOSE findProxyCopyKey;
      END IF;

      IF (closeCursor AND findProxyCopyKey%ISOPEN) THEN
         CLOSE findProxyCopyKey;
      END IF;
   ELSE
      raise_application_error(-20204, 'Translation not started');
   END IF;

   IF (closeCursor) THEN
      getProxyCopyCursor := NULL;
   END IF;
   
   IF (getProxyCopyRowcount IS NOT NULL) THEN
      getProxyCopyCursor := NULL;
      IF (getProxyCopyRowcount = 0 AND
          getProxyCopyNoRows.error IS NOT NULL) THEN
         raise_application_error(getProxyCopyNoRows.error,
                                 getProxyCopyNoRows.msg);
      ELSE
         RAISE no_data_found;			-- signal end-of-fetch
      END IF;
   END IF;

END getProxyCopy;

---------------------------- translateProxyCopyKey ----------------------------
PROCEDURE translateProxyCopyKey(
   pc_key 	IN number
  ,deviceType 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,expired      IN number)
IS
BEGIN
   validateState(getProxyCopyCursor);

   OPEN findProxyCopyKey(key            => pc_key,
                         deviceType     => deviceType,
                         available      => available,
                         unavailable    => unavailable,
                         deleted        => deleted,
                         expired        => expired);
   getProxyCopyCursor       := 'findProxyCopyKey';
   getProxyCopyNoRows.error := -20310;
   getProxyCopyNoRows.msg   := 'proxy copy is missing';
   getProxyCopyByHandle     := FALSE;
END translateProxyCopyKey;

-- Obsolete as of 8.1.6

---------------------------- translateProxyCopyKey ----------------------------
PROCEDURE translateProxyCopyKey(
   pc_key 	IN number
  ,device_type 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,handle 	OUT varchar2)
IS
   rcvRec	rcvRec_t;
BEGIN
   translateProxyCopyKey(pc_key		=> pc_key,
			 deviceType	=> device_type,
 		 	 available	=> available,
 	                 unavailable    => unavailable,
          		 deleted	=> deleted,
                         expired 	=> unavailable);

   getProxyCopy(rcvRec      => rcvRec,
                closeCursor => TRUE);

   recid  := rcvRec.recid_con;
   stamp  := rcvRec.stamp_con;
   handle := rcvRec.fileName_con;
END translateProxyCopyKey;

--------------------------- translateProxyCopyHandle --------------------------
PROCEDURE translateProxyCopyHandle(
   handle 	IN varchar2
  ,deviceType 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,expired      IN number)
IS
BEGIN
   validateState(getProxyCopyCursor);

   OPEN findProxyCopy(handle     => handle,
                      deviceType  => deviceType,
                      available   => available,
                      unavailable => unavailable,
                      deleted     => deleted,
                      expired     => expired);
   getProxyCopyCursor       := 'findProxyCopy';
   getProxyCopyNoRows.error := -20310;
   getProxyCopyNoRows.msg   := 'proxy copy is missing';
   getProxyCopyByHandle     := TRUE;
END translateProxyCopyHandle;

-- Obsolete as of 8.1.6

--------------------------- translateProxyCopyHandle --------------------------
PROCEDURE translateProxyCopyHandle(
   handle 	IN varchar2
  ,device_type 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,deleted 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number)
IS
   rcvRec	rcvRec_t;
BEGIN
   translateProxyCopyHandle(handle	=> handle,
                            deviceType	=> device_type,
                            available   => available,
                            unavailable => unavailable,
                            deleted     => deleted,
                            expired  	=> unavailable);

   getProxyCopy(rcvRec      => rcvRec,
                closeCursor => TRUE);

   recid  := rcvRec.recid_con;
   stamp  := rcvRec.stamp_con;
END translateProxyCopyHandle;

-- Translate a proxy copy tag into a list of proxy copies

---------------------------- translateProxyCopyTag ----------------------------
PROCEDURE translateProxyCopyTag(
   tag         IN varchar2
  ,device_type IN varchar2
  ,available   IN number
  ,unavailable IN number
  ,deleted     IN number)
IS
BEGIN
   validateState(getProxyCopyCursor);
   OPEN findProxyCopy(tag            => tag,
                      deviceType     => device_type,
                      available      => available,
                      unavailable    => unavailable,
                      deleted        => deleted,
                      expired        => unavailable);
   getProxyCopyCursor       := 'findProxyCopy';
   getProxyCopyNoRows.error := -20310;
   getProxyCopyNoRows.msg   := 'no matching proxy copy found';
   getProxyCopyByHandle     := FALSE;
END translateProxyCopyTag;

-- Obsolete as of 8.1.6

--------------------------------- getProxyCopy --------------------------------
PROCEDURE getProxyCopy(
   recid 	OUT number
  ,stamp 	OUT number
  ,handle 	OUT varchar2)
IS
   rcvRec	rcvRec_t;
BEGIN
   getProxyCopy(rcvRec);
   recid  := rcvRec.recid_con;
   stamp  := rcvRec.stamp_con;
   handle := rcvRec.fileName_con;
EXCEPTION
   WHEN no_data_found THEN
      recid := NULL;			-- indicate end-of-fetch
END getProxyCopy;

------------------------------
-- Backup Piece Translation --
------------------------------

-------------------------------- getBackupPiece -------------------------------
PROCEDURE getBackupPiece(
   bpRec	OUT bpRec_t
  ,closeCursor  IN  boolean DEFAULT FALSE)
IS
   dummy	bpRec_t;
   local        bpRec_t;
   eof		boolean := FALSE;
BEGIN
   IF (getBackupPieceDuplicates = FALSE# AND
       getBackupPieceDeviceType IS NULL) THEN
      -- If duplicate filtering is requested, it means only 1 copy of each 
      -- piece is wanted.  The deviceType must be specified in this case.
      -- If not, then raise an exception.
      raise_application_error(-20000, 'deviceType must be specified');
   END IF;

<<nextRow>>

   IF (getBackupPieceCursor = 'findBackupPieceBpKey') THEN
      FETCH findBackupPieceBpKey
       INTO local;

      IF (findBackupPieceBpKey%NOTFOUND) THEN
         eof := TRUE;
         CLOSE findBackupPieceBpKey;
      END IF;

      IF (closeCursor AND findBackupPieceBpKey%ISOPEN) THEN
         CLOSE findBackupPieceBpKey;
      END IF;
   ELSIF (getBackupPieceCursor = 'findBackupPieceBsKey') THEN
      FETCH findBackupPieceBsKey
       INTO local;

      IF (findBackupPieceBsKey%NOTFOUND) THEN
         eof := TRUE;
         CLOSE findBackupPieceBsKey;
      END IF;

      IF (closeCursor AND findBackupPieceBsKey%ISOPEN) THEN
         CLOSE findBackupPieceBsKey;
      END IF;
   ELSIF (getBackupPieceCursor = 'findBackupPiece_c') THEN
      FETCH findBackupPiece_c
       INTO local;

      IF (findBackupPiece_c%NOTFOUND) THEN
         eof := TRUE;
         CLOSE findBackupPiece_c;
      ELSE
         IF (getBackupPieceByHandle) THEN
            -- Make sure we can only fetch 1 row.
            FETCH findBackupPiece_c
             INTO dummy;
            IF (NOT findBackupPiece_c%NOTFOUND) THEN
               CLOSE findBackupPiece_c;
               raise_application_error(-20261, 'Ambiguous backup piece handle');
            END IF;
         END IF;
      END IF;

      IF (closeCursor AND findBackupPiece_c%ISOPEN) THEN
         CLOSE findBackupPiece_c;
      END IF;
   ELSE
      raise_application_error(-20204, 'Translation not started');
   END IF;

   IF (closeCursor OR eof) THEN
      getBackupPieceCursor := NULL;
   END IF;

   IF (eof) THEN				-- if end of fetch
      -- If we've been requested to check that all pieces of the set
      -- have been found, then perform the check.
      IF (getBackupPieceDuplicates = FALSE#) THEN
         -- We don't want duplicates, which means duplicate filtering is
         -- in effect.  We already asserted above that a deviceType has
	 -- been specified, so we know filtering is possible.
         IF (getBackupPieceExpectedPieces IS NOT NULL AND
             getBackupPieceExpectedPieces <> getBackupPiecePieceCount) THEN
             raise_application_error(-20216, 'Backup piece is missing');
         END IF;
      END IF;
      IF (getBackupPiecePieceCount = 0 AND
          getBackupPieceNoRows.error IS NOT NULL) THEN
         raise_application_error(getBackupPieceNoRows.error,
                                 getBackupPieceNoRows.msg);
      ELSE
         RAISE no_data_found;
      END IF;
   END IF;

   IF (getBackupPieceDuplicates = FALSE#) THEN
      -- Client does not want duplicates, so lets filter out duplicate
      -- pieces.
      IF (local.pieceNumber = getBackupPieceLast.pieceNumber) THEN
         -- This is a duplicate piece
         GOTO nextRow;
      END IF;
   END IF;

   getBackupPieceLast := local;
   bpRec := local;				-- set OUT mode arg

   getBackupPiecePieceCount := getBackupPiecePieceCount + 1;
END getBackupPiece;

--------------------------- translateBackupPieceKey ---------------------------
PROCEDURE translateBackupPieceKey(
   key 		IN number
  ,available 	IN number DEFAULT TRUE#
  ,unavailable 	IN number DEFAULT TRUE#
  ,expired      IN number DEFAULT TRUE#)
IS
BEGIN
   findBackupPiece(bpKey       => key,
                   available   => available,
                   unavailable => unavailable,
                   expired     => expired);

   getBackupPieceNoRows.error   := -20260;
   getBackupPieceNoRows.msg     := 'Backup piece is missing';
END translateBackupPieceKey;

-- Obsolete as of 8.1.6

--------------------------- translateBackupPieceKey ---------------------------
PROCEDURE translateBackupPieceKey(
   bp_key 	IN number
  ,available 	IN number
  ,unavailable 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,handle 	OUT varchar2
  ,set_stamp 	OUT number
  ,set_count 	OUT number
  ,piece# 	OUT number)
IS
   bpRec	bpRec_t;
BEGIN
   -- Open cursor to lookup bp by primary key.  Treat expired backup pieces
   -- as if they were unavailable.  We do this only because the interface
   -- to translateBackupPieceKey was not enhanced to have an "expired"
   -- argument when status 'X' was introduced.  It should have been enhanced
   -- to have one, but since it was not, we have to assume the most
   -- reasonable default value.
   -- Replaces the cursor that was in the 8.1.x translateBackupPieceKey.

   translateBackupPieceKey(key         => bp_key,
                           available   => available,
                           unavailable => unavailable,
                           expired     => unavailable);

   getBackupPiece(bpRec       => bpRec,
                  closeCursor => TRUE);

   recid     := bpRec.recid;
   stamp     := bpRec.stamp;
   handle    := bpRec.handle;
   set_stamp := bpRec.setStamp;
   set_count := bpRec.setCount;
   piece#    := bpRec.pieceNumber;
   
END translateBackupPieceKey;

-------------------------- translateBackupPieceHandle -------------------------
PROCEDURE translateBackupPieceHandle(
   handle 	IN varchar2
  ,deviceType 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,expired      IN number)
IS
BEGIN
   findBackupPiece(handle      => handle,
                   deviceType  => deviceType,
                   available   => available,
                   unavailable => unavailable,
                   expired     => expired);
   getBackupPieceNoRows.error   := -20260;
   getBackupPieceNoRows.msg     := 'Backup piece is missing';
   getBackupPieceByHandle       := TRUE;
END translateBackupPieceHandle;

-- Obsolete as of 8.1.6

-------------------------- translateBackupPieceHandle -------------------------
PROCEDURE translateBackupPieceHandle(
   handle 	IN varchar2
  ,device_type 	IN varchar2
  ,available 	IN number
  ,unavailable 	IN number
  ,recid 	OUT number
  ,stamp 	OUT number
  ,set_stamp 	OUT number
  ,set_count 	OUT number
  ,piece# 	OUT number)
IS
   bpRec	bpRec_t;
BEGIN
   translateBackupPieceHandle(handle		=> handle,
			      deviceType	=> device_type,
			      available		=> available,
			      unavailable	=> unavailable,
			      expired		=> unavailable);

   getBackupPiece(bpRec       => bpRec,
                  closeCursor => TRUE);

   recid     := bpRec.recid;
   stamp     := bpRec.stamp;
   set_stamp := bpRec.setStamp;
   set_count := bpRec.setCount;
   piece#    := bpRec.pieceNumber;
END translateBackupPieceHandle;

-- Translate a backup piece tag into a list of backup pieces

--------------------------- translateBackupPieceTag ---------------------------
PROCEDURE translateBackupPieceTag(
   tag         IN varchar2
  ,available   IN number
  ,unavailable IN number
  ,expired     IN number DEFAULT NULL)
IS
   expiredFlag	number;
BEGIN
   IF (expired IS NULL) THEN
      -- If caller did not specify, then caller is probably pre-8.1.6 version
      -- of RMAN.  In that case, do what the 8.1 version did.
      expiredFlag := unavailable;
   ELSE
      expiredFlag := expired;
   END IF;

   findBackupPiece(tag         => tag,
                   available   => available,
                   unavailable => unavailable,
                   expired     => expiredFlag);
END translateBackupPieceTag;

-------------------------- translateBackupPieceBSKey --------------------------
PROCEDURE translateBackupPieceBSKey(
   key          IN number
  ,tag		IN varchar2 DEFAULT NULL
  ,deviceType	IN varchar2 DEFAULT NULL
  ,pieceCount	IN number DEFAULT NULL
  ,duplicates	IN number DEFAULT TRUE#
  ,copyNumber   IN number DEFAULT NULL
  ,available	IN number DEFAULT TRUE#
  ,unavailable  IN number DEFAULT FALSE#
  ,deleted      IN number DEFAULT FALSE#
  ,expired      IN number DEFAULT FALSE#)
IS
BEGIN
   findBackupPiece(bsKey       => key,
                   tag         => tag,
                   deviceType  => deviceType,
                   copyNumber  => copyNumber,
                   available   => available,
                   unavailable => unavailable,
                   expired     => expired);

   getBackupPieceDuplicates	:= duplicates;
   getBackupPieceExpectedPieces := pieceCount;
END translateBackupPieceBSKey;

-- Obsolete as of 8.1.6

---------------------------- translateBackupSetKey ----------------------------
PROCEDURE translateBackupSetKey(
   bs_key          IN  number
  ,device_type     IN  varchar2
  ,available       IN  number
  ,unavailable     IN  number
  ,deleted         IN  number
  ,duplicates      IN  number
  ,backup_type     OUT varchar2
  ,recid           OUT number
  ,stamp           OUT number
  ,set_stamp       OUT number
  ,set_count       OUT number
  ,bslevel         OUT number
  ,completion_time OUT date)
IS
   bsRec 	bsRec_t;
BEGIN
   findBackupSet(bsKey => bs_key,		-- Lookup backset by key
                 bsRec => bsRec);

   backup_type     := bsRec.bsType;
   recid           := bsRec.recid;
   stamp           := bsRec.stamp;
   set_stamp       := bsRec.setStamp;
   set_count       := bsRec.setCount;
   bslevel         := bsRec.level;
   completion_time := bsRec.compTime;

   -- Open cursor to lookup bp by backup set key.  Treat expired pieces
   -- as if they were unavailable.  We do this only because the interface
   -- to translateBackupSetKey was not enhanced to have an "expired"
   -- argument when status 'X' was introduced.  It should have been enhanced
   -- to have one, but since it was not, we have to assume the most
   -- reasonable default value.

   -- Replaces bsq1
   translateBackupPieceBSKey(key	 => bs_key,
			     deviceType	 => device_type,
                             pieceCount  => bsRec.pieceCount,
                             available   => available,
                             unavailable => unavailable,
                             deleted     => deleted,
                             expired     => unavailable);
   
   getBackupPieceDuplicates := duplicates;

   IF (device_type IS NULL) THEN
      -- The pre-8.1.6 RMAN is broken.  When doing a LIST (surprise surprise)
      -- it calls this routine with device_type NULL, but the duplicates
      -- flag is FALSE# (don't want duplicates).  This makes no sense because
      -- we can do duplicate filtering only when a device type is specified.
      -- The old version of this package tolerated this client behaviour, but
      -- this version is more strict and will raise an exception.  So to avoid
      -- getting an exception when the old RMAN is calling us, set the
      -- duplicates flag to TRUE#.  This package will then behave the same
      -- as the old one.
      getBackupPieceDuplicates := TRUE#;	-- yes, we want duplicates
   END IF;

   IF (getBackupPieceDuplicates = FALSE#) THEN
      -- If we don't want duplicate pieces, then we are probably planning
      -- to access the backup set, so tell getBackupPiece to perform the
      -- missing piece check.
      getBackupPieceExpectedPieces := bsRec.pieceCount;
   END IF;
END translateBackupSetKey;

-- Obsolete as of 8.1

---------------------------- translateBackupSetKey ----------------------------
PROCEDURE translateBackupSetKey(
   bs_key          IN  number
  ,device_type     IN  varchar2
  ,available       IN  number
  ,unavailable     IN  number
  ,deleted         IN  number
  ,duplicates      IN  number
  ,backup_type     OUT varchar2
  ,recid           OUT number
  ,stamp           OUT number)
IS
   set_stamp       number;
   set_count       number;
   bslevel         number;
   completion_time date;
BEGIN
   translateBackupSetKey(bs_key, device_type, available, unavailable, deleted,
                         duplicates, backup_type, recid, stamp, set_stamp,
                         set_count, bslevel, completion_time);
END translateBackupSetKey;

-- Obsolete as of 8.1.6

--------------------------- translateBackupSetRecid ---------------------------
PROCEDURE translateBackupSetRecid(
   recid       IN  number
  ,stamp       IN  number
  ,device_type IN  varchar2
  ,bs_key      OUT number
  ,bslevel     OUT number
  ,completed   OUT date)
IS
   bsRec		bsRec_t;
   pieceCount		number;
   validationCursor 	validateBackupSetCursor_t;
   validationRec	validBackupSetRec_t;
   gotRecord		number;
   duplicates_flag      number;
BEGIN
   findBackupSet(recid => recid,
                 stamp => stamp,
                 bsRec => bsRec);

   bs_key    := bsRec.key;
   bslevel   := bsRec.level;
   completed := bsRec.compTime;

   -- See if all pieces are available from the same copy#.  We already
   -- know that the backupset is valid because we wouldn't be here
   -- otherwise.
   -- NOTE:  device_type is null if doing REPORT OBSOLETE

   findValidBackupSet(backupSetRec 		=> bsRec,
                      tag			=> restoreTag,
                      deviceType		=> device_type,
                      available			=> TRUE#);

   gotRecord := getValidBackupSet(validBackupSetRec => validationRec);

   CLOSE findValidBackupSet_c;

   IF (gotRecord = FALSE#) THEN
      -- Allow a mix of copy#s.  It is probably the case that a backup piece
      -- is missing and getBackupPiece will raise that error.  I don't
      -- know how we could have gotten here in that case, but getBackupPiece
      -- will do the right thing.
      validationRec.copyNumber := NULL;
   END IF;
   
   IF (device_type IS NULL) THEN
      -- This is the REPORT OBSOLETE case
      duplicates_flag := TRUE#;
   ELSE
      -- This is the normal case (restore/recover)
      duplicates_flag := FALSE#;
   END IF;

   translateBackupPieceBSKey(key	 => bsRec.key,
                             tag         => validationRec.tag,
                             deviceType  => device_type,
                             pieceCount  => bsRec.pieceCount,
                             duplicates  => duplicates_flag,
                             copyNumber  => validationRec.copyNumber,
                             available   => TRUE#);
END translateBackupSetRecid;

-- Obsolete as of 8.1

--------------------------- translateBackupSetRecid ---------------------------
PROCEDURE translateBackupSetRecid(
   recid       IN  number
  ,stamp       IN  number
  ,device_type IN  varchar2)
IS
   bs_key    number;
   bslevel   number;
   completed date;
BEGIN
  translateBackupSetRecid(recid, stamp, device_type, bs_key, bslevel,
                          completed);
END translateBackupSetRecid;

-- Obsolete as of 8.1.6

-------------------------------- getBackupPiece -------------------------------
PROCEDURE getBackupPiece(
   recid      OUT number
  ,stamp      OUT number
  ,bpkey      OUT number
  ,set_stamp  OUT number
  ,set_count  OUT number
  ,piece#     OUT number
  ,copy#      OUT number
  ,status     OUT varchar2
  ,completion OUT date
  ,handle     OUT varchar2)
IS
   bpRec	bpRec_t;
BEGIN
   getBackupPiece(bpRec);

   recid	:= bpRec.recid;
   stamp	:= bpRec.stamp;
   bpkey	:= bpRec.key;
   set_stamp    := bpRec.setStamp;
   set_count	:= bpRec.setCount;
   piece#	:= bpRec.pieceNumber;
   copy#	:= bpRec.copyNumber;
   status	:= bpRec.status;
   completion	:= bpRec.compTime;
   handle	:= bpRec.handle;
EXCEPTION
   WHEN no_data_found THEN
      bpRec.recid := NULL;		-- indicate end-of-fetch to client
END getBackupPiece;

-- Obsolete as of 8.1

-------------------------------- getBackupPiece -------------------------------
PROCEDURE getBackupPiece(
   recid     OUT number
  ,stamp     OUT number
  ,set_stamp OUT number
  ,set_count OUT number
  ,piece#    OUT number
  ,handle    OUT varchar2)
IS
   bpRec	bpRec_t;
BEGIN
   getBackupPiece(bpRec);

   recid	:= bpRec.recid;
   stamp	:= bpRec.stamp;
   set_stamp    := bpRec.setStamp;
   set_count	:= bpRec.setCount;
   piece#	:= bpRec.pieceNumber;
   handle	:= bpRec.handle;
EXCEPTION
   WHEN no_data_found THEN
      bpRec.recid := NULL;		-- indicate end-of-fetch to client
END getBackupPiece;

----------------------------
-- Backup Set Translation --
----------------------------

---------------------------- translateBackupSetKey ----------------------------
PROCEDURE translateBackupSetKey(
   key	      IN  number
  ,bsRec      OUT bsRec_t)
IS
BEGIN
   findBackupSet(bsKey => key,
                 bsRec => bsRec);
END translateBackupSetKey;

------------------------
-- Controlfile Backup --
------------------------

----------------------------- getControlfileBackup ----------------------------
FUNCTION getControlfileBackup(
   rcvRec	OUT rcvRec_t)
RETURN number IS
   found   	boolean := FALSE;
   ccfrec  	rcvRec_t;
   bcfrec  	rcvRec_t;
   xcfrec  	rcvRec_t;
   devtype 	bp.device_type%TYPE;
   tag     	bp.tag%TYPE;
   val_rc 	binary_integer;
   rcvRecCursor	rcvRecCursor_t;
   validateCursor validateBackupSetCursor_t;
   validateRec  validBackupSetRec_t;
BEGIN
   validateState(null);

   ccfrec.toSCN_act := 0;
   bcfrec.toSCN_act := 0;
   xcfrec.toSCN_act := 0;

   IF (restoreSource = COPY OR restoreSource IS NULL) THEN
      -- Replaces ccfq
      OPEN findControlfileCopy(currentIncarnation => TRUE#,
 			       tag		  => restoreTag,
                               untilSCN	          => untilSCN,
                               available          => TRUE#,
                               unavailable        => FALSE#,
                               deleted            => FALSE#,
                               expired            => FALSE#);
 			    
      LOOP
         FETCH findControlfileCopy
          INTO ccfrec;
         EXIT WHEN findControlfileCopy%NOTFOUND;

	 deb('getControlfileBackup found a controlfilecopy:');
         printRcvRec(ccfRec);

         IF (NOT diskDevice) THEN
            -- remember that we found one available copy
            found := TRUE;
            ccfrec.toSCN_act := 0;		-- do not use this one
         END IF;
         EXIT;  -- always return the first copy returned by the query
      END LOOP;
      CLOSE findControlfileCopy;    
   END IF;

   IF (restoreSource = BACKUP OR restoreSource IS NULL) THEN
      -- Replaces bcfq
      OPEN findControlfileBackup_c(currentIncarnation => TRUE#,
                                   untilSCN	      => untilSCN);
      -- Find optimal backup set
      LOOP
         FETCH findControlfileBackup_c
          INTO bcfrec;
         EXIT WHEN findControlfileBackup_c%NOTFOUND;

	 deb('getControlfileBackup found a controlfile backup:');
         printRcvRec(bcfRec);

         val_rc :=
            validateBackupSet(backupSetRec           => bcfrec,
                              tag                    => restoreTag,
                              tagMatchRequired       => TRUE,
                              checkDeviceIsAllocated => TRUE,
                              available              => TRUE#,
                              unavailable            => FALSE#,
                              deleted                => FALSE#,
                              expired                => FALSE#,
                              validRec               => validateRec);

         IF (val_rc = SUCCESS) THEN
            bcfRec.tag_con        := validateRec.tag;
            bcfRec.deviceType_con := validateRec.deviceType;
            bcfRec.copyNumber_con := validateRec.copyNumber;
            -- Return the first one that we find
            EXIT;
         END IF;

         bcfrec.toSCN_act := 0;			-- do not use this one

         IF (val_rc = dbms_rcvman.AVAILABLE) THEN
            -- remember that we encountered at least one available backup
            found := TRUE;
         END IF;
      END LOOP;
      CLOSE findControlfileBackup_c;

      -- Replaces xcfq
      OPEN findControlfileProxyCopy(currentIncarnation => TRUE#,
 			            tag		       => restoreTag,
                                    untilSCN	       => untilSCN,
                                    available          => TRUE#,
                                    unavailable        => FALSE#,
                                    deleted            => FALSE#,
                                    expired            => FALSE#);

      -- Find optimal proxy copy
      LOOP
         FETCH findControlfileProxyCopy
          INTO xcfrec;
         EXIT WHEN findControlfileProxyCopy%NOTFOUND;

	 deb('getControlfileBackup found a controlfile proxy copy:');
         printRcvRec(xcfRec);

         IF (isDeviceTypeAllocated(xcfrec.deviceType_con)) THEN
            -- Return the first one that we find
            EXIT;
         END IF;

         -- remember that we encountered at least one available backup
         found := TRUE;

         xcfrec.toSCN_act := 0;			-- do not use this one
      END LOOP;
      CLOSE findControlfileProxyCopy;
   END IF;

   -- Return the backup or the copy depending on which one has the highest
   -- checkpoint scn.  In case the scns are equal and != 0 return the copy.
   -- If both are 0 then return nothing (all NULLs).
   IF (xcfrec.toSCN_act > greatest(ccfrec.toSCN_act, bcfrec.toSCN_act)) THEN
      rcvRec := xcfrec;
   ELSIF (bcfrec.toSCN_act > ccfrec.toSCN_act) THEN
      rcvRec := bcfrec;
   ELSIF (ccfrec.toSCN_act > 0) THEN
      rcvRec := ccfrec;
   ELSE
      deb('getControlfileBackup found no backup');
      IF (found) THEN
         RETURN dbms_rcvman.AVAILABLE;
      ELSE
         RETURN dbms_rcvman.UNAVAILABLE;
      END IF;
   END IF;
   RETURN SUCCESS;
END getControlfileBackup;

-- This is for 8.0.4 compatibility

---------------------------- findControlFileBackup ----------------------------
FUNCTION findControlFileBackup(
   type       	OUT number
  ,recid      	OUT number
  ,stamp    	OUT number
  ,fname      	OUT varchar2
  ,device_type 	OUT varchar2
  ,ckp_scn    	OUT number)
RETURN number IS
   rcvRec	rcvRec_t;
   rc		number;
BEGIN
   rc := getControlfileBackup(rcvRec);

   IF (rc = SUCCESS) THEN
      IF (rcvRec.type_con = imageCopy_con_t) THEN
         type := COPY;
      ELSIF (rcvRec.type_con = backupSet_con_t) THEN
         type := BACKUP;
      ELSIF (rcvRec.type_con = proxyCopy_con_t) THEN
         type := PROXY;
      ELSE
         -- This is an unknown container type.
         RETURN dbms_rcvman.UNAVAILABLE;
      END IF;

      IF (type = BACKUP) THEN
         recid := rcvRec.bsRecid_con;
         stamp := rcvRec.bsStamp_con;
      ELSE
         recid := rcvRec.recid_con;
         stamp := rcvRec.stamp_con;
      END IF;
      fname       := rcvRec.fileName_con;
      device_type := rcvRec.deviceType_con;
      ckp_scn     := rcvRec.toSCN_act;
      RETURN SUCCESS;
   ELSE
      RETURN rc;
   END IF;

END findControlFileBackup;

-- Obsolete as of 8.1.6

---------------------------- findControlFileBackup ----------------------------
FUNCTION findControlFileBackup(
   type       	OUT number
  ,recid      	OUT number
  ,stamp    	OUT number
  ,fname      	OUT varchar2
  ,device_type 	OUT varchar2
  ,ckp_scn    	OUT number
  ,rlg_scn  	OUT number
  ,blksize     	OUT number)
RETURN number IS
   rcvRec	rcvRec_t;
   rc		number;
BEGIN
   rc := getControlfileBackup(rcvRec);

   IF (rc = SUCCESS) THEN
      IF (rcvRec.type_con = imageCopy_con_t) THEN
         type := COPY;
      ELSIF (rcvRec.type_con = backupSet_con_t) THEN
         type := BACKUP;
      ELSIF (rcvRec.type_con = proxyCopy_con_t) THEN
         type := PROXY;
         rcvRec_last := rcvRec;		-- save for translateProxyDFRecid
      ELSE
         -- This is an unknown container type.
         RETURN dbms_rcvman.UNAVAILABLE;
      END IF;

      IF (type = BACKUP) THEN
         recid := rcvRec.bsRecid_con;
         stamp := rcvRec.bsStamp_con;
      ELSE
         recid := rcvRec.recid_con;
         stamp := rcvRec.stamp_con;
      END IF;
      fname       := rcvRec.fileName_con;
      device_type := rcvRec.deviceType_con;
      ckp_scn     := rcvRec.toSCN_act;
      rlg_scn     := rcvRec.rlgSCN_act;
      blksize     := rcvRec.blockSize_con;
      RETURN SUCCESS;
   ELSE
      RETURN rc;
   END IF;
END findControlFileBackup;

-------------------------
-- Archived Log Backup --
-------------------------

---------------------------- findArchivedLogBackup ----------------------------
PROCEDURE findArchivedLogBackup(
   thread    IN number
  ,sequence  IN number
  ,lowSCN    IN number)
IS
BEGIN
   validateState(null);
   -- RMAN always looks for the log on disk before calling this, so we only
   -- need to look for archivelog backup sets here.
   -- Note that proxy copy for archivelogs is not supported.
   OPEN findArcLogBackup(thread   => thread,
                         sequence => sequence,
                         lowSCN   => lowSCN);
END findArchivedLogBackup;

----------------------------- getArchivedLogBackup ----------------------------
FUNCTION getArchivedLogBackup(
   rcvRec	OUT rcvRec_t)
RETURN binary_integer IS
   myRcvRec	rcvRec_t;
   validRec	validBackupSetRec_t;
   valRC	number;
   found	boolean := FALSE;
BEGIN
   LOOP
      FETCH findArcLogBackup
       INTO myRcvRec;

      IF (findArcLogBackup%NOTFOUND) THEN
         CLOSE findArcLogBackup;
         IF (found) THEN
            RETURN dbms_rcvman.AVAILABLE;
         ELSE
            RETURN dbms_rcvman.UNAVAILABLE;
         END IF;
      END IF;

      valRC := validateBackupSet(backupSetRec	        => myRcvRec,
                                 checkDeviceIsAllocated => TRUE,
                                 available              => TRUE#,
                                 unavailable            => FALSE#,
                                 deleted                => FALSE#,
                                 expired                => FALSE#,
                                 validRec               => validRec);

      IF (valRC = SUCCESS) THEN
         CLOSE findArcLogBackup;
         rcvRec := myRcvRec;
         rcvRec.deviceType_con := validRec.deviceType;
         rcvRec.tag_con        := validRec.tag;
         rcvRec.copyNumber_con := validRec.copyNumber;
         RETURN SUCCESS;
      ELSIF (valRC = dbms_rcvman.AVAILABLE) THEN
         found := TRUE;
      END IF;
   END LOOP;
END getArchivedLogBackup;

-- Obsolete as of 8.1.6

---------------------------- findArchivedLogBackup ----------------------------
FUNCTION findArchivedLogBackup(
   thread#    	IN number
  ,sequence#  	IN number
  ,low_scn    	IN number
  ,type       	OUT number
  ,recid      	OUT number
  ,stamp        OUT number
  ,device_type  OUT varchar2)
RETURN number IS
   rcvRec	rcvRec_t;
   RC		binary_integer;
BEGIN
   -- NOTE:  The previous implementation of this checked the restore_from
   -- variable.  This is really not necessary as all RMAN versions always
   -- called setFrom(backup) before calling this procedure.

   findArchivedLogBackup(thread#, sequence#, low_scn);

   RC := getArchivedLogbackup(rcvRec);

   IF (RC = SUCCESS) THEN
      type        := BACKUP;
      recid       := rcvRec.bsRecid_con;
      stamp       := rcvRec.bsStamp_con;
      device_type := rcvRec.deviceType_con;
   END IF;

   RETURN RC;
END findArchivedLogBackup;

---------------
-- List Copy --
---------------

------------------------- listTranslateControlfileCopy ------------------------
PROCEDURE listTranslateControlfileCopy(
   tag             IN varchar2
  ,completedAfter  IN date
  ,completedBefore IN date
  ,available	   IN number DEFAULT TRUE#	-- for backward compatibility
  ,unavailable     IN number DEFAULT TRUE#	-- for backward compatibility
  ,deleted	   IN number DEFAULT FALSE#	-- for backward compatibility
  ,expired   	   IN number DEFAULT TRUE#)	-- for backward compatibility
IS
BEGIN
   IF (findControlfileCopy%ISOPEN) THEN	-- should not be open
      CLOSE findControlfileCopy;
   END IF;

   -- Replaces the lccf cursor
   OPEN findControlfileCopy(currentIncarnation => TRUE#,
                            tag                => tag,
                            completedAfter     => completedAfter,
                            completedBefore    => completedBefore,
                            available          => available,
                            unavailable        => unavailable,
                            deleted            => deleted,
                            expired            => expired);

END listTranslateControlfileCopy;

---------------------------- listGetControlfileCopy ---------------------------
PROCEDURE listGetControlfileCopy(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   FETCH findControlfileCopy
    INTO rcvRec;

   IF (findControlfileCopy%NOTFOUND) THEN
      CLOSE findControlfileCopy;
      RAISE no_data_found;
   END IF;

   -- Do not need to check the allocated device types.  Copies are available
   -- only on type DISK, so we can assume we wouldn't even be here if
   -- a DISK wasn't allocated.
END listGetControlfileCopy;

-- Obsolete as of 8.1.6

---------------------------- listGetControlfileCopy ---------------------------
FUNCTION listGetControlfileCopy(
   bcfkey     OUT number,
   ckpscn     OUT number,
   ckptime    OUT date,
   status     OUT varchar2,
   completion OUT date,
   fname      OUT varchar2) RETURN number IS

   rcvRec rcvRec_t;
BEGIN
   listGetControlfileCopy(rcvRec);

   bcfkey     := rcvRec.key_con;
   ckpscn     := rcvRec.toSCN_act;
   ckptime    := rcvRec.toTime_act;
   status     := rcvRec.status_con;
   completion := rcvRec.compTime_con;
   fname      := rcvRec.fileName_con;
   return TRUE#;
EXCEPTION
   WHEN no_data_found THEN
      RETURN FALSE#;
END listGetControlfileCopy;

-------------------------- listTranslateDataFileCopy --------------------------
PROCEDURE listTranslateDataFileCopy(
   file#             IN number
  ,creation_change#  IN number
  ,tag               IN varchar2 DEFAULT NULL
  ,file_name_pattern IN varchar2 DEFAULT NULL
  ,completedAfter    IN date DEFAULT NULL
  ,completedBefore   IN date DEFAULT NULL
  ,available	     IN number DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number DEFAULT TRUE#)	-- for backward compatibility
IS
   currentIncarnation number;
   creationSCN        number;
BEGIN
   IF (allIncarnations = TRUE#) THEN
      currentIncarnation := FALSE#;		-- don't care about dbinc_key
      IF (ignoreCreationSCN = TRUE#) THEN
         -- Since the flag is true, we want to list copies of all
         -- incarnations of each datafile number.  Set crescn to NULL so that
         -- the query returns all datafilecopies of a particular file#.
         -- This is used only by RMAN 8.1.5 and prior 8.1 releases.
         creationSCN := NULL;
      ELSE
         creationSCN := creation_change#;
      END IF;
   ELSE
      currentIncarnation := TRUE#;
      creationSCN := creation_change#;
   END IF;

   -- Replaces lcdf cursor
   OPEN findDatafileCopyFno(fno                => file#,
                            crescn             => creationSCN,
                            currentIncarnation => currentIncarnation,
                            tag                => tag,
                            pattern            => file_name_pattern,
                            completedAfter     => completedAfter,
                            completedBefore    => completedBefore,
                            available          => TRUE#,
                            unavailable        => TRUE#);
END listTranslateDataFileCopy;

----------------------------- listGetDataFileCopy -----------------------------
PROCEDURE listGetDataFileCopy(
   rcvRec OUT rcvRec_t) IS
BEGIN
   FETCH findDatafileCopyFno
    INTO rcvRec;

   IF (findDatafileCopyFno%NOTFOUND) THEN
      CLOSE findDatafileCopyFno;
      RAISE no_data_found;
   END IF;

   -- Do not need to check the allocated device types.  Copies are available
   -- only on type DISK, so we can assume we wouldn't even be here if
   -- a DISK wasn't allocated.
END listGetDatafileCopy;

-- Obsolete as of 8.1.6

----------------------------- listGetDataFileCopy -----------------------------
FUNCTION listGetDataFileCopy(
   cdf_key            OUT number
  ,status             OUT varchar2
  ,fname              OUT varchar2
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date)
RETURN number IS
   rcvRec rcvRec_t;
BEGIN
   listGetDatafileCopy(rcvRec);

   cdf_key            := rcvRec.key_con;
   status             := rcvRec.status_con;
   fname              := rcvRec.fileName_con;
   completion_time    := rcvRec.compTime_con;
   checkpoint_change# := rcvRec.toSCN_act;
   checkpoint_time    := rcvRec.toTime_act;
   RETURN TRUE#;
EXCEPTION
   WHEN no_data_found THEN
      RETURN FALSE#;
END listGetDataFileCopy;

------------------------- listTranslateArchivedLogCopy ------------------------
PROCEDURE listTranslateArchivedLogCopy(
   thread#           IN number
  ,sequence#         IN number
  ,first_change#     IN number
  ,file_name_pattern IN varchar2 DEFAULT NULL
  ,completedAfter    IN date     DEFAULT NULL
  ,completedBefore   IN date     DEFAULT NULL
  ,available	     IN number   DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number   DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number   DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number   DEFAULT TRUE#)	-- for backward compatibility
IS
BEGIN
   OPEN findArchivedLogCopy(thread          => thread#,
                            sequence        => sequence#,
                            lowSCN          => first_change#,
                            pattern         => file_name_pattern,
                            completedAfter  => completedAfter,
                            completedBefore => completedBefore,
                            available       => available,
                            unavailable     => unavailable,
                            deleted         => deleted,
                            expired         => expired);
END listTranslateArchivedLogCopy;

---------------------------- listGetArchivedLogCopy ---------------------------
PROCEDURE listGetArchivedLogCopy(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   FETCH findArchivedLogCopy
    INTO rcvRec;

   IF (findArchivedLogCopy%NOTFOUND) THEN
      CLOSE findArchivedLogCopy;
      RAISE no_data_found;
   END IF;
   -- Do not need to check the allocated device types.  Copies are available
   -- only on type DISK, so we can assume we wouldn't even be here if
   -- a DISK wasn't allocated.
END listGetArchivedLogCopy;

-- Obsolete as of 8.1.6

---------------------------- listGetArchivedLogCopy ---------------------------
FUNCTION listGetArchivedLogCopy(
   al_key          OUT number
  ,status          OUT varchar2
  ,fname           OUT varchar2
  ,completion_time OUT date)
RETURN number IS
   rcvRec	rcvRec_t;
BEGIN
   listGetArchivedLogCopy(rcvRec);

   al_key	   := rcvRec.key_con;
   status	   := rcvRec.status_con;
   fname	   := rcvRec.fileName_con;
   completion_time := rcvRec.compTime_con;
   RETURN TRUE#;
EXCEPTION
   WHEN no_data_found THEN
      RETURN FALSE#;
END listGetArchivedLogCopy;

-----------------
-- List Backup --
-----------------

------------------------ listTranslateControlfileBackup -----------------------
PROCEDURE listTranslateControlfileBackup(
   tag             IN  varchar2
  ,completedAfter  IN  date
  ,completedBefore IN  date
  ,available	   IN number DEFAULT TRUE#	-- for backward compatibility
  ,unavailable     IN number DEFAULT TRUE#	-- for backward compatibility
  ,deleted	   IN number DEFAULT FALSE#	-- for backward compatibility
  ,expired   	   IN number DEFAULT TRUE#)	-- for backward compatibility
IS
BEGIN
   IF (findControlfileBackup_c%ISOPEN) THEN	-- should not be open
      CLOSE findControlfileBackup_c;
   END IF;

   -- Replaces the lbcf cursor.  Note that the tag is handled by
   -- validateBackupSet in the get() procedure.
   OPEN findControlfileBackup_c(-- ### This is probably a bug.
                                -- currentIncarnation should probably be true,
                                -- as it is for listTranslateControlfileCopy
                                currentIncarnation => FALSE#,
                                completedAfter     => completedAfter,
                                completedBefore    => completedBefore);

   -- The following parameters are saved in global variables for use by
   -- the pre-8.1.6 listGetControlfileBackup procedure.  Note that the 8.1.6
   -- procedure does NOT use them.  Instead, it is expected that the 8.1.6
   -- RMAN will use the findValidBackupSet procedures to get a list of the
   -- valid copies of a backup set.

   listGetBackupTag         := tag;
   listGetBackupAvailable   := available;
   listGetBackupUnavailable := unavailable;
   listGetBackupDeleted     := deleted;
   listGetBackupExpired     := expired;
END listTranslateControlfileBackup;

--------------------------- listGetControlfileBackup --------------------------
PROCEDURE listGetControlfileBackup(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   FETCH findControlfileBackup_c
    INTO rcvRec;

   -- Note: no backupset validation done here.  RMAN should use the
   -- findValidBackupSet procedures to do that.

   IF (findControlfileBackup_c%NOTFOUND) THEN
      CLOSE findControlfileBackup_c;
      RAISE no_data_found;
   END IF;
END listGetControlfileBackup;

-- Obsolete as of 8.1.6

--------------------------- listGetControlfileBackup --------------------------
FUNCTION listGetControlfileBackup(
   bskey      OUT number,
   ckpscn     OUT number,
   ckptime    OUT date)
RETURN number IS
   rcvRec 		rcvRec_t;
   validationRec	validBackupSetRec_t;
   validationRC		binary_integer;
BEGIN
   <<nextRow>>
   BEGIN
      listGetControlfileBackup(rcvRec);
   EXCEPTION
      WHEN no_data_found THEN
         RETURN FALSE#;
   END;

   validationRC :=
      validateBackupSet(backupSetRec           => rcvRec,
                        tag                    => listGetBackupTag,
                        tagMatchRequired       => TRUE,
                        checkDeviceIsAllocated => TRUE,
                        available              => listGetBackupAvailable,
                        unavailable            => listGetBackupUnavailable,
                        deleted                => listGetBackupDeleted,
                        expired                => listGetBackupExpired,
                        validRec               => validationRec);

   IF (validationRC <> SUCCESS) THEN
      GOTO nextRow;
   END IF;

   bskey      := rcvRec.bsKey_con;
   ckpscn     := rcvRec.toSCN_act;
   ckptime    := rcvRec.toTime_act;
   return TRUE#;
EXCEPTION
    WHEN no_data_found THEN
       RETURN FALSE#;
END listGetControlfileBackup;

------------------------- listTranslateDataFileBackup -------------------------
PROCEDURE listTranslateDataFileBackup(
   file#             IN number
  ,creation_change#  IN number
  ,tag               IN varchar2 DEFAULT NULL
  ,completedAfter    IN date     DEFAULT NULL
  ,completedBefore   IN date     DEFAULT NULL
  ,available	     IN number   DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number   DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number   DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number   DEFAULT TRUE#)	-- for backward compatibility
IS
   rlgSCN       number;
   rlgTime      date;
   crescn       number;
BEGIN
   IF (findDatafileBackup%ISOPEN) THEN
      CLOSE findDatafileBackup;
   END IF;

   IF (allIncarnations = TRUE#) THEN
      IF (ignoreCreationSCN = TRUE#) THEN
         -- Since the flag is true, we want to list copies of all
         -- incarnations of each datafile number.  Set crescn to NULL so that
         -- the query returns all datafilecopies of a particular file#.
         -- This is used only by RMAN 8.1.5 and prior 8.1 releases.
         -- Leave rlgSCN and Time be null.
         crescn := NULL;
      ELSE
         crescn := creation_change#;
      END IF;
   ELSE
      -- The 8.0 RMAN did not list backups that belonged to incarnations
      -- other than the current incarnation.
      rlgSCN  := this_reset_scn;
      rlgTime := this_reset_time;
      crescn  := creation_change#;
   END IF;

   -- Replaces lbdf
   OPEN findDatafileBackup(fno                => file#,
                           crescn             => crescn,
                           rlgSCN	      => rlgSCN,
                           rlgTime            => rlgTime,
                           completedAfter     => completedAfter,
                           completedBefore    => completedBefore);

   -- The following parameters are saved in global variables for use by
   -- the pre-8.1.6 listGetDatafileBackup procedure.  Note that the 8.1.6
   -- procedure does NOT use them.  Instead, it is expected that the 8.1.6
   -- RMAN will use the findValidBackupSet procedures to get a list of the
   -- valid copies of a backup set.

   listGetBackupTag         := tag;
   listGetBackupAvailable   := available;
   listGetBackupUnavailable := unavailable;
   listGetBackupDeleted     := deleted;
   listGetBackupExpired     := expired;
END listTranslateDataFileBackup;

---------------------------- listGetDataFileBackup ----------------------------
PROCEDURE listGetDataFileBackup(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   FETCH findDatafileBackup
    INTO rcvRec;

   IF (findDatafileBackup%NOTFOUND) THEN
      CLOSE findDatafileBackup;
      RAISE no_data_found;
   END IF;
END listGetDataFileBackup;

-- Obsolete as of 8.1.6

---------------------------- listGetDataFileBackup ----------------------------
FUNCTION listGetDataFileBackup(
   bs_key             OUT number
  ,backup_type        OUT varchar2
  ,incremental_level  OUT number
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date)
RETURN number IS
   rcvRec	rcvRec_t;
   valRC        binary_integer;
   validationRec  validBackupSetRec_t;
BEGIN
<<nextRow>>
   BEGIN
      listGetDataFileBackup(rcvRec => rcvRec);
   EXCEPTION
      WHEN no_data_found THEN
         RETURN FALSE#;
   END;

   valRC :=
      validateBackupSet(backupSetRec           => rcvRec,
                        tag                    => listGetBackupTag,
                        tagMatchRequired       => TRUE,
                        checkDeviceIsAllocated => TRUE,
                        available              => listGetBackupAvailable,
                        unavailable            => listGetBackupUnavailable,
                        deleted		       => listGetBackupDeleted,
                        expired                => listGetBackupExpired,
                        validRec	       => validationRec);

   IF (valRC <> SUCCESS) THEN
      GOTO nextRow;
   END IF;

   bs_key             := rcvRec.bsKey_con;
   IF (rcvRec.fromSCN_act = 0) THEN
      backup_type := 'Full';
   ELSE
      backup_type := 'Incremental';
   END IF;
   incremental_level  := rcvRec.level_act;
   completion_time    := rcvRec.compTime_con;		-- somewhat bogus
   checkpoint_change# := rcvRec.toSCN_act;
   checkpoint_time    := rcvRec.toTime_act;
   RETURN TRUE#;
END listGetDataFileBackup;

-- A stupid idea from the 8.1 LIST implementation

----------------------------- translateBackupFile -----------------------------
PROCEDURE translateBackupFile(
   bs_recid    IN  number
  ,bs_stamp    IN  number
  ,fno         IN  number
  ,bskey       OUT number
  ,inclevel    OUT number
  ,backup_type OUT varchar2
  ,completed   OUT date)
IS
BEGIN
   -- Rather than running another query to get values we already
   -- fetched, we simply save the last rcvRec we fetched in a global
   -- variable.  We validate that the record matches
   -- our input args, and then simply extract the values and return them.
   -- The 8.1 implementation of LIST should have simply extended those other
   -- functions to return the values that it required rather than executing
   -- a 2nd query to fetch them, but that did not happen.
   IF (rcvRec_last.type_con <> backupSet_con_t OR
       rcvRec_last.bsRecid_con <> bs_recid OR
       rcvRec_last.bsStamp_con <> bs_stamp) THEN
      raise_application_error(-20204, 'Translation not started');
   END IF;

   bskey     := rcvRec_last.bsKey_con;
   inclevel  := rcvRec_last.level_act;
   completed := rcvRec_last.compTime_con;

   IF (rcvRec_last.logSequence_obj IS NOT NULL) THEN
      backup_type := 'Archived Log';
   ELSE
      IF (rcvRec_last.fromSCN_act = 0) THEN
         backup_type := 'Full';
      ELSE
         backup_type := 'Incremental';
      END IF;
   END IF;
END translateBackupFile;

-- Used by 8.0 and 8.1.6, but not 8.1

------------------------ listTranslateArchivedLogBackup -----------------------
PROCEDURE listTranslateArchivedLogBackup(
   thread#       	IN number
  ,sequence#     	IN number
  ,first_change# 	IN number
  ,completedAfter 	IN date   DEFAULT NULL
  ,completedBefore	IN date   DEFAULT NULL
  ,available		IN number DEFAULT TRUE#
  ,unavailable	  	IN number DEFAULT TRUE#
  ,deleted		IN number DEFAULT FALSE#
  ,expired   		IN number DEFAULT TRUE#)
IS
BEGIN
   OPEN findArcLogBackup(thread          => thread#,
                         sequence        => sequence#,
                         lowSCN          => first_change#,
                         completedAfter  => completedAfter,
                         completedBefore => completedBefore);
   listGetBackupAvailable   := available;
   listGetBackupUnavailable := unavailable;
   listGetBackupDeleted     := deleted;
   listGetBackupExpired     := expired;
END listTranslateArchivedLogBackup;

--------------------------- listGetArchivedLogBackup --------------------------
PROCEDURE listGetArchivedLogBackup(
   rcvRec OUT rcvRec_t)
IS
BEGIN
   FETCH findArcLogBackup
    INTO rcvRec;

   IF (findArcLogBackup%NOTFOUND) THEN
      CLOSE findArcLogBackup;
      RAISE no_data_found;
   END IF;
END listGetArchivedLogBackup;

-- Obsolete as of 8.1

--------------------------- listGetArchivedLogBackup --------------------------
FUNCTION listGetArchivedLogBackup(
   bs_key          OUT number
  ,completion_time OUT date)
RETURN number IS
   rcvRec	rcvRec_t;
   validRec	validBackupSetRec_t;
   valRC	binary_integer;
BEGIN
<<get_next>>
   listGetArchivedLogBackup(rcvRec);

   valRC :=
      validateBackupSet(backupSetRec	       => rcvRec,
                        checkDeviceIsAllocated => TRUE,
                        available              => listGetBackupAvailable,
                        unavailable            => listGetBackupUnavailable,
                        deleted                => listGetBackupDeleted,
                        expired                => listGetBackupExpired,
                        validRec	       => validRec);
   IF (valRC <> SUCCESS) THEN
      GOTO get_next;
   END IF;

   bs_key          := rcvRec.bsKey_con;
   completion_time := rcvRec.compTime_con;
   return TRUE#;
EXCEPTION
   WHEN no_data_found THEN
      RETURN FALSE#;
END listGetArchivedLogBackup;

-- Obsolete as of 8.1.6.
-- This  procedure is just a bad idea left here for backwards compatibility
-- with the broken LIST BACKUP OF ARCHIVELOG command of 8.1.

------------------------ listTranslateArchivedLogBackup -----------------------
PROCEDURE listTranslateArchivedLogBackup(
   thread#	IN number   DEFAULT NULL
  ,lowseq       IN number   DEFAULT NULL
  ,highseq      IN number   DEFAULT NULL
  ,lowscn       IN number   DEFAULT NULL
  ,highscn      IN number   DEFAULT NULL
  ,from_time    IN date     DEFAULT NULL
  ,until_time   IN date     DEFAULT NULL
  ,pattern	IN varchar2 DEFAULT NULL)
IS
BEGIN
  if lbal2%isopen then
    close lbal2;
  end if;
  open lbal2(thread#, lowseq, highseq, lowscn, highscn, from_time, until_time);
END listTranslateArchivedLogBackup;

-- Obsolete as of 8.1.6

--------------------------- listGetArchivedLogBackup --------------------------
FUNCTION listGetArchivedLogBackup(
   bs_key          OUT number
  ,thread#         OUT number
  ,sequence#       OUT number
  ,first_change#   OUT number
  ,next_change#    OUT number
  ,first_time      OUT date
  ,next_time       OUT date)
RETURN number IS
   rcvRec      rcvRec_t;
   validRec    validBackupSetRec_t;
BEGIN
<<get_next>>
  fetch lbal2 into rcvRec;
  if lbal2%found then
    deb('listGetArchivedLogBackup: got a backupset:');
    printRcvRec(rcvRec);
    if validateBackupSet(backupSetRec           => rcvRec,
                         checkDeviceIsAllocated => TRUE,
                         available              => TRUE#,
                         unavailable            => TRUE#,
                         deleted                => FALSE#,
                         expired                => TRUE#,
                         validRec               => validRec) <> SUCCESS then
       goto get_next;
    end if;
    bs_key          := rcvRec.bsKey_con;
    thread#         := rcvRec.logThread_obj;
    sequence#       := rcvRec.logSequence_obj;
    first_change#   := rcvRec.logLowSCN_obj;
    next_change#    := rcvRec.logNextSCN_obj;
    first_time      := rcvRec.logLowTime_obj;
    next_time       := rcvRec.logNextTime_obj;
    return TRUE#;
  else
    close lbal2;
    return FALSE#;
  end if;
END listGetArchivedLogBackup;

---------------------
-- List Proxy Copy --
---------------------

-- Note that this is used for both datafiles and the controlfile

-------------------------- listTranslateProxyDataFile -------------------------
PROCEDURE listTranslateProxyDataFile(
   file#             IN number
  ,creation_change#  IN number
  ,tag               IN varchar2 DEFAULT NULL
  ,handle_pattern    IN varchar2 DEFAULT NULL
  ,completedAfter    IN date     DEFAULT NULL
  ,completedBefore   IN date     DEFAULT NULL
  ,available	     IN number   DEFAULT TRUE#	-- for backward compatibility
  ,unavailable       IN number   DEFAULT TRUE#	-- for backward compatibility
  ,deleted	     IN number   DEFAULT FALSE#	-- for backward compatibility
  ,expired   	     IN number   DEFAULT TRUE#)	-- for backward compatibility
IS
   currentInc   number;
   crescn	number;
BEGIN
   validateState(null);

   IF (allIncarnations = TRUE#) THEN
      currentInc := FALSE#;			-- don't care about dbinc_key
      IF (ignoreCreationSCN = TRUE#) THEN
         -- Since the flag is true, we want to list copies of all
         -- incarnations of each datafile number.  Set crescn to NULL so that
         -- the query returns all datafilecopies of a particular file#.
         -- This is used only by RMAN 8.1.5 and prior 8.1 releases.
         crescn := NULL;
      ELSE
         crescn := creation_change#;
      END IF;
   ELSE
      currentInc := TRUE#;
      crescn     := creation_change#;
   END IF;

   IF (file# = 0) THEN
      IF (findControlfileProxyCopy%ISOPEN) THEN
         CLOSE findControlfileProxyCopy;
      END IF;

      -- This replaces lxdf
      OPEN findControlfileProxyCopy(currentIncarnation => currentInc,
                                    tag                => tag,
                                    pattern            => handle_pattern,
                                    completedAfter     => completedAfter,
                                    completedBefore    => completedBefore,
                                    available          => available,
                                    unavailable        => unavailable,
                                    deleted            => deleted,
                                    expired            => expired);
      listGetProxyDatafileCursor := 'findControlfileProxyCopy';
   ELSE
      IF (findDatafileProxyCopyFno%ISOPEN) THEN
         CLOSE findDatafileProxyCopyFno;
      END IF;

      -- This replaces lxdf
      OPEN findDatafileProxyCopyFno(fno                => file#,
                                    crescn             => crescn,
                                    currentIncarnation => currentInc,
                                    tag                => tag,
                                    pattern            => handle_pattern,
                                    completedAfter     => completedAfter,
                                    completedBefore    => completedBefore,
                                    available          => available,
                                    unavailable        => unavailable,
                                    deleted            => deleted,
                                    expired            => expired);
      listGetProxyDatafileCursor := 'findDatafileProxyCopyFno';
   END IF;
END listTranslateProxyDataFile;

----------------------------- listGetProxyDataFile ----------------------------
PROCEDURE listGetProxyDataFile(
   rcvRec OUT rcvRec_t)
IS
   local rcvRec_t;
BEGIN

<<nextRow>>

   IF (listGetProxyDatafileCursor = 'findControlfileProxyCopy') THEN
      FETCH findControlfileProxyCopy
       INTO local;
      IF (findControlfileProxyCopy%NOTFOUND) THEN
         CLOSE findControlfileProxyCopy;
         RAISE no_data_found;
      END IF;
   ELSIF (listGetProxyDatafileCursor = 'findDatafileProxyCopyFno') THEN
      FETCH findDatafileProxyCopyFno
       INTO local;
      IF (findDatafileProxyCopyFno%NOTFOUND) THEN
         CLOSE findDatafileProxyCopyFno;
         RAISE no_data_found;
      END IF;
   ELSE
      raise_application_error(-20204, 'Translation not started');
   END IF;

   -- Proxy copies can be on different device types, so make sure we
   -- have the right one allocated.
   IF (NOT isDeviceTypeAllocated(local.deviceType_con)) THEN
      GOTO nextRow;
   END IF;

   rcvRec := local;				-- set OUT mode arg
END listGetProxyDataFile;

-- Obsolete as of 8.1.6

----------------------------- listGetProxyDataFile ----------------------------
FUNCTION listGetProxyDataFile(
   xdf_key            OUT number
  ,recid              OUT number
  ,stamp              OUT number
  ,status             OUT varchar2
  ,handle             OUT varchar2
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date)
RETURN number IS
   rcvRec rcvRec_t;
BEGIN
   listGetProxyDataFile(rcvRec);

   xdf_key		:= rcvRec.key_con;
   recid		:= rcvRec.recid_con;
   stamp		:= rcvRec.stamp_con;
   status		:= rcvRec.status_con;
   handle		:= rcvRec.fileName_con;
   completion_time	:= rcvRec.compTime_con;
   checkpoint_change#	:= rcvRec.toSCN_act;
   checkpoint_time	:= rcvRec.toTime_act;
   RETURN TRUE#;
EXCEPTION
   WHEN no_data_found THEN
      RETURN FALSE#;
END listGetProxyDataFile;

-- This procedure serves absolutely no purpose.  It is here only for
-- backwards compatbility with 8.1.5.  The only call to this is from
-- krmkafs(), which gets called from krmkgra().  Since the calls are always
-- in sequence, we can simply save the last record returned from
-- getRecoveryAction and avoid doing an extra query.
-- The only value this functions returns that krmkgra() didn't already have
-- in 8.1.5 is the xdf_key.  Completion time was being estimated from the
-- stamp.

-------------------------- listTranslateProxyDFRecid --------------------------
PROCEDURE listTranslateProxyDFRecid(
   recid              IN number
  ,stamp              IN number
  ,xdf_key            OUT number
  ,file#              OUT number
  ,status             OUT varchar2
  ,handle             OUT varchar2
  ,completion_time    OUT date
  ,checkpoint_change# OUT number
  ,checkpoint_time    OUT date) IS
BEGIN
   -- See if the last rcvRec we returned matches the one requested here.
   -- I think it will always be the case that we can use the value we 
   -- saved in rcvRec_last.  But in case there is a path through Don's
   -- spaghetti code that I could not find, I include the cursor from the
   -- 8.1 package below and use it if the cached value is no good.

   IF (recid <> rcvRec_last.recid_con OR
       stamp <> rcvRec_last.stamp_con) THEN
  select d.xdf_key, d.file#, d.status, d.handle, d.completion_time,
         d.checkpoint_change#, d.checkpoint_time
  into xdf_key, file#, status, handle, completion_time, checkpoint_change#,
       checkpoint_time
  from   rc_proxy_datafile d
  where  db_key = this_db_key
  and   d.recid = listTranslateProxyDFRecid.recid
  and   d.stamp = listTranslateProxyDFRecid.stamp
  union all
  select c.xcf_key, 0, c.status, c.handle, c.completion_time,
         c.checkpoint_change#, c.checkpoint_time
  from   rc_proxy_controlfile c
  where  db_key = this_db_key
  and   c.recid = listTranslateProxyDFRecid.recid
  and   c.stamp = listTranslateProxyDFRecid.stamp;
 
   ELSE
      deb('listTranslateProxyDFRecid: using cached rcvRec_last');
      xdf_key            := rcvRec_last.key_con;
      file#              := rcvRec_last.dfNumber_obj;
      status             := rcvRec_last.status_con;
      handle             := rcvRec_last.fileName_con;
      completion_time    := rcvRec_last.compTime_con;
      checkpoint_change# := rcvRec_last.toSCN_act;
      checkpoint_time    := rcvRec_last.toTime_act;
   END IF;      
END listTranslateProxyDFRecid;

-------------------------------
-- List Database Incarnation --
-------------------------------

-------------------------- listTranslateDBIncarnation -------------------------
PROCEDURE listTranslateDBIncarnation(
   db_name	IN varchar2 DEFAULT NULL)
IS
BEGIN
   IF (ldbi%isopen) THEN
      CLOSE ldbi;
   END IF;
   OPEN ldbi(db_name);
END listTranslateDBIncarnation;

----------------------------- listGetDBIncarnation ----------------------------
FUNCTION listGetDBIncarnation(
   db_key            OUT number
  ,dbinc_key         OUT number
  ,db_name           OUT varchar2
  ,db_id             OUT number
  ,current_inc       OUT varchar2
  ,resetlogs_change# OUT number
  ,resetlogs_time    OUT date)
RETURN number IS
BEGIN
   FETCH ldbi
    INTO db_key, dbinc_key, db_name, db_id, current_inc,
         resetlogs_change#, resetlogs_time;
   IF (ldbi%found) THEN
      RETURN TRUE#;
   ELSE
      CLOSE ldbi;
      RETURN FALSE#;
   END IF;
END listGetDBIncarnation;

--------------------------------------
-- List Rollback Segment Tablespace --
--------------------------------------

-------------------------- listRollbackSegTableSpace --------------------------
PROCEDURE listRollbackSegTableSpace
IS
   prev_ckp_key number;
   next_ckp_key number;
BEGIN
   -- find the full rcvcat checkpoint just before or at the untilSCN
   -- If untilSCN is not set, find the latest full rcvcat checkpoint
   SELECT max(ckp_key)
     INTO prev_ckp_key
     FROM ckp
    WHERE ckp.dbinc_key = this_dbinc_key
      AND ckp.ckp_type  = 'FULL'
      AND ckp.ckp_db_status = 'OPEN'
      AND (ckp.ckp_scn <= untilSCN or untilSCN is NULL);

   -- find the full rcvcat checkpoint just after or at the untilSCN
   -- If untilSCN is not set or there is no full checkpoint after the
   -- untilSCN, return null
   SELECT min(ckp_key)
     INTO next_ckp_key
     FROM ckp
    WHERE ckp.dbinc_key = this_dbinc_key
      AND ckp.ckp_type  = 'FULL'
      AND ckp.ckp_db_status = 'OPEN'
      AND ckp.ckp_scn  >= untilSCN;

   IF (lrtbs%ISOPEN) THEN
      CLOSE lrtbs;
   END IF;

   OPEN lrtbs(prev_ckp_key, next_ckp_key);
END listRollbackSegTableSpace;

------------------------------ listGetTableSpace ------------------------------
FUNCTION listGetTableSpace(
   ts#               OUT number
  ,ts_name           OUT varchar2)
RETURN number IS
BEGIN
   FETCH lrtbs
    INTO ts#, ts_name;

   IF (lrtbs%FOUND) THEN
      RETURN TRUE#;
   ELSE
      CLOSE lrtbs;
      RETURN FALSE#;
   END IF;
END listGetTableSpace;

------------------------
-- Incremental Backup --
------------------------

------------------------------ getIncrementalScn ------------------------------
FUNCTION getIncrementalScn(
   file# 	IN number
  ,create_scn 	IN number
  ,reset_scn 	IN number
  ,reset_time 	IN date
  ,incr_level 	IN number
  ,cumulative 	IN number)
RETURN number IS
   max_ckp_scn	number;
   device_type 	varchar2(80);
   df_dbinc_key number;
   rcvRecCursor rcvRecCursor_t;
   ilevel	number;
   backupSet	rcvRec_t;
   proxyCopy	rcvRec_t;
   imageCopy	rcvRec_t;
   validRec	validBackupSetRec_t;
BEGIN
   IF (incr_level not in (1,2,3,4) OR incr_level is NULL) THEN
      raise_application_error(-20270, 'invalid incremental level');
   END IF;
   IF (cumulative not in (0,1) OR cumulative is NULL) THEN
      raise_application_error(-20271, 'invalid cumulative option');
   END IF;

   -- Find the backup with highest checkpoint scn that

   -- o belongs to the incarnation of the datafile
   -- o matches the given file#
   -- o is an incremental backup/copy at level N or less if non-cumulative or
   --   is an incremental backup/copy at level N-1 or less if cumulative
   -- o belongs to an available backup set if backup

   -- NOTE: Backups from ancestral incarnations are ignored, even if there is
   -- a spanning offline range.  Incremental backups cannot cross resetlogs
   -- boundaries.

   IF (cumulative = TRUE#) THEN
      ilevel := incr_level - 1;		-- Consider only higher level backups
   ELSE
      ilevel := incr_level;
   END IF;

   OPEN findDatafileBackup(fno      => file#,
                           crescn   => create_scn,
                           rlgSCN   => reset_scn,
                           rlgTime  => reset_time,
                           level    => ilevel);

   LOOP
      FETCH findDatafileBackup
       INTO backupSet;

      IF (findDatafileBackup%NOTFOUND) THEN
         EXIT;
      END IF;

      -- We have to validate that a backupset is available if we are to
      -- base an incremental backup upon it.

      IF (validateBackupSet(backupSetRec	   => backupSet,
                            checkDeviceIsAllocated => FALSE,
                            available		   => TRUE#,
                            unavailable            => FALSE#,
                            deleted                => FALSE#,
                            expired                => FALSE#,
                            validRec		   => validRec)
	  <> dbms_rcvman.UNAVAILABLE)
      THEN
         EXIT;					-- SUCCESS or AVAILABLE is OK
      ELSE
         backupSet.toSCN_act := NULL;		-- ignore this backup set
      END IF;
   END LOOP;

   CLOSE findDatafileBackup;

   OPEN findDatafileProxyCopyFno(fno                => file#,
                                 crescn             => create_scn,
                                 currentIncarnation => TRUE#,
                                 level              => ilevel,
                                 minSCN             => backupSet.toSCN_act,
                                 available          => TRUE#);

   FETCH findDatafileProxyCopyFno
    INTO proxyCopy;

   CLOSE findDatafileProxyCopyFno;

   OPEN findDatafileCopyFno(fno                => file#,
                            crescn             => create_scn,
                            currentIncarnation => TRUE#,
                            level              => ilevel,
                            minSCN           => greatest(backupSet.toSCN_act,
                                                         proxyCopy.toSCN_act),
                            available          => TRUE#);

   FETCH findDatafileCopyFno
    INTO imageCopy;

   CLOSE findDatafileCopyFno;

   -- Since we used the minSCN parm when looking for proxy copies and
   -- image copies, we can simply check if the toSCN is not null to
   -- decide which backup type had the highest SCN.

   IF (imageCopy.toSCN_act IS NOT NULL) THEN
      RETURN imageCopy.toSCN_act;
   ELSIF (proxyCopy.toSCN_act IS NOT NULL) THEN
      RETURN proxyCopy.toSCN_act;
   ELSIF (backupSet.toSCN_act IS NOT NULL) THEN
      RETURN backupSet.toSCN_act;
   ELSE
      raise_application_error(-20272, 'cannot take incremental backup');
   END IF;
END getIncrementalScn;

--------------------
-- Offline Ranges --
--------------------

-- Find a controlfile copy that contains the given offline range
-- The checkpoint scn of the controlfile must be greater than or equal to
-- the online scn because the offline range record is inserted into the
-- controlfile when the file is onlined.  The controlfile creation time
-- must be equal to the cursor parameter.
-- Lastly, the min_offr_recid must be
-- less than or equal to the offline range recid to guarantee that record
-- has not been written over in the controlfile copy.

----------------------------- findOfflineRangeCopy ----------------------------
PROCEDURE findOfflineRangeCopy(
   offr_recid   IN number
  ,offr_ckpscn  IN number
  ,cf_cretime   IN date
  ,dbinc_key    IN number)
IS
BEGIN
   validateState(null);

   OPEN getOfflineRangeCopy_c(offrRecid  => offr_recid,
                              offrCkpSCN => offr_ckpscn,
                              cfCreTime  => cf_cretime,
                              dbincKey   => dbinc_key);
END findOfflineRangeCopy;

----------------------------- getOfflineRangeCopy -----------------------------
PROCEDURE getOfflineRangeCopy(
   rcvRec	OUT	rcvRec_t)
IS
BEGIN
   IF (NOT getOfflineRangeCopy_c%ISOPEN) THEN
      raise_application_error(-20204, 'Translation not started');
   END IF;
      
   FETCH getOfflineRangeCopy_c
    INTO rcvRec;

   IF (getOfflineRangeCopy_c%NOTFOUND) THEN
      CLOSE getOfflineRangeCopy_c;
      RAISE no_data_found;
   END IF;
   CLOSE getOfflineRangeCopy_c;
END getOfflineRangeCopy;

-- Obsolete as of 8.1.6

----------------------------- getOfflineRangeCopy -----------------------------
FUNCTION getOfflineRangeCopy
RETURN varchar2 IS
   rcvRec	rcvRec_t;
BEGIN
   getOfflineRangeCopy(rcvRec);
   RETURN rcvRec.fileName_con;
EXCEPTION
   WHEN no_data_found THEN
      RETURN NULL;
END getOfflineRangeCopy;

---------------------------------------
-- Recovery Functions and Procedures --
---------------------------------------

------------------------ setComputeRecoveryActionMasks ------------------------
PROCEDURE setComputeRecoveryActionMasks(
   containerMask	IN number
  ,actionMask		IN number
  ,allRecords           IN number)
IS
BEGIN
   getRA_containerMask := containerMask;
   getRA_actionMask    := actionMask;
   computeRA_allRecords := allRecords;
END setComputeRecoveryActionMasks;

-- Obsolete as of 8.1.6

---------------------------------- setRAflags ---------------------------------
PROCEDURE setRAflags(
   kindMask    IN number
  ,allRecords  IN boolean)
IS
   containerMask	number;
   actionMask		number;
BEGIN

   -- Set container mask

   containerMask := 0;

   IF (bitand(kindMask, implicitOfflRange + cleanRange + applyOfflRange) > 0)
   THEN
      containerMask := containerMask + offlineRangeRec_con_t;
   END IF;

   IF (bitand(kindMask, dfCopy) > 0) THEN
      containerMask := containerMask + imageCopy_con_t;
   END IF;
 
   IF (bitand(kindMask, buSet + applyIncremental) > 0) THEN
      containerMask := containerMask + backupSet_con_t;
   END IF;
 
   IF (bitand(kindMask, proxyFull) > 0) THEN
      containerMask := containerMask + proxyCopy_con_t;
   END IF;

   getRA_containerMask := containerMask;

   -- Set Action Mask

   actionMask := 0;

   IF (bitand(kindMask, dfCopy + ProxyFull + buSet) > 0) THEN
      actionMask := actionMask + full_act_t;
   END IF;

   IF (bitand(kindMask, applyIncremental) > 0) THEN
      actionMask := actionMask + incremental_act_t;
   END IF;

   IF (bitand(kindMask, redo) > 0) THEN
      actionMask := actionMask + redo_act_t;
   END IF;

   IF (bitand(kindMask, implicitOfflRange) > 0) THEN
      actionMask := actionMask + implicitRange_act_t;
   END IF;

   IF (bitand(kindMask, cleanRange) > 0) THEN
      actionMask := actionMask + cleanRange_act_t;
   END IF;

   IF (bitand(kindMask, applyOfflRange) > 0) THEN
      actionMask := actionMask + offlineRange_act_t;
   END IF;

   getRA_actionMask := actionMask;

   IF (allRecords) THEN
      computeRA_allRecords := TRUE#;
   ELSE
      computeRA_allRecords := FALSE#;
   END IF;

   deb('setRAflags kindMask=' || to_char(kindMask) ||
       ' containerMask=' || to_char(containerMask) ||
       ' actionMask=' || to_char(actionMask));

END setRAflags;

---------------------------- computeRecoveryActions ---------------------------
FUNCTION computeRecoveryActions(
fno        IN number,   -- Datafile number.
crescn     IN number,   -- Datafile creation SCN.
df_rlgscn  IN number    -- Datafile resetlogs SCN.  Null unless we are doing
   default null,        -- a RECOVER, in which case is the value in the
                        -- datafile header.
df_rlgtime IN date      -- Datafile resetlogs time.  Null if df_rlgscn is
   default null,        -- null, else value from datafile header.
df_ckpscn  IN number    -- Datafile checkpoint SCN.  Null if df_rlgscn is
   default null,        -- null, else value from datafile header.
offlscn    IN number    -- kccfeofs (0 -> no offline range)
   default 0,
onlscn     IN number    -- kccfeonc (0 if offlscn is 0).
   default 0,
onltime    IN date      -- kccfonc_time
   default null,
cleanscn   IN number    -- kccfecps if either SOR or WCC set, else 0.
   default 0,
clean2scn  IN number    -- CF ckpt SCN if WCC set, infinity if SOR bit set
   default 0,           -- else 0.
clean2time IN date      -- controlfile ckpt time if WCC, SYSDATE if SOR, else
   default null,        -- this is ignored if cleanscn is 0
allowfuzzy IN boolean   -- TRUE if can be fuzzy at until SCN/time, FALSE if
  default FALSE,        -- not.  default is FALSE.
partial_rcv IN boolean  -- TRUE if can do partial recovery, FALSE if not.
  default FALSE,        -- A partial recovery would be to recover a datafile
                        -- using redo up to some SCN, and then switching
                        -- back to incrementals.  This would be done
                        -- because of a missing/unavailable incremental backup.
                        -- We need to know because a partial
                        -- media recovery can only be done if we have a
                        -- current controlfile.  A partial recovery does not
                        -- recover the controlfile.  This could be implemented,
                        -- but it requires using an enqueue to ensure only
                        -- 1 process tries to recover the confile.
                        -- Since we aren't recovering the controlfile,
                        -- the file header won't be handled properly when
                        -- we hit controlfile redo.  That is why we are
                        -- requiring a current controlfile.
                        -- The default is false because RMAN currently does
                        -- not support partial recovery.
cf_scn     IN number    -- controlfile ckpt SCN (NULL if none mounted)
  default NULL,
cf_cretime IN date      -- controlfile creation time (NULL if none mounted)
  default NULL,
cf_offrrid IN number    -- recid of oldest offline range in controlfile
  default NULL,         -- (NULL if none mounted)
allCopies  IN boolean
  default FALSE)
RETURN binary_integer IS
   rc          boolean;
   last        number;
   this        number;
   succ_flag   boolean;
   thisAct     rcvRec_t;
BEGIN
   IF (this_dbinc_key is NULL) THEN
      raise_application_error(-20020, 'Database incarnation not set');
   END IF;

   IF (rcvRecStack.count > 0) THEN
      rcvRecStack.trim(rcvRecStack.count);
   END IF;

   computeRA_restorable := FALSE;
   computeRA_available := FALSE;

   rcvRecStackState.lowAction := 0;
   rcvRecStackState.savePoint := 0;
   rcvRecStackState.top       := 0;

   IF (allCopies) THEN
      deb('allCopies is TRUE');
   ELSE
      deb('allCopies is FALSE');
   END IF;

   rc := computeRecoveryActions(fno, crescn, df_rlgscn, df_rlgtime,
                                df_ckpscn,
                                offlscn, onlscn, onltime,
                                cleanscn, clean2scn, clean2time,
                                allowfuzzy, partial_rcv,
                                null, this_dbinc_key,
                                cf_scn, cf_cretime, cf_offrrid,
                                FALSE, succ_flag, allCopies);

   IF (succ_flag) THEN
      IF (rcvRecStack.count > 0) THEN
         IF (computeRA_allRecords = FALSE#) THEN
            -- We need to find any incremental backups that are redundant
            -- and mark them to be skipped.  It is possible that we
            -- have stacked an incremental backup that we can skip because
            -- it overlaps two other incremental backups.

            -- We can delete action n if action n - 1 can be applied on
            -- top of action n + 1.  I.e., if the toSCN(n+1) >= fromSCN(n-1).

            -- Note that the actions on the stack, from top..1, are in
            -- descending toSCN order.  This means n+1 comes before n-1.

            -- Loop from top-1..2  These are the "middle" actions.  Go in
            -- reverse order since that is the order in which the stack was
            -- built.  "last" is the last action we know we are keeping.
            -- We know we are keeping the action at the top of the stack, as it
            -- is always a full backup.  So "last" starts at the stack top.
            -- "last" is n+1 in the formula above.

            last := rcvRecStack.count;
            FOR this IN REVERSE 2..rcvRecStack.count - 1 LOOP
               IF ((rcvRecStack(last).toSCN_act >=
                    rcvRecStack(this-1).fromSCN_act) AND NOT
                   -- Keep it if allCopies and it is the same container.
                   (allCopies AND
                    rcvRecStack(this).key_con = rcvRecStack(this-1).key_con AND
                    rcvRecStack(this).type_con = rcvRecStack(this-1).type_con))
                  THEN
                  deb('computeRecoveryActions: marking this action deleted:');
                  rcvRecGet(this, thisAct);
                  printRcvRec(thisAct); 
                  rcvRecStack(this).type_con :=
                     rcvRecStack(this).type_con + deleted_con_t;
               ELSE
                  -- We are going to keep this action, so it becomes the last.
                  last := this;
               END IF;
            END LOOP;
         END IF;		-- computeRA_allRecords = FALSE#
         RETURN SUCCESS;
      ELSE
         RETURN NO_ACTION;	-- a recovery that can only use redo
      END IF;
   ELSIF (computeRA_available) THEN
      RETURN dbms_rcvman.AVAILABLE;
   ELSIF (computeRA_restorable) THEN
      RETURN RESTORABLE;
   ELSE
      RETURN dbms_rcvman.UNAVAILABLE;
   END IF;

END computeRecoveryActions;

------------------------------ getRecoveryAction ------------------------------
FUNCTION getRecoveryAction(
   action OUT rcvRec_t)
RETURN binary_integer IS
   redoAction rcvRec_t;
   skip       boolean;
   local      rcvRec_t;
   top        rcvRec_t;
BEGIN
<<getNext>>
   IF (rcvRecStack.count = 0) THEN
      -- Signal to krmk we have reached the end.
      -- We usually only get here if filtering, in which case we cannot
      -- always predict when we have reached the last row.
      --
      -- pre-8.1.3 rman does not tell us to filter out any records (via
      -- the RA flags), so we should not get here.  If we do then it is
      -- a protocol error, so we really should signal an error.
      -- However, there is no way to signal an internal error from a
      -- package.  So just raise the no-data-found condition.
      -- This will cause the pre-8.1.3 RMAN to signal an error because
      -- it does not tolerate any errors from getRecoveryAction.
      raise no_data_found;
   END IF;

   rcvRecPop(local);

   -- See if we want this action kind or not

   IF (bitand(local.type_con, getRA_containerMask) = 0) THEN
      deb('getRecoveryAction: skipping non-selected container type');
      skip := TRUE;                             -- then skip this action
   ELSIF (bitand(local.type_act, getRA_actionMask) = 0) THEN
      deb('getRecoveryAction: skipping non-selected action type');
      skip := TRUE;                             -- then skip this action
   ELSIF (bitand(local.type_con, deleted_con_t) > 0) THEN
      deb('getRecoveryAction: deleted action skipped:');
      skip := TRUE;                             -- then skip this action
   -- If we were doing a LIST, compare the TAG if specified
   ELSIF (computeRA_allRecords = TRUE# AND
          restoreTag is not null AND
          bitand(local.type_con, tagMask_con_t) > 0 AND
          (local.tag_con <> restoreTag OR local.tag_con is null)) THEN
      deb('getRecoveryAction: tag mismatch - skipped:');
      skip := TRUE;                             -- then skip this action
   -- Compare the COMPLETED AFTER time if specified.  Note that
   -- local.compTime may be null.
   ELSIF (getRA_completedAfter IS NOT NULL AND
          local.compTime_con < getRA_completedAfter) THEN
      deb('getRecoveryAction: compTime < completedAfter - skipped:');
      skip := TRUE;                             -- then skip this action
   -- Compare the COMPLETED BEFORE if specified
   ELSIF (getRA_completedBefore IS NOT NULL AND
          local.compTime_con > getRA_completedBefore) THEN
      deb('getRecoveryAction: compTime > completedBefore - skipped:');
      skip := TRUE;                             -- then skip this action
   -- Compare the LIKE pattern if specified
   ELSIF (getRA_likePattern IS NOT NULL AND
          local.fileName_con NOT LIKE getRA_likePattern) THEN
      deb('getRecoveryAction: LikePattern not matched - skipped:');
      skip := TRUE;                             -- then skip this action
   ELSE
      skip := FALSE;
   END IF;

   IF (skip) THEN
      IF (debug) THEN
         printRcvRec(local);
      END IF;
      GOTO getNext;
   END IF;

<<merge_actions>>
   IF (rcvRecStack.count > 0) THEN
      --------------------------------------------------------
      -- See if the next action can be merged with this one --
      --------------------------------------------------------
      rcvRecTop(top);
      IF (local.type_act = redo_act_t AND
          top.type_act = redo_act_t) THEN
         -- Two contiguous redo actions can always be merged.  We know they
         -- must be from the same incarnation because an offline range
         -- action always appears between 2 different incarnations.
         redoAction := local;
         rcvRecPop(local);
         local.fromSCN_act := redoAction.fromSCN_act;
         GOTO merge_actions;
      END IF;
      action := local;
      rcvRec_last := local;
      RETURN TRUE#;			-- more actions to return yet
   ELSE
      action := local;
      rcvRec_last := local;
      RETURN FALSE#;			-- this is the last action
   END IF;

END getRecoveryAction;

-- Obsolete as of 8.1.6

------------------------------ getRecoveryAction ------------------------------
FUNCTION getRecoveryAction(
   kind       OUT number
  ,set_stamp  OUT number
  ,set_count  OUT number
  ,recid      OUT number
  ,stamp      OUT number
  ,fname      OUT varchar2
  ,blocksize  OUT number
  ,blocks     OUT number
  ,devtype    OUT varchar2
  ,from_scn   OUT number
  ,to_scn     OUT number
  ,to_time    OUT date
  ,rlgscn     OUT number
  ,rlgtime    OUT date
  ,cfcretime  OUT date
  ,dbinc_key  OUT number)
RETURN binary_integer IS
   rcvRec	rcvRec_t;
   rc		binary_integer;
BEGIN
   rc := getRecoveryAction(rcvRec);

   IF (rcvRec.type_con = offlineRangeRec_con_t) THEN
      IF (rcvRec.type_act = offlineRange_act_t) THEN
         kind := applyOfflRange;
      ELSIF (rcvRec.type_act = cleanRange_act_t) THEN
         kind := cleanRange;
      ELSIF (rcvRec.type_act = implicitRange_act_t) THEN
         kind := implicitOfflRange;
      ELSE
         deb('cannot convert type_con=' || to_char(rcvRec.type_con) ||
             ' type_act=' || to_char(rcvRec.type_act) ||
             ' to recovery action kind');
         raise_application_error(-20000, 'internal error: getRecoveryAction');
      END IF;
   ELSIF (rcvRec.type_con = backupSet_con_t) THEN
      IF (rcvRec.type_act = full_act_t) THEN
         kind := buSet;
      ELSE
         kind := applyIncremental;
      END IF;
   ELSIF (rcvRec.type_con = proxyCopy_con_t) THEN
      kind := proxyFull;
   ELSIF (rcvRec.type_con = imageCopy_con_t) THEN
      kind := dfCopy;
   ELSIF (rcvRec.type_con IS NULL) THEN
      IF (rcvRec.type_act = redo_act_t) THEN
         kind := redo;
      END IF;
   END IF;

   deb('getRecoveryAction: kind=' || nvl(to_char(kind), 'null'));
   rcvRecConvert(rcvRec);			-- get rid of nulls
   printRcvRec(rcvRec);

   set_stamp := rcvRec.setStamp_con;
   set_count := rcvRec.setCount_con;
   IF (rcvRec.type_con = backupSet_con_t) THEN
      recid     := rcvRec.bsRecid_con;
      stamp     := rcvRec.bsStamp_con;
   ELSE
      recid     := rcvRec.recid_con;
      stamp     := rcvRec.stamp_con;
   END IF;
   fname     := rcvRec.fileName_con;
   blocksize := rcvRec.blockSize_con;
   blocks    := rcvRec.blocks_con;
   devtype   := rcvRec.deviceType_con;
   from_scn  := rcvRec.fromSCN_act;
   to_scn    := rcvRec.toSCN_act;
   to_time   := rcvRec.toTime_act;		-- null OK
   rlgscn    := rcvRec.rlgSCN_act;
   rlgtime   := rcvRec.rlgTime_act;
   cfcretime := rcvRec.cfCreationTime_con;	-- null OK
   dbinc_key := rcvRec.dbincKey_act;		-- null OK

   RETURN rc;
END;

----------------------------- printRecoveryActions ----------------------------
PROCEDURE printRecoveryActions
IS
   action rcvRec_t;
   rc     number;
BEGIN
   deb('===== ' || to_char(rcvRecStack.count) || ' actions stacked =====');

   IF (rcvRecStack.count = 0) THEN
      return;
   END IF;

   LOOP
      rc := getRecoveryAction(action);

      printRcvRec(action);

      EXIT WHEN rc = FALSE#;
   END LOOP;

END printRecoveryActions;

----------------------------- trimRecoveryActions -----------------------------
PROCEDURE trimRecoveryActions(
   maxActions 		IN number
  ,containerMask 	IN number
  ,actionMask		IN number)
IS
   n     number;
BEGIN
   n := trimRecoveryActions(maxActions, containerMask, actionMask);
END trimRecoveryActions;

---------------------
-- Report Obsolete --
---------------------

----------------------------- reportTranslateDFDel ----------------------------
PROCEDURE reportTranslateDFDel
IS
BEGIN
   IF (rddf%isopen) THEN
      CLOSE rddf;
   END IF;
   OPEN rddf;
END reportTranslateDFDel;

-- pre-8.1.5 version.  Discards unused out variables and PROXY rows.

-------------------------------- reportGetDFDel -------------------------------
FUNCTION reportGetDFDel(
   file#               OUT number
  ,filetype            OUT number
  ,checkpoint_change#  OUT number
  ,checkpoint_time     OUT date
  ,resetlogs_change#   OUT number
  ,resetlogs_time      OUT date
  ,incremental_change# OUT number
  ,fuzzy_change#       OUT number
  ,recid               OUT number
  ,stamp               OUT number
  ,fname               OUT varchar2
  ,restorable          OUT number)
RETURN number IS
   rc              number;
   mytype          number;
   key             number;
   completion_time date;
BEGIN
   <<get_next>>
   rc := reportGetDFDel( file#
                        ,mytype
                        ,checkpoint_change#
                        ,checkpoint_time
                        ,resetlogs_change#
                        ,resetlogs_time
                        ,incremental_change#
                        ,fuzzy_change#
                        ,recid
                        ,stamp
                        ,fname
                        ,restorable
                        ,key
                        ,completion_time);
   IF (rc = TRUE#) THEN
      IF (mytype = PROXY) THEN
         GOTO get_next;
      END IF;
      filetype := mytype;
   END IF;
   RETURN rc;
END reportGetDFDel;

-- 8.1.5+ version

-------------------------------- reportGetDFDel -------------------------------
FUNCTION reportGetDFDel(
   file#               OUT number
  ,filetype            OUT number
  ,checkpoint_change#  OUT number
  ,checkpoint_time     OUT date
  ,resetlogs_change#   OUT number
  ,resetlogs_time      OUT date
  ,incremental_change# OUT number
  ,fuzzy_change#       OUT number
  ,recid               OUT number
  ,stamp               OUT number
  ,fname               OUT varchar2
  ,restorable          OUT number
  ,key                 OUT number
  ,completion_time     OUT date)
RETURN number IS
   device_type rc_backup_piece.device_type%TYPE;
   mytype      number;
   set_stamp   number;
   set_count   number;
   pref        number;
   bsRec       bsRec_t;
   validRec    validBackupSetRec_t;
   rcvRec      rcvRec_t;
BEGIN
   FETCH rddf
    INTO pref, file#, mytype, checkpoint_change#, checkpoint_time,
         resetlogs_change#, resetlogs_time, incremental_change#, fuzzy_change#,
         recid, stamp, fname, set_stamp, set_count, key, completion_time,
         device_type;
   filetype := mytype;
   IF (rddf%found) THEN
      IF (mytype in (FULL_DF_BACKUP, INCREMENTAL_DF_BACKUP)) THEN
         findBackupSet(recid => recid,
                       stamp => stamp,
                       bsRec => bsRec);
         -- This is a bit dangerous.  We are hacking together a rcvRec
         -- from a bsRec.
         rcvRec.bsKey_con      := bsRec.key;
         rcvRec.pieceCount_con := bsRec.pieceCount;
         restorable := validateBackupSet(backupSetRec           => rcvRec,
                                         checkDeviceIsAllocated => TRUE,
                                         available              => TRUE#,
                                         expired                => FALSE#,
                                         validRec               => validRec);
      ELSIF (mytype = OFFLINE_RANGE) THEN
         restorable := SUCCESS;
      ELSE
         IF (isDeviceTypeAllocated(device_type)) THEN
            restorable := SUCCESS;
         ELSE
            restorable := dbms_rcvman.AVAILABLE;
         END IF;
      END IF;
      RETURN TRUE#;
   ELSE
      CLOSE rddf;
      RETURN FALSE#;
   END IF;
END reportGetDFDel;

------------
-- TSPITR --
------------

--------------------------------- getCloneName --------------------------------
FUNCTION getCloneName(
   fno    IN number
  ,crescn IN number)
RETURN varchar2 IS
   fname df.clone_fname%TYPE;
BEGIN
   IF (this_dbinc_key is NULL) THEN
      raise_application_error(-20020, 'Database incarnation not set');
   END IF;

   SELECT clone_fname
     INTO fname
     FROM df
    WHERE dbinc_key = this_dbinc_key
      AND file# = fno
      AND create_scn = crescn;

   RETURN fname;
EXCEPTION
   WHEN no_data_found THEN
      -- This should never happen.  The fno and crescn values came from
      -- the recovery catalog, so we should have this record.
      raise_application_error(-20218,
                              'Datafile not found in recovery catalog');
   WHEN others THEN
      raise;
END getCloneName;

------------------
-- Version Info --
------------------

-- Return all the protocol versions that we support, one at a time.
-- Return them in ascending version number order.

------------------------------ getPackageVersion ------------------------------
FUNCTION getPackageVersion
RETURN varchar2 IS
BEGIN
   IF (versionCounter > versionMaxIndex) THEN
      versionCounter := 1;
      RETURN NULL;
   END IF;
   versionCounter := versionCounter + 1;
   RETURN versionList(versionCounter - 1);
END getPackageVersion;

---------------------------
-- Package Instantiation --
---------------------------

BEGIN
   -- initialize version list.
   -- change version_max_index when adding new values.
   versionList(1) := '08.00.04';
   versionList(2) := '08.00.05';
   versionList(3) := '08.01.03';
   versionList(4) := '08.01.05';
   versionList(5) := '08.01.06';

   -- initialize version list iterator variables
   versionCounter := 1;
   versionMaxIndex := 5; -- must match highest index used above

   resetAll;		-- init package variables to defaults
END dbms_rcvman;
>>>

# #########################################################################
#                      Upgrade Recovery Catalog                           #
#                                                                         #
#  If new members are added here, the static array krmkupg_libunits must  #
#  be updated with the new member names.                                  #
#                                                                         #
#  This code applies all changes from 8.0.2 through the current version.  #
#  krmk will ignore errors for columns and views that already exist,      #
#  because that just means that the catalog wasn't so old.                #
#                                                                         #
#  Some members from CREATE CATALOG are also used during upgrade.         #
#  The order in which these members are executed is defined in krmkupg,   #
#  and is important.  For example, the rcver table is manipulated last    #
#  so that the UPGRADE CATALOG command can be re-run if it fails          #
#  midstream.                                                             #
#                                                                         #
# #########################################################################

# 8.0.2 -> 8.0.3
define upgcat_1 <<< alter table ckp add (ckp_db_status VARCHAR2(7)) >>> 
define upgcat_2
<<<
alter table ts add CONSTRAINT ts_u2 UNIQUE (dbinc_key, ts_name, create_scn)
>>>
define upgcat_3 <<< alter table df add (clone_fname VARCHAR2(1024)) >>> 
define upgcat_4 <<< alter table bdf add (completion_time date) >>> 
define upgcat_5 <<< alter table offr add (cf_create_time date) >>>
define upgcat_6 <<< alter table offr drop constraint offr_u2 >>>
define upgcat_7
<<<
alter table offr add constraint offr_u2
  UNIQUE (dbinc_key, file#, offline_scn, cf_create_time)
>>>

# 8.0.3 -> 8.0.4
define upgcat_8
<<< update dbinc set high_df_recid = 0 where high_df_recid is not null >>>
define upgcat_9  <<< alter table df add (stop_scn number) >>>
define upgcat_10 <<< alter table df add (read_only number) >>>
define upgcat_11 <<< update df set stop_scn = null, read_only = 0 >>>
define upgcat_12 <<< alter table df modify (read_only not null) >>>

# 8.0.4 -> 8.0.5
define upgcat_13 <<< alter table dfatt modify (fname null) >>>
define upgcat_14 <<< alter table dfatt modify (blocks null) >>>

# 8.0.5 -> 8.1.3
define upgcat_15 <<< alter table bp add (media_pool number) >>>
define upgcat_16 <<< alter table bp add (copy# number) >>>
define upgcat_17 <<< update bp set copy# = 1 >>>
define upgcat_18 <<< alter table bp modify (copy# not null) >>>
define upgcat_19 <<< alter table bp drop constraint bp_c_status >>>
define upgcat_20
<<<
alter table bp add constraint bp_c_status check (status in ('A','U','D','X'))
>>>
define upgcat_21
<<< alter table dbinc add (high_pc_recid  number default 0) >>>
define upgcat_22 <<< update dbinc set high_pc_recid = 0 >>>
define upgcat_23 <<< alter table dbinc modify (high_pc_recid not null) >>>
define upgcat_24
<<< alter table rlh add constraint rlh_f1
FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE >>>
define upgcat_25
<<< alter table al add constraint al_f1
FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE >>>
define upgcat_26
<<< alter table ccf add constraint ccf_f1
FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE >>>
define upgcat_27
<<< alter table xcf add constraint xcf_f1
FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE >>>
define upgcat_28
<<< alter table cdf add constraint cdf_f1
FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE >>>
define upgcat_29
<<< alter table xdf add constraint xdf_f1
FOREIGN KEY (dbinc_key) REFERENCES dbinc ON DELETE CASCADE >>>
define upgcat_30 <<< alter table bs modify (bs_recid NULL) >>>
define upgcat_31 <<< alter table bs modify (bs_stamp NULL) >>>
define upgcat_32 <<< alter table bs modify (bck_type NULL) >>>
define upgcat_33 <<< alter table bs modify (incr_level NULL) >>>
define upgcat_34 <<< alter table bs modify (status NULL) >>>
define upgcat_35 <<< alter table bs drop constraint bs_u1 >>>
define upgcat_36 <<< alter table bs modify (start_time NULL) >>>
define upgcat_37 <<< alter table bs modify (completion_time NULL) >>>
define upgcat_38 <<< alter table df add (stop_time date) >>>
define upgcat_end_1 <<< delete from rcver >>>
define upgcat_end_2
<<< alter table rcver add constraint rcver_version_unique unique(version) >>>

# #########################################################################
#                      Drop Recovery Catalog                              #
#                                                                         #
#  If new members are added here, the static array krmkdrp_libunits must  #
#  be updated with the new member names.                                  #
#                                                                         #
#  If any database objects are removed between one version and another    #
#  of the recovery catalog (i.e. a catalog upgrade drops an object), do   #
#  NOT remove the corresponding drpcat libmem for that object, because    #
#  the DROP CATALOG command must remove all remnants of any version of    #
#  the catalog.                                                           #
#                                                                         #
# #########################################################################

#
# catnormn.sql
#
#  Copyright (c) Oracle Corporation 1996, 1997, 1998, 1999. All Rights Reserved.
#
#    NAME
#      catnormn.sql
#
#    DESCRIPTION
#      Drop Recovery Catalog tables and views
#    NOTES
#
#    MODIFIED   (MM/DD/YY)
#    swerthei    05/19/98 - bring up-to-date with catrman                    
#    swerthei    08/18/97 - x$dbms_rcvman synonym is not used anymore
#    gpongrac    01/28/97 - drop tsatt table too
#    tpystyne    09/11/96 - drop recovery catalog tables and views
#    tpystyne    09/11/96 - Created
#

define drpcat_1  <<< DROP SEQUENCE rman_seq >>>
define drpcat_2  <<< DROP TABLE rcver >>>
define drpcat_3  <<< DROP TABLE db cascade constraints >>>
define drpcat_4  <<< DROP TABLE dbinc cascade constraints >>>
define drpcat_5  <<< DROP TABLE ckp cascade constraints >>>
define drpcat_6  <<< DROP TABLE ts cascade constraints >>>
define drpcat_7  <<< DROP TABLE tsatt cascade constraints >>>
define drpcat_8  <<< DROP TABLE df cascade constraints >>>
define drpcat_9  <<< DROP TABLE dfatt cascade constraints >>>
define drpcat_10 <<< DROP TABLE offr cascade constraints >>>
define drpcat_11 <<< DROP TABLE rr cascade constraints >>>
define drpcat_12 <<< DROP TABLE rt cascade constraints >>>
define drpcat_13 <<< DROP TABLE orl cascade constraints >>>
define drpcat_14 <<< DROP TABLE rlh cascade constraints >>>
define drpcat_15 <<< DROP TABLE bs cascade constraints >>>
define drpcat_16 <<< DROP TABLE bp cascade constraints >>>
define drpcat_17 <<< DROP TABLE bcf cascade constraints >>>
define drpcat_18 <<< DROP TABLE ccf cascade constraints >>>
define drpcat_19 <<< DROP TABLE xcf cascade constraints >>>
define drpcat_20 <<< DROP TABLE bdf cascade constraints >>>
define drpcat_21 <<< DROP TABLE cdf cascade constraints >>>
define drpcat_22 <<< DROP TABLE xdf cascade constraints >>>
define drpcat_23 <<< DROP TABLE brl cascade constraints >>>
define drpcat_24 <<< DROP TABLE al cascade constraints >>>
define drpcat_25 <<< DROP TABLE bcb cascade constraints >>>
define drpcat_26 <<< DROP TABLE ccb cascade constraints >>>
define drpcat_27 <<< DROP TABLE scr cascade constraints >>>
define drpcat_28 <<< DROP TABLE scrl cascade constraints >>> 
define drpcat_29 <<< DROP VIEW rc_database >>>
define drpcat_30 <<< DROP VIEW rc_database_incarnation >>>
define drpcat_31 <<< DROP VIEW rc_resync >>>
define drpcat_32 <<< DROP VIEW rc_checkpoint >>>
define drpcat_33 <<< DROP VIEW rc_tablespace >>>
define drpcat_34 <<< DROP VIEW rc_datafile >>>
define drpcat_35 <<< DROP VIEW rc_redo_thread >>>
define drpcat_36 <<< DROP VIEW rc_redo_log >>>
define drpcat_37 <<< DROP VIEW rc_log_history >>>
define drpcat_38 <<< DROP VIEW rc_archived_log >>>
define drpcat_39 <<< DROP VIEW rc_backup_set >>>
define drpcat_40 <<< DROP VIEW rc_backup_piece >>>
define drpcat_41 <<< DROP VIEW rc_backup_datafile >>>
define drpcat_42 <<< DROP VIEW rc_backup_controlfile >>>
define drpcat_43 <<< DROP VIEW rc_datafile_copy >>>
define drpcat_44 <<< DROP VIEW rc_controlfile_copy >>>
define drpcat_45 <<< DROP VIEW rc_backup_redolog >>>
define drpcat_46 <<< DROP VIEW rc_backup_corruption >>>
define drpcat_47 <<< DROP VIEW rc_copy_corruption >>>
define drpcat_48 <<< DROP VIEW rc_offline_range >>>
define drpcat_49 <<< DROP VIEW rc_stored_script >>>
define drpcat_50 <<< DROP VIEW rc_stored_script_line >>>
define drpcat_51 <<< DROP VIEW rc_proxy_datafile >>>
define drpcat_52 <<< DROP VIEW rc_proxy_controlfile >>>
define drpcat_53 <<< DROP PACKAGE dbms_rcvcat >>>
define drpcat_54 <<< DROP PACKAGE dbms_rcvman >>>
define drpcat_55 <<< DROP TABLE config >>>
