SQL> ed Wrote file afiedt.buf 1* select text from dba_source where name = 'DBMS_RCVCAT' and type = 'PACKAGE BODY' order by line SQL> / PACKAGE BODY dbms_rcvcat IS -- prvtrvct.sql -- -- -- NAME -- prvtrvct.sql -- -- DESCRIPTION -- -- NOTES -- -- MODIFIED (MM/DD/YY) -- molagapp 04/29/09 - bump up version to 11.2.0.1 -- molagapp 03/27/09 - bug 5739423 -- molagapp 03/17/09 - bug 8324589 -- banand 03/13/09 - bug 8340871:changeArchivelog to deal with recid/stamp -- molagapp 07/10/08 - bug 7215002 -- banand 07/08/08 - bug-7117200 -- banand 06/26/08 - bug 7173341: fix cleanupRSR performance -- banand 05/30/08 - bug 7138218 -- banand 04/22/08 - bug 6993175 done txn bug-6965089 -- fsanchez 06/05/07 - tspitr datapump -- jkrismer 02/29/08 - bug-6055481 bsStatusRecalc only from 9.2 client -- molagapp 02/13/08 - bug-6774767 -- banand 01/26/08 - bug 6750214:df.rfile# and df.create_time can be null -- banand 12/05/07 - bug-6653570: fix tempfile resync in DG env. -- jkrismer 11/21/07 - bug 5906892 fix ORA-12899 when differnt charset -- jciminsk 10/22/07 - Upgrade support for 11.2 -- jciminsk 10/08/07 - version to 11.2.0.0.0 -- molagapp 10/02/07 - bug 6451722 -- banand 08/15/07 - fix update brl SQL and cleanupROUT procedure -- jciminsk 08/03/07 - version to 11.1.0.7.0 -- molagapp 06/05/07 - bump up version to 11.1.0.6 -- jkrismer 06/12/07 - Bug 5932029 reset db high_ic_recid in beginCkpt -- raguzman 05/11/07 - ReOrg restore point resync, add proxy copy affect -- banand 06/05/07 - bug-6034995 -- molagapp 05/15/07 - bug-5939669 -- banand 05/10/07 - track remote cf not updated during resync -- banand 05/01/07 - bug 6011303 -- banand 05/01/07 - backupset reset db_unique_name allowed -- banand 04/23/07 - bug 5971763 -- banand 04/08/07 - package support for file sharing attributes -- molagapp 04/18/07 - bump up version to 11.1.0.5 -- banand 03/26/07 - bug 5885624 - validate db_id for resetDatabase -- jkrismer 03/27/07 - 5932181 and 5934290 fix resync for temp -- molagapp 04/02/07 - bug 5899994 -- raguzman 02/19/07 - checkDeletedObject should always update site_dfatt -- banand 03/01/07 - update site_key of duplicate record during resync -- banand 02/23/07 - water mark usage at stby for non-null db_unique_name -- molagapp 02/14/07 - bump up version to 11.1.0.4 -- banand 02/21/07 - update comments and minor fixes -- banand 01/29/07 - resync at standby to use last full resync ckpt scn -- banand 01/26/07 - add encrypted, backed_by_osb -- banand 08/31/06 - bug-5647645 -- banand 11/22/06 - resync conf from catalog cf first time, if there are -- configurations for that site. -- banand 11/08/06 - bug 5219484 -- molagapp 11/01/06 - bump up version to 11.1.0.3 -- banand 09/10/06 - bug 5441981 -- molagapp 10/04/06 - bump up version to 11.1.0.2 -- molagapp 09/15/06 - bump up version to 11.1.0.1 -- raguzman 08/07/06 - Resync time for guaranteed restore points -- swerthei 08/02/06 - bug 5364391, remove date conversion in bcf insert -- molagapp 07/13/06 - refix bug-2107554 -- amjoshi 07/05/06 - lrg-2384619. -- raguzman 06/10/06 - Resync normal restore points -- cpedrega 06/12/06 - dbuname set to 30 (=KRMK_KDBUNMAXLEN); -- fixed SELECT SUBSTR for db_uname -- banand 06/10/06 - 17844_phase_3: unregistersite remove conf rows also -- amjoshi 06/08/06 - Update node table on full resync. -- molagapp 05/29/06 - improve block corruption project -- swerthei 10/12/05 - virtual private catalog -- swerthei 01/07/06 - multi-section backups -- banand 05/09/06 - 17844_phase_2: spfile/change/resync changes -- molagapp 01/23/06 - backup transportable tablespace -- molagapp 03/20/06 - bug-5106952 -- molagapp 12/21/05 - merge catalog project -- banand 12/27/05 - 17844_phase_1: track site specific info -- fsanchez 01/05/06 - keep track of reason and actions of full resync -- amjoshi 03/17/06 - LRG 2106205: fix resync. code for datafiles. -- banand 12/27/05 - schema changes to track node specific info -- molagapp 01/30/06 - bug-4941096 -- molagapp 01/16/06 - bump up version to 11.1.0.0 -- molagapp 12/12/05 - bug 4754328 -- banand 12/07/05 - bug 4755799 -- banand 09/28/05 - bug 4637849 -- molagapp 10/03/05 - update versionList -- molagapp 09/17/05 - fix changeBackuppiece -- molagapp 08/05/05 - bug 4531791 -- banand 05/19/05 - schema change for encrypted backup configurations -- fsanchez 11/02/04 - backup optimization -- banand 03/22/05 - bug-3877184 -- molagapp 03/18/05 - add getLogHistoryLowSCN -- molagapp 03/02/05 - bug-4146404 -- molagapp 02/15/05 - rewrite sql query for performance -- molagapp 02/14/05 - remove unnecessary to_date -- molagapp 01/26/05 - bug-3959063: add isDuplicateRecord -- banand 01/05/05 - bug-3966722 -- fsanchez 08/25/04 - bug 2794801 - get current dbinc -- banand 09/17/04 - bug 3888851 -- molagapp 08/27/04 - remove guaranteed_flashback_scn -- molagapp 08/18/04 - change default block_size as null -- banand 04/20/04 - enhance job views -- molagapp 05/18/04 - add guaranteed_flashback_scn -- molagapp 05/20/04 - add new_incarnation to resetDatabase -- molagapp 05/01/04 - tempfile re-creation project -- molagapp 03/23/04 - bug-3527769 -- jeffyu 02/06/04 - bug 3234433 -- fsanchez 02/13/04 - Rewrite checkOfflineRange procedure -- molagapp 02/11/04 - bug 3310413 -- banand 10/10/03 - bug 3134939 -- molagapp 10/16/03 - remove is_recovery_dest_file from checkDataFile -- swerthei 09/12/03 - add bdf.blocks_read -- fsanchez 09/29/03 - lrg_1578516 -- molagapp 09/10/03 - update bs status while removing duplicate handles -- molagapp 09/10/03 - fix update to rlh table with clear status -- sdizdar 09/09/03 - bug-3115984: add rename "delete object" -- molagapp 09/05/03 - lrg 1564671: close scrlQ cursor at end of fetch -- jeffyu 08/26/03 - modifying updateOldestFlashbackSCN - lrg 1562424 -- sdizdar 08/21/03 - bug-3005920 -- sdizdar 08/19/03 - add more debugs in checkRmanStatus -- jeffyu 07/21/03 - bug 2976535 -- molagapp 06/09/03 - fix force_resync2cf update -- molagapp 05/06/03 - allow uncatalog of bp when db is not mounted -- banand 05/14/03 - multi-node RMAN configuration support -- molagapp 05/30/03 - fix keep_options default values -- molagapp 03/23/03 - use dbms_output buffer_size as null -- fsanchez 01/20/03 - enhanced_scripts -- sdizdar 01/30/03 - prj 2090 (compressed backup): -- - add resync of compressed flag -- fsanchez 03/26/03 - bug-2845436 -- swerthei 02/21/03 - bug 2803823; make cleanupCKP query more efficient -- sdizdar 02/25/03 - remove rsr from ckptNeeded() -- banand 02/13/03 - bug 2802688: fix updating of duplicate names for al -- molagapp 02/04/03 - fix 10i package compatibility -- mjaeger 02/02/03 - bug 2719863: changeDatafileCopy: add cond on db key -- mjaeger 12/10/02 - bug 2554861: lockForCkpt: avoid deadlock on CKP -- sdizdar 09/03/02 - add resync of RC_RMAN_STATUS -- molagapp 08/20/02 - recovery area project -- banand 08/26/02 - dbinc_status values as in varchar2 -- banand 08/09/02 - Recovery thru resetlogs proj: -- - get resetlogs stamps for offr and orl -- - add recomputeDbincStatus -- nsadaran 09/30/02 - fixing comments -- jeffyu 09/19/02 - Added rename tablespace support in resync -- mdilman 09/18/02 - support for bigfile column in rc_tablespace -- molagapp 05/24/02 - delete cdf entry with status 'D' -- sdizdar 05/22/02 - modify unregisterDatabase -- molagapp 05/13/02 - fix update no_of_pieces in checkBackupPiece -- molagapp 04/17/02 - proxy archived log -- molagapp 01/06/02 - catalog backuppiece support -- banand 02/01/02 - fix 2210440 -- molagapp 01/31/01 - change default value for cftype in beginCkpt -- molagapp 11/29/01 - update package version 9.2.0 -- molagapp 11/13/01 - bug 2107554 -- fsanchez 11/10/01 - bug-2071872 -- molagapp 10/29/01 - fix standby/primary switch full resync -- molagapp 05/27/01 - bug 1530744 -- molagapp 10/08/01 - fix default value for scanned -- sdizdar 09/08/01 - SPFILE backup: add functions for resync -- swerthei 08/03/01 - close cursors in cancelCkpt -- banand 07/24/01 - fix 1856783 -- banand 07/02/01 - fix 758489 -- fsanchez 05/23/01 - dbnewid -- sdizdar 06/26/01 - bug-1852923: fix beginConfigResync() -- swerthei 05/08/01 - code cleanup -- swerthei 04/04/01 - add v$datafile_copy.scanned -- fsanchez 04/11/01 - remove_get_put -- swerthei 04/03/01 - add new setDatabase calls for debugging -- swerthei 03/05/01 - ckp table cleanup -- fsanchez 02/15/01 - bug-1538834 -- fsanchez 01/25/01 - bug-1586048 -- molagapp 11/30/00 - bug 1518515 -- sdizdar 11/12/00 - bug-1496982: 8.2 -> 9.0 -- molagapp 10/26/00 - bug-1332121 -- molagapp 10/24/00 - bug-1478785 -- molagapp 10/16/00 - bug-1467871 -- sdizdar 09/21/00 - fix 8.x compatibility -- - modify ckptNeeded -- fsanchez 05/30/00 - cfile-autobackup -- sdizdar 09/11/00 - tablespace resync fix: -- - fix ckptNeeded (add recid of tablespace records) -- - improved beginTableSpaceResyn -- molagapp 08/28/00 - fix 8.2 upgrade -- dbeusee 06/28/00 - rman82_maint_syntax_unification -- dbeusee 07/20/00 - rman82_debug_enhancements -- fsanchez 07/31/00 - backup_flat_files -- sdizdar 06/28/00 - Configure auxfilename and exclude tablespace: -- - setDatabase calls dbms_rcvman.setDatabase -- - add getCloneName, -- - updated checkDatafile and checkTablespace -- fsanchez 05/30/00 - cfile-autobackup -- sdizdar 05/12/00 - RMAN retention policy (keep): -- - keep backup support -- banand 05/19/00 - use value in findConfig_c cursor -- swerthei 06/06/00 - add archived log logminer dictionary columns -- dbeusee 04/13/00 - rman82_cf_status_unification -- molagapp 05/18/00 - rfile# and recovered changes -- fsanchez 03/22/00 - instantiate_standby -- sdizdar 04/17/00 - RMAN configuration: -- - add setConfig, resetConfig, deleteConfig, getConfig -- swerthei 04/04/00 - allow orphaned blocks in bcb -- molagapp 02/25/00 - bug 1186598: remove compatible, undo deleteAL change -- sdizdar 02/18/00 - bug-977412: added commit to -- deleteScript, createScript and replaceScript -- swerthei 09/14/99 - add dbms_rcvcat.reNormalize -- gpongrac 08/16/99 - add 8.1.6 to versionlist -- gpongrac 08/04/99 - really delete records now -- gpongrac 07/14/99 - add stop_time to df table -- gpongrac 07/06/99 - change rcver to 8.1.6 -- gpongrac 12/18/98 - clean up checkBackupPiece -- dbeusee 09/13/98 - bug-728666 -- rlu 05/03/99 - 621515_restore_main -- gpongrac 04/08/99 - change REM to -- -- rlu 11/25/98 - bug_621515 -- swerthei 10/22/98 - change proxy messages -- fsanchez 10/07/98 - bug-607271 -- swerthei 06/19/98 - prepare for wrapping to recover.bsq -- swerthei 06/17/98 - make compatible with 8.0 -- swerthei 06/01/98 - add media_pool -- swerthei 06/01/98 - add changeProxyCopy -- swerthei 05/18/98 - resync proxy copy records -- dbeusee 04/20/98 - rpt_redundancy_enh -- gpongrac 05/06/98 - add bsStatusRecalc -- dbeusee 04/06/98 - xcheck enh -- fsanchez 03/29/98 - Duplexed backup sets -- gpongrac 01/27/98 - change getCatalogVersion -- gpongrac 01/16/98 - bug 612344: deal with null fname in checkDatafile -- fsanchez 01/04/98 - Allow setDatabase to receive dbid without dbname -- gpongrac 09/02/97 - add setDatafile Size -- tpystyne 09/12/97 - bug 480172, fix name translation -- gpongrac 08/12/97 - deal with clone_name becoming the real filename -- gpongrac 07/01/97 - fix typo -- gpongrac 06/30/97 - keep offline clean and read-only scn in df table -- gpongrac 06/30/97 - record current offline range in kccor -- gpongrac 06/26/97 - uppercase all keywords and reformat -- gpongrac 04/08/97 - deal with 0 creation scn in checkofflinerange -- gpongrac 04/03/97 - consider offline ranges in ckptneeded -- gpongrac 03/31/97 - change to use version_time instead of cf_create_t -- gpongrac 03/31/97 - add cf_create_time to offr -- tpystyne 03/20/97 - update catalog version to 8.00.03 -- tpystyne 02/27/97 - add ckptneeded -- gpongrac 02/20/97 - add compltion_time to bdf -- swerthei 01/14/97 - add setclonename -- ### comments from 1996 removed type version_list_type is table of varchar2(11) 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; catalogVersion CONSTANT VARCHAR2(11) := '11.02.00.01'; /*-------------------------* * Package State Variables * *-------------------------*/ -- Package State Variables: -- debug -- Controls whether deb() sends debug info back to RMAN. -- this_db_key -- This is the primary key of the db record for the target database we -- are dealing with. It is set as a result of calling registerDatabase, -- resetDatabase or setDatabase. -- this_dbinc_key -- This is the primary key of the dbinc for the incarnation of the target -- database we are dealing with. It is set as a result of calling -- registerDatabase, resetDatabase or setDatabase. -- this_ckp_key -- This is the primary key of the current recovery catalog checkpoint -- or 0 for partial checkpoint. -- It is set when beginCkpt is called, and cleared to null when endCkpt -- is called. -- last_* -- Used to ensure that record are passed to check* procedures -- in ascending order. -- -- see Cursor Row Variables section for more state variables debug BOOLEAN := FALSE; this_ckp_key NUMBER := NULL; this_ckp_scn NUMBER := NULL; this_ckp_time DATE := NULL; last_full_ckp_scn NUMBER := NULL; last_ts# NUMBER; last_file# NUMBER; last_thread# NUMBER; last_fname site_dfatt.fname%type; last_ts_recid NUMBER; last_df_recid NUMBER; last_tf_recid NUMBER; last_rt_recid NUMBER; last_orl_recid NUMBER; last_conf_recid NUMBER; force_resync2cf VARCHAR2(3) := 'NO'; last_rlh_recid NUMBER; last_al_recid NUMBER; last_offr_recid NUMBER; last_bs_recid NUMBER; last_bp_recid NUMBER; last_bdf_recid NUMBER; last_bsf_recid NUMBER; last_brl_recid NUMBER; last_cdf_recid NUMBER; last_bcb_recid NUMBER; last_ccb_recid NUMBER; last_do_recid NUMBER; last_xdf_recid NUMBER := NULL; last_xal_recid NUMBER := NULL; last_rsr_recid NUMBER; last_rout_stamp NUMBER := NULL; last_inst_startup_stamp NUMBER := NULL; lrsr_key NUMBER; lrout_skey NUMBER; lsession_recid NUMBER; lsession_stamp NUMBER; lrman_status_recid NUMBER; lrman_status_stamp NUMBER; -- 5906892 krbmror_llength_bytes NUMBER := 130; -- last_ic_recid contains Non-NULL value, if incarnation records are -- resynced using recids. last_ic_recid NUMBER := NULL; scr_key NUMBER := NULL; scr_line NUMBER; scr_glob BOOLEAN; kccdivts NUMBER; type bskeys is table of number index by binary_integer; cntbs NUMBER := 0; updatebs bskeys; last_reset_scn NUMBER; last_reset_time DATE; last_dbinc_key NUMBER; do_temp_ts_resync BOOLEAN := FALSE; -- indicates if temp_ts is resynced last_cf_version_time DATE; dbglvl NUMBER := RCVCAT_LEVEL_DEFAULT; low_nrsp_recid NUMBER; last_nrsp_recid NUMBER; last_grsp_recid NUMBER; last_rspname grsp.rspname%type; last_bcr_recid NUMBER; last_resync_cksum NUMBER; -- -- NOTE :: this_cf_type usage -- -- Starting 11g catalog schema, we keep track of high water marks for primary -- and standby database if client supplies non-null db_unique_name. The reason -- we can not keep track of water marks for standby when db_unique_name is -- null (when connected 9iR2 RMAN), is there is only one row in NODE table with -- db_unique_name value NULL. And we don't want to change behavior of -- optimizing resync at primary in this case. If we update water marks every -- time when a new standby control file or primary control file is seen, it -- could cause resync of all records from primary and standby if RMAN is -- connected to primary and standby alternatively. Instead of paying penalty -- in this case, we just don't keep track of water marks for standby cf. -- -- This variable MUST be used in all circular record beginresync/endresync -- code to return 0 as recid and not to update high water mark if we can not -- keep track of high water marks for particular control file type. -- this_cf_type VARCHAR2(7) := NULL; -- Db unique name (once it was called service name) this_db_unique_name VARCHAR2(30) := NULL; this_site_key NUMBER; -- Never NULL even for 9i RMAN client client_site_aware boolean := FALSE; -- see prvtrmnu.sql for semantics of below 3 variables logs_shared number := FALSE#; -- used only when client_site_aware disk_backups_shared number := TRUE#; -- indicates shared accross all sites tape_backups_shared number := TRUE#; -- indicates shared accross all sites 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; RENORM_TFATT CONSTANT binary_integer := 7; -- data_type to track session circular section water marks. When controlfile -- is backup, we try to resync all records starting from mined resync -- timestamp. Even if mining fails, we will just pay the cost for resync -- only once in the session. Later, this sessionWaterMark will maintain -- the watermarks for the session which will make the subsequent resync -- faster. type sessionWaterMarks_t is record ( last_kccdivts number := 0, -- check if ctl version is diff high_rout_stamp number := 0, -- for rman_output resync high_ic_recid number := 0, -- incarnation recid high_offr_recid number := 0, -- offline range (kkor) recid high_rlh_recid number := 0, -- log history (kcclh) recid high_al_recid number := 0, -- archived log (kccal) recid high_bs_recid number := 0, -- backup set (kccbs) recid high_bp_recid number := 0, -- backup piece (kccbp) recid high_bdf_recid number := 0, -- backup datafile (kccbf) recid high_cdf_recid number := 0, -- datafile copy (kccdc) recid high_brl_recid number := 0, -- backup redo log (kccbl) recid high_bcb_recid number := 0, -- backup datafile corruption recid high_ccb_recid number := 0, -- datafile copy corruption recid high_do_recid number := 0, -- deleted object recid high_pc_recid number := 0, -- proxy copy (kccpc) recid high_bsf_recid number := 0, -- backup SPFILE (kccbi) recid high_rsr_recid number := 0, -- RMAN status (kccrsr) recid high_nrsp_recid number := 0, -- normal restore point recid high_bcr_recid number := 0 -- high blk crpt (kccblkcor) recid ); init_sessionWaterMarks sessionWaterMarks_t; prev_sessionWaterMarks sessionWaterMarks_t; sessionWaterMarks sessionWaterMarks_t; -- data_types used in import catalog implementation type ts_name_list is table of ts.ts_name%type index by binary_integer; type numTab_t is table of number index by binary_integer; type key_columns_list is table of varchar2(30); -- variables for import catalog implementation -- -- list of database id that will be imported. import_dbid numTab_t; -- -- If you have a new column name that is generated by rman_seq, then -- add your new column here so that IMPORT CATALOG will know to -- increment that column value. -- -- Key column names that are used in rman catalog schema. They should -- end with key. These values must be generated by rman_seq. -- key_columns CONSTANT key_columns_list := key_columns_list ('DB_KEY' , 'DBINC_KEY' , 'CURR_DBINC_KEY' , 'PARENT_DBINC_KEY', 'CKP_KEY' , 'START_CKP_KEY' , 'END_CKP_KEY' , 'OFFR_KEY' , 'RR_KEY' , 'RLH_KEY' , 'AL_KEY' , 'BS_KEY' , 'BP_KEY' , 'BCF_KEY' , 'CCF_KEY' , 'XCF_KEY' , 'BSF_KEY' , 'BDF_KEY' , 'CDF_KEY' , 'XDF_KEY' , 'XAL_KEY' , 'BRL_KEY' , 'BDF_KEY' , 'RSR_KEY' , 'RSR_PKEY' , 'RSR_L0KEY' , 'SCR_KEY' , 'ROUT_SKEY' , 'SITE_KEY' , 'DF_KEY' , 'TF_KEY'); -- Global variables that represent dblink of the source recovery catalog -- and the offset at which the key columns has to be incremented import_dblink tempres.name%type; import_offset number; /*---------* * Cursors * *---------*/ -- Package Cursors: -- tsQ -- Used to resync the list of tablespaces. -- dfQ -- Used to resync the list of datafiles. -- tfQ -- Used to resync the list of tempfiles. -- rtQ -- Used to resync the list of threads. -- orlQ -- Used to resync the list of online redo logs. -- grspQ -- Used to resync the list of guaranteed restore point. -- scrlQ -- Used to fetch lines from a stored script. -- select all current tablespaces in this database incarnation cursor tsQ IS SELECT ts.ts_name, ts.ts#, ts.create_scn, ts.create_time, tsatt.rbs_count, ts.included_in_database_backup, ts.bigfile, ts.temporary, ts.encrypt_in_backup FROM ts, tsatt WHERE ts.dbinc_key = tsatt.dbinc_key AND ts.ts# = tsatt.ts# AND ts.create_scn = tsatt.create_scn AND ts.dbinc_key = this_dbinc_key AND ts.drop_scn IS NULL -- skip ones we know were dropped AND tsatt.end_ckp_key IS NULL ORDER BY ts.ts#; -- client passes rows to checkTs in -- ascending ts# order. We can detect -- new or dropped tablespaces this way. -- select all datafiles in this database incarnation at this site cursor dfQ IS SELECT df.file#, df.create_scn, df.create_time, df.plugin_scn, df.ts#, site_dfatt.fname, df.blocks, df.clone_fname, df.stop_scn, df.read_only, df.plugged_readonly, df.create_thread, df.create_size FROM df, site_dfatt WHERE df.dbinc_key = this_dbinc_key -- our dbinc please AND df.drop_scn IS NULL -- df not dropped AND this_site_key = site_dfatt.site_key(+) -- select names for the site AND df.df_key = site_dfatt.df_key(+) -- join site_dfatt to df ORDER BY df.file#; -- client passes rows to checkDf in -- ascending file# order. We can detect -- new datafiles this way -- select all tempfiles in this database incarnation cursor tfQ IS SELECT tf.file#, tf.create_scn, tf.create_time, tf.ts#, site_tfatt.fname, site_tfatt.blocks, site_tfatt.autoextend, site_tfatt.max_size, site_tfatt.next_size, tf.tf_key tf_key FROM tf, site_tfatt WHERE tf.dbinc_key = this_dbinc_key -- our dbinc please AND this_site_key = site_tfatt.site_key -- select names for the site AND tf.tf_key = site_tfatt.tf_key -- join site_tfatt to tf AND site_tfatt.drop_scn IS NULL -- tf not dropped ORDER BY tf.file#; -- client passes rows to checkTf in -- ascending file# order. We can detect -- new tempfiles 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 AND orl.site_key = this_site_key ORDER BY nlssort(orl.fname, 'NLS_COMP=ANSI NLS_SORT=ASCII7'); -- bug 2107554 -- select all guaranteed and preserved restore points cursor grspQ IS SELECT grsp.rspname, grsp.from_scn, grsp.to_scn FROM grsp, dbinc WHERE grsp.dbinc_key = dbinc.dbinc_key AND dbinc.db_key = this_db_key AND grsp.site_key = this_site_key ORDER BY nlssort(grsp.rspname, 'NLS_COMP=ANSI NLS_SORT=ASCII7'); -- select all undeleted pieces that match the device_type and handle -- as per backup sharing attributes. cursor bpq(device_type VARCHAR2, handle VARCHAR2, bp_recid VARCHAR2, bp_stamp VARCHAR2) IS SELECT bp_key, bs_key FROM bp WHERE db_key = this_db_key AND device_type = bpq.device_type AND ((disk_backups_shared = TRUE# AND bp.device_type = 'DISK') OR (tape_backups_shared = TRUE# AND bp.device_type <> 'DISK') OR (this_site_key = nvl(bp.site_key, this_site_key))) AND handle = bpq.handle AND handle_hashkey = substr(bpq.device_type,1,10) || substr(bpq.handle,1,10) || substr(bpq.handle,-10) 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 site_dfatt WHERE df_key in (select df_key from df, dbinc where df.dbinc_key = dbinc.dbinc_key and dbinc.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 reNorm_tfatt_c IS SELECT fname FROM site_tfatt WHERE tf_key in (select tf_key from tf, dbinc where tf.dbinc_key = dbinc.dbinc_key and dbinc.db_key = this_db_key) FOR UPDATE; cursor lscrnames_c(glob number, allnames number) IS select 1 oby, rdbi.db_name dname, s.scr_name sname, s.scr_comment scomm from db rdb, dbinc rdbi, scr s where lscrnames_c.glob is null and lscrnames_c.allnames is null and rdbi.dbinc_key = rdb.curr_dbinc_key and rdb.db_key = s.db_key and s.db_key = this_db_key and s.db_key is not NULL UNION ALL select 2, 'ORA%GLOB', s.scr_name, s.scr_comment from scr s where s.db_key IS NULL UNION ALL select 3, rdbi.db_name, s.scr_name, s.scr_comment from db rdb, dbinc rdbi, scr s where lscrnames_c.glob is null and lscrnames_c.allnames is not null and rdbi.dbinc_key = rdb.curr_dbinc_key and rdb.db_key = s.db_key and s.db_key is not NULL order by 1 asc, 2 asc, 3 asc; /*----------------------* * 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. -- tfRec -- Holds 1 row from tfQ. tfRec.file# is null when not doing a tempfile -- resync. tfRec.file# is set to MAXNUMVAL when tfQ 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. -- grspRec -- Holds 1 row from grspQ. grspRec.rspname is null when not doing a -- guaranteed restore point resync. grspRec.rspname is set to char(255) -- when grspQ reaches end-of-fetch. tsRec tsQ%rowtype; dfRec dfQ%rowtype; tfRec tfQ%rowtype; rtRec rtQ%rowtype; orlRec orlQ%rowtype; grspRec grspQ%rowtype; /*---------------* * Private Types * *---------------*/ /*-------------------* * Private functions * *-------------------*/ PROCEDURE setDebugOn(dbglevel IN NUMBER DEFAULT RCVCAT_LEVEL_DEFAULT) IS BEGIN -- -- Passing buffer_size as null is an undocumented way to buffer -- unlimited number of rows. PL/SQL storage is the limit -- dbms_output.enable(buffer_size => null); debug := TRUE; dbglvl := dbglevel; END; PROCEDURE setDebugOff IS BEGIN dbms_output.disable; -- free memory debug := FALSE; END; PROCEDURE deb(line IN varchar2 ,level IN number DEFAULT RCVCAT_LEVEL_DEFAULT) IS buffer_overflow exception; BEGIN if debOK(level) then dbms_output.put_line('DBGRCVCAT: '||line); end if; EXCEPTION WHEN others THEN dbms_output.put_line('caught exception during deb ' || substr(sqlerrm, 1, 512)); END deb; FUNCTION debOK(level IN number DEFAULT RCVCAT_LEVEL_DEFAULT) RETURN boolean IS BEGIN return (debug and dbglvl >= level); END debOK; -- 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; IF (this_site_key IS NULL) THEN raise_application_error(-20099, 'Database site key not set'); END IF; END checkResync; -- Note: this is a copy of a function in recover.txt function date2stamp(dt IN date) return number is stamp number; begin stamp := (((((to_number(to_char(dt, 'YYYY'))-1988)*12 + (to_number(to_char(dt, 'MM'))-1))*31 + (to_number(to_char(dt, 'DD'))-1))*24 + (to_number(to_char(dt, 'HH24'))))*60 + (to_number(to_char(dt, 'MI'))))*60 + (to_number(to_char(dt, 'SS'))); return stamp; end; -- Note: this is a copy of a function in recover.txt function stamp2date(stamp IN number) return date IS x number; dt varchar2(19); begin x := stamp; dt := to_char(mod(x,60), 'FM09'); -- seconds x := floor(x/60); dt := to_char(mod(x,60), 'FM09') || ':' || dt; -- minutes x := floor(x/60); dt := to_char(mod(x,24), 'FM09') || ':' || dt; -- hours x := floor(x/24); dt := to_char(mod(x,31)+1, 'FM09') || ' ' || dt; -- days x := floor(x/31); dt := to_char(mod(x,12)+1, 'FM09') || '/' || dt; -- months dt := to_char(floor(x/12)+1988) || '/' || dt; return to_date(dt, 'YYYY/MM/DD HH24:MI:SS'); end; -- recompute incarnation status for all incarnations from dbinc_key PROCEDURE recomputeDbincStatus(db_key IN NUMBER, dbinc_key IN NUMBER) IS -- Recursively calls itself to set status in its parent incarnation. PROCEDURE updateDbincStatus(db_key IN NUMBER, dbinc_key IN NUMBER) IS parent_key NUMBER; BEGIN BEGIN deb('updateDbincStatus - for db_key='||db_key||' dbinc='||dbinc_key); update dbinc set dbinc_status='PARENT' where dbinc_key = updateDbincStatus.dbinc_key and db_key = updateDbincStatus.db_key; -- find parent and then set its status select parent_dbinc_key into parent_key from dbinc where dbinc_key= updateDbincStatus.dbinc_key and db_key = updateDbincStatus.db_key; updateDbincStatus(db_key, parent_key); deb('updateDbincStatus - normal return for dbinc=' || dbinc_key); EXCEPTION WHEN no_data_found THEN deb('updateDbincStatus- Last parent is ' || dbinc_key); IF (dbinc_key is NOT NULL) THEN -- set last incarnation in the chain update dbinc set dbinc_status='PARENT' where dbinc_key=updateDbincStatus.dbinc_key and db_key = updateDbincStatus.db_key; END IF; return; -- reached the last known parent WHEN OTHERS THEN deb('updateDbincStatus - rollback all, release locks'); rollback; RAISE; END; END updateDbincStatus; BEGIN -- and make the given incarnation of the database as current UPDATE db SET curr_dbinc_key = recomputeDbincStatus.dbinc_key WHERE db_key = recomputeDbincStatus.db_key and db_key = recomputeDbincStatus.db_key; UPDATE dbinc SET dbinc_status='ORPHAN' WHERE dbinc.db_key = recomputeDbincStatus.db_key; updateDbincStatus(db_key, dbinc_key); UPDATE dbinc SET dbinc_status='CURRENT' where dbinc_key=recomputeDbincStatus.dbinc_key and db_key = recomputeDbincStatus.db_key; END recomputeDbincStatus; /*-------------------* * 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 = catalogVersion; 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, upper(db_name), reset_scn,reset_time); SELECT rman_seq.currval INTO local.dbinc_key FROM dual; -- make it current recomputeDbincStatus(local.db_key, local.dbinc_key); deb('registerDatabase - adding a row to site table, with null db_unique_name'); INSERT INTO node(db_key, force_resync2cf, database_role, site_key) VALUES(local.db_key, 'NO', 'PRIMARY', rman_seq.nextval); setReason(RESYNC_REASON_NOACTION); deb('registerDatabase - commit, release locks'); commit; -- rollback on error EXCEPTION WHEN OTHERS THEN deb('registerDatabase - rollback, released all locks'); 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 = catalogVersion; 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, upper(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; -- recompute chain of incarnation, and make it current recomputeDbincStatus(local.db_key, local.dbinc_key); deb('resetDatabase - commit, release locks'); commit; -- rollback on error EXCEPTION WHEN OTHERS THEN deb('resetDatabase - rollback, released all locks'); rollback; RAISE; END resetDatabase; -- reset database to specified incarnation. FUNCTION 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 ) RETURN NUMBER 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 = catalogVersion; 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; BEGIN SELECT db_key INTO local.db_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; SELECT dbinc_key INTO local.dbinc_key FROM dbinc WHERE dbinc.db_key = local.db_key AND dbinc.reset_scn = resetDatabase.reset_scn AND dbinc.reset_time = resetDatabase.reset_time; resetDatabase(local.dbinc_key, db_name); RETURN local.dbinc_key; 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 = catalogVersion; 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 (upper(db_name) <> local.db_name OR db_name IS NULL) THEN raise_application_error(-20004, 'Database name does not match'); END IF; -- recompute chain of incarnation recomputeDbincStatus(local.db_key, resetDatabase.dbinc_key); deb('resetDatabase - commit, release locks'); commit; -- rollback on error EXCEPTION WHEN OTHERS THEN deb('resetDatabase - rollback, released all locks'); rollback; RAISE; END resetDatabase; procedure resetDatabase( dbinc_key IN number ,db_name IN varchar2 ,reset_scn OUT number ,reset_time OUT date ,db_id IN number DEFAULT NULL ) IS local_db_key dbinc.db_key%TYPE; BEGIN IF db_id IS NOT NULL THEN BEGIN SELECT db_key INTO local_db_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; ELSE local_db_key := this_db_key; END IF; BEGIN SELECT reset_scn, reset_time INTO resetDatabase.reset_scn, resetDatabase.reset_time FROM dbinc WHERE dbinc.dbinc_key = resetDatabase.dbinc_key AND (db_id IS NULL OR dbinc.db_key = local_db_key); EXCEPTION WHEN no_data_found THEN raise_application_error(-20010, 'Database incarnation not found'); END; resetDatabase(dbinc_key, db_name); END resetDatabase; PROCEDURE unRegisterDatabase( db_key IN NUMBER DEFAULT NULL ,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_id = unRegisterDatabase.db_id; EXCEPTION WHEN no_data_found THEN raise_application_error(-20001, 'Database not found'); END; DELETE FROM db WHERE db.db_id = unRegisterDatabase.db_id; deb('unRegisterDatabase - commit, release locks'); commit; -- rollback on error EXCEPTION WHEN OTHERS THEN deb('unregisterDatabase - rollback, released all locks'); rollback; RAISE; END unRegisterDatabase; -- set Archive log file sharing scope attributes for the session PROCEDURE setArchiveFileScopeAttributes(logs_shared IN NUMBER) IS BEGIN deb('setArchiveFileScopeAttributes'); IF logs_shared > 0 THEN dbms_rcvcat.logs_shared := TRUE#; ELSE dbms_rcvcat.logs_shared := FALSE#; END IF; deb('logs_shared = ' || dbms_rcvcat.logs_shared); dbms_rcvman.setArchiveFileScopeAttributes(logs_shared); deb('exiting setArchiveFileScopeAttributes'); END setArchiveFileScopeAttributes; -- set Backup file sharing scope attributes for the session PROCEDURE setBackupFileScopeAttributes( disk_backups_shared IN NUMBER, tape_backups_shared IN NUMBER) IS lsite_key NUMBER; BEGIN deb('setBackupFileScopeAttributes'); IF disk_backups_shared IS NOT NULL THEN IF disk_backups_shared > 0 THEN dbms_rcvcat.disk_backups_shared := TRUE#; ELSE dbms_rcvcat.disk_backups_shared := FALSE#; END IF; END IF; IF tape_backups_shared IS NOT NULL THEN IF tape_backups_shared > 0 THEN dbms_rcvcat.tape_backups_shared := TRUE#; ELSE dbms_rcvcat.tape_backups_shared := FALSE#; END IF; END IF; deb('disk_backups_shared='||dbms_rcvcat.disk_backups_shared); deb('tape_backups_shared='||dbms_rcvcat.tape_backups_shared); dbms_rcvman.setBackupFileScopeAttributes(disk_backups_shared, tape_backups_shared); deb('exiting setBackupFileScopeAttributes'); END setBackupFileScopeAttributes; /*--------------* * 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 ,db_unique_name IN VARCHAR2 ,dummy_instance IN BOOLEAN ,cf_type IN NUMBER ,site_aware IN BOOLEAN default FALSE) IS local dbinc%rowtype; -- local variables current_inc VARCHAR2(3); dbnm dbinc.db_name%TYPE; dbnm_in dbinc.db_name%TYPE; rid varchar2(18); local_site_key number; dbunqnm node.db_unique_name%TYPE; db_role node.database_role%type; dbunqnm_in node.db_unique_name%TYPE; cat_version varchar2(12); vpd_version varchar2(12); prim_dbunqnm_in node.db_unique_name%TYPE; db_id_in number; tmp_dbunqnm_cnt number; tmp_primary_cnt number; BEGIN -- 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(-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 using a virtual private catalog, and the base catalog has been -- upgraded, then upgrade the private catalog. IF user <> dbms_catowner THEN BEGIN SELECT max(version) INTO cat_version FROM rcver; SELECT version INTO vpd_version FROM vpc_users; IF cat_version <> vpd_version OR vpd_version IS NULL THEN create_virtual_catalog; END IF; EXCEPTION WHEN OTHERS THEN raise_application_error(-20013, 'Error upgrading virtual private catalog', true); END; END IF; this_db_key := NULL; -- clear in case exception raised this_dbinc_key := NULL; dbnm_in := upper(db_name); dbunqnm_in := upper(db_unique_name); db_id_in := db_id; <> -- 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_in IS NOT NULL) THEN BEGIN SELECT db_key, curr_dbinc_key, db_name INTO local.db_key, local.dbinc_key, local.db_name FROM db WHERE db.db_id = db_id_in; -- 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 (dbnm_in is NOT NULL AND db_id is NOT NULL) THEN -- Now validate that the resetlogs SCN we were passed matches that -- of the current incarnation of this database. If not, then -- a reset database should be done, or the wrong controlfile is -- mounted. BEGIN SELECT decode(dbinc.dbinc_key, db.curr_dbinc_key, 'YES', 'NO'), dbinc.db_name, dbinc.rowid INTO current_inc, dbnm, rid FROM db, dbinc WHERE db.db_key = dbinc.db_key AND db.db_id = setDatabase.db_id AND dbinc.reset_scn = setDatabase.reset_scn AND dbinc.reset_time = setDatabase.reset_time; EXCEPTION WHEN no_data_found THEN raise_application_error(-20003, 'Database incarnation not found'); END; IF (current_inc = 'NO') THEN raise_application_error(-20011, 'Database incarnation not current'); END IF; IF (dbnm != dbnm_in) THEN UPDATE dbinc SET dbinc.db_name = dbnm_in WHERE rowid = rid; deb('setDatabase - commit, release locks'); COMMIT; END IF; END IF; IF (NOT dummy_instance AND dbunqnm_in IS NOT NULL) THEN deb('setDatabase - check db_unique_name= ' || dbunqnm_in || ' cf_type= ' || cf_type); -- If we are seeing a database with a non-null db_unique_name for -- the first time after upgrade from 9i, set the value of null -- db_unique_name to current one and fall through. Note that there can -- be only one row with null db_unique_name value due to unique -- constraint on this column and dbid. Also assert upgrade went thru -- as expected. SELECT count(*) into tmp_dbunqnm_cnt FROM node WHERE node.db_unique_name is NULL AND node.db_key = local.db_key; IF tmp_dbunqnm_cnt = 1 THEN SELECT count(*) into tmp_dbunqnm_cnt FROM node WHERE node.db_unique_name is not NULL AND node.db_key = local.db_key; IF tmp_dbunqnm_cnt > 0 THEN raise_application_error(-20999, 'internal error: found non-null and null site name'); END IF; UPDATE NODE SET node.db_unique_name = dbunqnm_in WHERE node.db_unique_name is NULL AND node.db_key = local.db_key; deb('setDatabase: updating null db_unique_name with ' ||dbunqnm_in || 'number of rows updated ' || sql%rowcount); END IF; BEGIN -- change database_role if it is changed now SELECT node.database_role, site_key INTO db_role, local_site_key FROM node WHERE node.db_key = local.db_key AND node.db_unique_name = dbunqnm_in; -- count if any other databases are marked as primary other than -- current one SELECT count(*) into tmp_primary_cnt FROM node WHERE node.database_role = 'PRIMARY' AND site_key <> local_site_key AND node.db_key = local.db_key; deb('setDatabase - check database_role'); IF (cf_type = CF_STANDBY AND db_role != 'STANDBY') THEN -- controlfile is standby but not the database role deb('setDatabase - database role not standby - updating'); UPDATE node SET node.database_role = 'STANDBY', node.high_conf_recid = 0 WHERE site_key = local_site_key; COMMIT; ELSIF ((cf_type = CF_CURRENT OR cf_type = CF_BACKUP) AND (db_role != 'PRIMARY' OR tmp_primary_cnt > 1)) THEN -- controlfile is for primary but not the database role deb('setDatabase - not primary or primary_cnt='||tmp_primary_cnt); -- change current primary to standby and make new as primary UPDATE node SET node.database_role = 'STANDBY', node.high_conf_recid = 0 WHERE site_key <> local_site_key AND db_key = local.db_key; -- whenever we see a new primary database, resync the fixed -- record section to reflect the new primary database -- information. We only need to get the incremental changes -- from circular sections. Hence no need to reset those pointers. UPDATE node SET node.database_role = 'PRIMARY', node.high_conf_recid = 0, high_ic_recid = 0, high_ts_recid = NULL, high_df_recid = NULL, high_rt_recid = NULL, high_orl_recid = NULL, high_tf_recid = 0 WHERE site_key = local_site_key AND db_key = local.db_key; sessionWaterMarks.high_ic_recid := 0; COMMIT; prev_sessionWaterMarks := sessionWaterMarks; END IF; EXCEPTION WHEN no_data_found THEN IF (cf_type = CF_CURRENT OR cf_type = CF_BACKUP) THEN deb('setDatabase: found new primary database...'); -- change current primary to standby and make current as primary UPDATE node SET node.database_role = 'STANDBY', node.high_conf_recid = 0 WHERE db_key = local.db_key; INSERT INTO node(db_unique_name, db_key, force_resync2cf, database_role, site_key) VALUES(dbunqnm_in, local.db_key, 'NO', 'PRIMARY', rman_seq.nextval); COMMIT; ELSIF cf_type = CF_STANDBY THEN -- New standby site detected deb('setDatabase: found new standby database...'); INSERT INTO node(db_unique_name, db_key, force_resync2cf, database_role, site_key) VALUES(dbunqnm_in, local.db_key, 'NO', 'STANDBY', rman_seq.nextval); COMMIT; ELSE -- for all practical purposes, assume this to be primary site -- when the control file becomes, primary we will automatically -- rename the db_unique_name. Till then use the current -- primary database db_unique_name. -- Note, we must find one row, otherwise the upgrade did not -- work as expected for this database. BEGIN deb('setDatabase - falking db_unique_name from'|| dbunqnm_in); SELECT db_unique_name into prim_dbunqnm_in from node WHERE db_key = local.db_key AND database_role = 'PRIMARY'; dbunqnm_in := prim_dbunqnm_in; deb('setDatabase - changing dbunqnm_in to ' || dbunqnm_in); EXCEPTION WHEN no_data_found THEN deb('setDatabase - unknown dbunqnm_in set to null'); dbunqnm_in := null; END; END IF; END; END IF; -- if db_id is unknown, try using db_name ELSIF (dbnm_in IS NOT NULL) THEN BEGIN SELECT db.db_key, db.curr_dbinc_key, db.db_id INTO local.db_key, local.dbinc_key, db_id_in FROM db, dbinc WHERE db.curr_dbinc_key = dbinc.dbinc_key AND dbinc.db_name = dbnm_in; 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; GOTO now_try_with_dbid; ELSE raise_application_error(-20006, 'Database name is missing'); END IF; -- In case that db_unique_name is NULL, then we know this is pre-10i -- database. Or, it is 11g with new db_unique_name in nomount state. this_db_unique_name := dbunqnm_in; this_db_key := local.db_key; this_dbinc_key := local.dbinc_key; deb('setDatabase - this_db_unique_name=' ||this_db_unique_name); deb('setDatabase - this_dbinc_key:'||to_char(this_dbinc_key)); BEGIN select site_key into this_site_key from node where db_unique_name=upper(dbunqnm_in) AND db_key = this_db_key; deb('setDatabase - this_site_key:'||this_site_key); EXCEPTION WHEN no_data_found THEN BEGIN select site_key, db_unique_name into this_site_key, dbunqnm_in from node where database_role='PRIMARY' AND db_key = this_db_key; deb('setDatabase - this_site_key(primary):'||this_site_key); EXCEPTION WHEN no_data_found THEN -- in 11g, all sites known for db can be standby and we don't -- know the current primary. deb('setDatabase - this_site_key is null'); this_site_key := null; END; END; cntbs := 0; -- call setDatabase from rcvman package. Note that in this we call -- recovery catalog version! dbms_rcvman.setDatabase (dbnm_in, reset_scn, reset_time, db_id, this_db_unique_name, site_aware, dummy_instance); client_site_aware := site_aware; IF client_site_aware THEN setArchiveFileScopeAttributes(logs_shared => 0); setBackupFileScopeAttributes (disk_backups_shared => 0, tape_backups_shared => 1); END IF; END setDatabase; -- Exists for compatability PROCEDURE setDatabase(db_name IN VARCHAR2 ,reset_scn IN NUMBER ,reset_time IN DATE ,db_id IN NUMBER ,db_unique_name IN VARCHAR2 DEFAULT NULL) IS BEGIN setDatabase(db_name => db_name, reset_scn => reset_scn, reset_time => reset_time, db_id => db_id, db_unique_name => db_unique_name, dummy_instance => FALSE, cf_type => CF_CURRENT); END setDatabase; -- These two versions of setDatabase are not used by RMAN, they are shorthand -- methods of invoking setDatabase when you are accessing the recovery catalog -- schema from a tool like Sql*Plus, and you want to invoke some of the -- stored procecdures in the dbms_rcvcat or dbms_rcvman packages. PROCEDURE setDatabase(dbinc_key number) IS dbinc_row dbinc%ROWTYPE; db_row db%ROWTYPE; BEGIN select * into dbinc_row from dbinc where dbinc_key = setDatabase.dbinc_key; select * into db_row from db where db_key = dbinc_row.db_key; setDatabase(db_name => dbinc_row.db_name, reset_scn => dbinc_row.reset_scn, reset_time => dbinc_row.reset_time, db_id => db_row.db_id); END setDatabase; procedure setDatabase IS dbinckey number; BEGIN select curr_dbinc_key into dbinckey from db; setDatabase(dbinckey); END setDatabase; /*-----------------------------* * Recovery Catalog Checkpoint * *-----------------------------*/ PROCEDURE lockForCkpt IS local_dbid NUMBER; start_time DATE := sysdate; BEGIN IF (this_ckp_key IS NOT NULL) THEN raise_application_error(-20030, 'Resync in progress'); END IF; IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; -- We need to acquire lock on db before reading target database cf -- in order to give ckptNeeded with correct water marks. -- Otherwise RMAN may signal RMAN-20035 or RMAN-20033 errors when -- multiple resyncs are done in parallel. SELECT db_id INTO local_dbid FROM db WHERE db_key = this_db_key FOR UPDATE; deb('lockForCkpt - took ' || ((sysdate - start_time) * 86400) || ' seconds'); deb('lockForCkpt - Obtained all locks for db ' || to_char(this_db_key)); -- The locks obtained here will be released by one of ckptNeeded, -- cancelCkpt, or endCkpt. END lockForCkpt; FUNCTION ckptNeeded( ckp_scn IN NUMBER ,ckp_cf_seq IN NUMBER ,cf_version IN DATE ,cf_type IN NUMBER ,high_df_recid IN NUMBER ,high_orl_recid IN NUMBER ,high_cdf_recid IN NUMBER ,high_al_recid IN NUMBER ,high_bp_recid IN NUMBER ,high_do_recid IN NUMBER ,high_offr_recid IN NUMBER ,high_pc_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_conf_recid IN NUMBER DEFAULT NULL -- for compatibility ,rltime IN DATE DEFAULT NULL -- for compatibility ,high_ts_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_bs_recid IN NUMBER DEFAULT NULL -- for compatibility ,lopen_reset_scn IN number DEFAULT NULL -- for compatibility ,lopen_reset_time IN DATE DEFAULT NULL -- for compatibility ,high_ic_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_tf_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_rt_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_grsp_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_nrsp_recid IN NUMBER DEFAULT NULL -- for compatibility ,high_bcr_recid IN NUMBER DEFAULT NULL -- for compatibility ) RETURN NUMBER IS ckp_type NUMBER; local node%rowtype; local_dbid NUMBER := 0; local_reset_time DATE; local_reset_scn NUMBER := 0; cksum NUMBER; BEGIN IF (this_ckp_key IS NOT NULL) THEN raise_application_error(-20030, 'Resync in progress'); END IF; IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; IF (this_site_key IS NULL) THEN raise_application_error(-20199, 'Site key is not set'); END IF; SELECT db_id INTO local_dbid FROM db WHERE db.db_key = this_db_key FOR UPDATE; deb('ckptNeeded - Obtained all locks for database ' || to_char(this_db_key)); -- add the new recid that you add for resync here cksum := high_df_recid + high_orl_recid + high_cdf_recid + high_al_recid + high_bp_recid + high_do_recid + high_offr_recid + nvl(high_pc_recid, 0) + nvl(high_conf_recid, 0) + nvl(high_ts_recid, 0) + nvl(high_bs_recid, 0) + nvl(high_ic_recid, 0) + nvl(high_tf_recid, 0) + nvl(high_rt_recid, 0) + nvl(high_grsp_recid, 0) + nvl(high_nrsp_recid, 0) + nvl(high_bcr_recid, 0); -- 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 do not need to deal -- with nulls. SELECT cf_create_time, nvl(high_df_recid,0), nvl(high_ts_recid,0), nvl(high_orl_recid,0), nvl(high_cdf_recid,0), nvl(high_al_recid,0), nvl(high_bp_recid,0), nvl(high_do_recid,0), nvl(high_offr_recid,0), nvl(high_pc_recid,0), full_ckp_cf_seq, job_ckp_cf_seq, nvl(high_ic_recid,0), nvl(high_bs_recid,0), nvl(high_tf_recid, 0), nvl(high_rt_recid, 0), nvl(high_grsp_recid, 0), nvl(high_nrsp_recid, 0), nvl(high_bcr_recid, 0), high_conf_recid, force_resync2cf INTO local.cf_create_time, local.high_df_recid, local.high_ts_recid, local.high_orl_recid, local.high_cdf_recid, local.high_al_recid, local.high_bp_recid, local.high_do_recid, local.high_offr_recid, local.high_pc_recid, local.full_ckp_cf_seq, local.job_ckp_cf_seq, local.high_ic_recid, local.high_bs_recid, local.high_tf_recid, local.high_rt_recid, local.high_grsp_recid, local.high_nrsp_recid, local.high_bcr_recid, local.high_conf_recid, local.force_resync2cf FROM node WHERE site_key = this_site_key; SELECT reset_scn, reset_time into local_reset_scn, local_reset_time FROM dbinc WHERE dbinc_key = this_dbinc_key; ckp_type := RESYNC_NONE; setReason(RESYNC_REASON_NONE); IF (rltime IS NOT NULL AND rltime != local_reset_time) THEN -- We have not yet issued a RESET DATABASE after doing RESETLOGS, -- or we are not using the latest incarnation. In either case, we -- do not want to do implicit resync now, so tell caller resync is not -- needed now. -- rltime will be NULL if called from a PRE-8.2 RMAN, in which case -- we cannot check it, nor is the check needed for PRE-8.2. deb('ckptNeeded - rltime='||to_char(rltime)|| ', local_reset_time='||to_char(local_reset_time)); ckp_type := RESYNC_NONE; GOTO ret; ELSIF (cf_version = local.cf_create_time) THEN deb('ckptNeeded - local_reset_scn='||local_reset_scn|| ' lopen_reset_scn='||lopen_reset_scn); deb('ckptNeeded - local_reset_time='||local_reset_time|| ' lopen_reset_time='||lopen_reset_time); -- 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 AND (lopen_reset_scn IS NULL or local_reset_scn = lopen_reset_scn) AND (lopen_reset_time IS NULL or local_reset_time = lopen_reset_time)) THEN deb('ckptNeeded - high_ts_recid='||to_char(high_ts_recid)|| ', local.high_ts_recid='||to_char(local.high_ts_recid)); IF (high_ts_recid > local.high_ts_recid) THEN ckp_type := RESYNC_FULL; IF local.high_ts_recid = 0 THEN setReason(RESYNC_REASON_NOACTION); ELSE setReason(RESYNC_REASON_TS); END IF; GOTO ret; ELSIF (high_ts_recid < local.high_ts_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_df_recid='||to_char(high_df_recid)|| ', local.high_df_recid='||to_char(local.high_df_recid)); IF (high_df_recid > local.high_df_recid) THEN ckp_type := RESYNC_FULL; setReason(RESYNC_REASON_DF); GOTO ret; ELSIF (high_df_recid < local.high_df_recid) THEN -- the high recid in the controlfile should never be less than -- the one in the recovery catalog. If we ever get here it probably -- means that the user made an operational error such as restoring -- controlfile from an old copy made using os utilities. raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_tf_recid='||to_char(high_tf_recid)|| ', local.high_tf_recid='||to_char(local.high_tf_recid)); IF (high_tf_recid > local.high_tf_recid) THEN ckp_type := RESYNC_FULL; setReason(RESYNC_REASON_TF); GOTO ret; ELSIF (high_tf_recid < local.high_tf_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_rt_recid='||to_char(high_rt_recid)|| ', local.high_rt_recid='||to_char(local.high_rt_recid)); IF (high_rt_recid > local.high_rt_recid) THEN ckp_type := RESYNC_FULL; setReason(RESYNC_REASON_THR); GOTO ret; ELSIF (high_rt_recid < local.high_rt_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_orl_recid='||to_char(high_orl_recid)|| ', local.high_orl_recid='||to_char(local.high_orl_recid)); IF (high_orl_recid > local.high_orl_recid) THEN ckp_type := RESYNC_FULL; setReason(RESYNC_REASON_ORL); GOTO ret; ELSIF (high_orl_recid < local.high_orl_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; -- We will say that we need full resync only if high_conf_recid does not -- local.high_conf_recid (RC and CF have different recids), or if -- local.force_resync2cf is set to TRUE. deb('ckptNeeded - high_conf_recid='||high_conf_recid|| ', local.high_conf_recid='||local.high_conf_recid); deb(' local.force_resync2cf='||local.force_resync2cf); IF (high_conf_recid != local.high_conf_recid OR local.force_resync2cf = 'YES') THEN ckp_type := RESYNC_FULL; setReason(RESYNC_REASON_CONF); GOTO ret; END IF; -- For backup/standby control file since we always do partial resync, -- configuration record changes are obtained, When that changes, we will -- have to trigger atleast partial resync. deb('ckptNeeded - high_cdf_recid='||to_char(high_cdf_recid)|| ', local.high_cdf_recid='||to_char(local.high_cdf_recid)); IF (high_cdf_recid > local.high_cdf_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_cdf_recid < local.high_cdf_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_al_recid='||to_char(high_al_recid)|| ', local.high_al_recid='||to_char(local.high_al_recid)); IF (high_al_recid > local.high_al_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_al_recid < local.high_al_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_bp_recid='||to_char(high_bp_recid)|| ', local.high_bp_recid='||to_char(local.high_bp_recid)); IF (high_bp_recid > local.high_bp_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_bp_recid < local.high_bp_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_bs_recid='||to_char(high_bs_recid)|| ', local.high_bs_recid='||to_char(local.high_bs_recid)); IF (high_bs_recid > local.high_bs_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_bs_recid < local.high_bs_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_do_recid='||to_char(high_do_recid)|| ', local.high_do_recid='||to_char(local.high_do_recid)); IF (high_do_recid > local.high_do_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_do_recid < local.high_do_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_offr_recid='||to_char(high_offr_recid)|| ', local.high_offr_recid='||to_char(local.high_offr_recid)); IF (high_offr_recid > local.high_offr_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_offr_recid < local.high_offr_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_pc_recid='||to_char(high_pc_recid)|| ', local.high_pc_recid='||to_char(local.high_pc_recid)); IF (high_pc_recid > local.high_pc_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_pc_recid < local.high_pc_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded - high_ic_recid='||to_char(high_ic_recid)|| ', local.high_ic_recid='||to_char(local.high_ic_recid)); IF (high_ic_recid > local.high_ic_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_ic_recid < local.high_ic_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded: high_grsp_recid='||to_char(high_grsp_recid)|| ', local.high_grsp_recid='||to_char(local.high_grsp_recid)); IF (high_grsp_recid > local.high_grsp_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_grsp_recid < local.high_grsp_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded: high_bcr_recid='||to_char(high_bcr_recid)|| ', local.high_bcr_recid='||to_char(local.high_bcr_recid)); IF (high_bcr_recid > local.high_bcr_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_bcr_recid < local.high_bcr_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; deb('ckptNeeded: high_nrsp_recid='||to_char(high_nrsp_recid)|| ', local.high_nrsp_recid='||to_char(local.high_nrsp_recid)); IF (high_nrsp_recid > local.high_nrsp_recid) THEN ckp_type := RESYNC_PARTIAL; GOTO ret; ELSIF (high_nrsp_recid < local.high_nrsp_recid) THEN raise_application_error(-20035, 'Invalid high recid'); END IF; ELSE -- Same backup or standby control file seen on site. IF (cksum = last_resync_cksum AND kccdivts = date2stamp(cf_version)) THEN deb('ckptNeeded - resync checksum same as last checksum'); ckp_type := RESYNC_NONE; ELSE ckp_type := RESYNC_PARTIAL; last_resync_cksum := cksum; END IF; END IF; ELSE -- The controlfile is different from the one seen at the last resync. -- If the control file is current then the database must have been -- opened since the restore and a full resync is needed. Otherwise -- we can only do a partial resync. IF (cf_type = CF_CURRENT) THEN deb('ckptNeeded - cf_type = CF_CURRENT'); ckp_type := RESYNC_FULL; setReason(RESYNC_REASON_CF); ELSE deb('ckptNeeded - cf_type != CF_CURRENT'); IF (cksum = last_resync_cksum AND kccdivts = date2stamp(cf_version)) THEN deb('ckptNeeded - resync checksum same as last checksum'); ckp_type := RESYNC_NONE; ELSE ckp_type := RESYNC_PARTIAL; last_resync_cksum := cksum; END IF; END IF; END IF; <> -- 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 do not need a resync. -- One of the circular record high water marks may still be zero because -- we have not been passed any records since we reset the high water mark -- to zero. This happens when a backup controlfile is mounted and no -- new circular records have been added for some record type. IF (ckp_type = RESYNC_PARTIAL AND cf_version = local.cf_create_time AND ckp_cf_seq = greatest(local.job_ckp_cf_seq, local.full_ckp_cf_seq)) THEN deb('ckptNeeded - cf_seq has not advanced - do not need a resync'); ckp_type := RESYNC_NONE; END IF; -- if resync is not needed, release the lock on dbinc and conf. IF (ckp_type = RESYNC_NONE) THEN deb('ckptNeeded - resync not needed, rollback, released all locks'); rollback; END IF; deb('ckptNeeded - returning ckp_type='||ckp_type); RETURN ckp_type; -- rollback on error to release the lock on dbinc EXCEPTION WHEN OTHERS THEN deb('ckptNeeded - error, rollback, released all locks'); rollback; RAISE; END ckptNeeded; PROCEDURE beginCkpt( ckp_scn IN NUMBER ,ckp_cf_seq IN NUMBER ,cf_version IN DATE ,ckp_time IN DATE ,ckp_type IN VARCHAR2 ,ckp_db_status IN VARCHAR2 ,high_df_recid IN NUMBER ,cf_type IN VARCHAR2 DEFAULT 'CURRENT' -- for compatibility reasons ) IS local ckp%rowtype; node_count NUMBER; db_role node.database_role%type; local_dbid NUMBER; local_reset_watermarks boolean := TRUE; 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; IF (this_site_key IS NULL) THEN raise_application_error(-20199, 'Site key is not set'); END IF; clearResyncActions; deb('beginCkpt - ckp_type = '||ckp_type||', cf_type ='||cf_type || ', ckp_scn =' || ckp_scn); -- lock the db table row to allow only one checkpoint at a time (per db) SELECT db_id INTO local_dbid FROM db WHERE db_key = this_db_key FOR UPDATE; deb('beginCkpt - Obtained all locks for db '|| to_char(this_db_key)); -- 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 do not 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 -- select the information needed to ensure that the checkpoint is valid SELECT ckp_scn, cf_create_time, decode(beginCkpt.ckp_type, 'FULL', full_ckp_cf_seq, greatest(job_ckp_cf_seq, full_ckp_cf_seq)), dbinc_key INTO local.ckp_scn, local.cf_create_time, local.ckp_cf_seq, local.dbinc_key FROM node WHERE site_key = this_site_key; -- save the control file version used for resync at this site last_cf_version_time := local.cf_create_time; -- find the previous checkpoint for this dbinc SELECT max(ckp_key) INTO local.ckp_key FROM ckp WHERE dbinc_key = this_dbinc_key; IF (local.ckp_key IS NULL) THEN deb('beginCkpt - first checkpoint for this incarnation '|| this_dbinc_key); local_reset_watermarks := TRUE; ELSIF (cf_type = 'CURRENT' OR (cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN -- If this is a full resync then make sure that the controlfile -- checkpoint scn is not less than the highest checkpoint scn so far. -- We cannot allow full resync from an old controlfile because it might -- logically corrupt the tablespace and datafile information. -- This check is skipped for a partial resync does not usually have -- a checkpoint scn and partial resync from an old controlfile is harmless IF (ckp_type = 'FULL' AND this_dbinc_key = local.dbinc_key) THEN IF (ckp_scn < local.ckp_scn) THEN deb('beginCkpt - cf scn='||ckp_scn||',catalog cf scn='||local.ckp_scn); raise_application_error(-20032, 'Invalid checkpoint SCN'); ELSIF (ckp_scn = local.ckp_scn AND ckp_cf_seq < local.ckp_cf_seq) THEN deb('beginCkpt - cf seq='||ckp_cf_seq||',catalog cf seq='|| local.ckp_cf_seq); raise_application_error(-20033, 'Invalid checkpoint cf seq#'); ELSIF (ckp_scn = local.ckp_scn AND ckp_cf_seq = local.ckp_cf_seq) THEN raise_application_error(-20034, 'Resync not needed'); END IF; END IF; IF (cf_version = local.cf_create_time) THEN deb('beginCkpt - Resync from same last control file'); -- 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 AND this_dbinc_key = local.dbinc_key) THEN deb('beginCkpt - cf seq='||ckp_cf_seq||',catalog cf seq='|| local.ckp_cf_seq); raise_application_error(-20033, 'Invalid checkpoint cf seq#'); ELSIF (ckp_cf_seq = local.ckp_cf_seq AND this_dbinc_key = local.dbinc_key) THEN raise_application_error(-20034, 'Resync not needed'); END IF; local_reset_watermarks := FALSE; ELSE deb('beginCkpt - Resync from different control file'); local_reset_watermarks := TRUE; END IF; ELSE -- if mount status is BACKUP, then this is a explicit resync from -- a control file copy or a backup controlfile. IF (ckp_db_status = 'BACKUP') THEN deb('beginCkpt - Resync from control file copy'); local_reset_watermarks := TRUE; ELSE -- if control version is same as seen before, then this must be -- same controlfile that we previously resynced. IF (kccdivts = sessionWaterMarks.last_kccdivts) THEN deb('beginCkpt - Resync from same backup control file'); local_reset_watermarks := FALSE; ELSE deb('beginCkpt - Resync from different backup control file'); local_reset_watermarks := TRUE; END IF; END IF; END IF; IF (local_reset_watermarks) THEN deb('beginCkpt - init session watermarks'); sessionWaterMarks := init_sessionWaterMarks; sessionWaterMarks.last_kccdivts := kccdivts; END IF; -- For now, the water marks are kept track only for current and -- standby control file (if db_unique_name is not null). IF (cf_type = 'CURRENT' OR (cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN IF NOT local_reset_watermarks THEN deb('beginCkpt - update ckp_scn and use existing water marks'); -- update the ckp_scn and ckp_cf_seqs for the next resync. UPDATE node 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)), bcr_in_use = nvl2(high_bcr_recid, 'YES', 'NO') WHERE site_key = this_site_key; ELSE -- This has to be one of the following cases: -- 1) It might be first checkpoint for this incarnation -- 2) The control file is different from the last time at this node -- 3) The incarnation at which this site did last time resync is -- different than the current incarnation. -- 4) It may be created control file in same incarnation when last time -- resync was done. -- In all these cases, the water marks in node table are not correct, -- and hence reset them. -- -- bug 5932029 also set high_id_recid = 0 deb('beginCkpt - update ckp_scn and reset water marks, this_site_key '|| this_site_key); UPDATE node SET cf_create_time = beginCkpt.cf_version, dbinc_key = this_dbinc_key, 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_ic_recid = 0, high_ts_recid = NULL, high_df_recid = NULL, high_rt_recid = NULL, high_orl_recid = NULL, high_offr_recid = 0, high_rlh_recid = 0, high_al_recid = 0, high_bs_recid = 0, high_bp_recid = 0, high_bdf_recid = 0, high_cdf_recid = 0, high_brl_recid = 0, high_bcb_recid = 0, high_ccb_recid = 0, high_do_recid = 0, high_pc_recid = 0, high_bsf_recid = 0, high_rsr_recid = 0, high_tf_recid = 0, high_grsp_recid = 0, high_nrsp_recid = 0, high_bcr_recid = 0, bcr_in_use = nvl2(high_bcr_recid, 'YES', 'NO') WHERE site_key = this_site_key; END IF; ELSE -- remember the control file version and ckp_scn at this node UPDATE node SET cf_create_time = beginCkpt.cf_version, dbinc_key = this_dbinc_key, 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), bcr_in_use = nvl2(high_bcr_recid, 'YES', 'NO') WHERE site_key = this_site_key; END IF; -- record resyncs BEGIN INSERT INTO ckp (ckp_key, ckp_scn, ckp_cf_seq, cf_create_time, ckp_time, dbinc_key, ckp_type, ckp_db_status, resync_time, site_key) VALUES (rman_seq.nextval, ckp_scn, ckp_cf_seq, cf_version, ckp_time, this_dbinc_key, beginCkpt.ckp_type, ckp_db_status, sysdate, this_site_key); -- set package state variables SELECT rman_seq.currval INTO this_ckp_key FROM dual; EXCEPTION WHEN dup_val_on_index THEN IF (cf_type = 'CURRENT' OR (cf_type = 'STANDBY' AND this_db_unique_name IS NOT NULL)) THEN RAISE; ELSE -- this is a non-current controlfile resync. Because we do not track -- the high water marks for this type of controlfile, ckptNeeded -- always returns PARTIAL resync for non-current controlfile. -- So, we optimize here that resync is not needed if no controlfile -- txn is done. But, if some controlfile txn is done, then ALL -- circular records (including duplicate) are resynced. raise_application_error(-20034, 'Resync not needed'); END IF; END; SELECT count(*) INTO node_count FROM node WHERE node.db_key = this_db_key AND node.db_unique_name = this_db_unique_name; IF (node_count = 0 AND this_db_unique_name IS NOT NULL) THEN IF (cf_type = 'STANDBY') THEN db_role := 'STANDBY'; ELSE db_role := 'PRIMARY'; END IF; deb('beginCkpt - adding node row with force_resync2cf=NO'); INSERT INTO node(db_unique_name, db_key, high_conf_recid, force_resync2cf, database_role, site_key) VALUES(this_db_unique_name, this_db_key, 0, 'NO', db_role, rman_seq.nextval); END IF; IF cf_type = 'STANDBY' THEN SELECT max(ckp_scn) INTO last_full_ckp_scn FROM ckp WHERE ckp_type = 'FULL' AND dbinc_key = this_dbinc_key; END IF; this_ckp_scn := ckp_scn; this_ckp_time := ckp_time; this_cf_type := cf_type; -- rollback on error to release the lock on dbinc EXCEPTION WHEN OTHERS THEN deb('beginCkpt - error, rollback, released all locks'); 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; deb('endCkpt - commit, release locks'); commit; -- commit all of our changes prev_sessionWaterMarks := sessionWaterMarks; /* Do not run cleanupTempResource when connected to a virtual catalog, * because we do not allow merging catalogs to be done by a virtual * catalog user. */ if user = dbms_catowner then cleanupTempResource; end if; this_ckp_key := NULL; -- and update state variable this_ckp_scn := NULL; this_ckp_time := NULL; this_cf_type := NULL; last_cf_version_time := NULL; END endCkpt; PROCEDURE cancelCkpt IS BEGIN deb('cancelCkpt - rollback, released all locks'); rollback; sessionWaterMarks := prev_sessionWaterMarks; IF (this_ckp_key IS NOT NULL) THEN -- rollback and reset state variables this_ckp_key := NULL; this_ckp_scn := NULL; this_ckp_time := NULL; END IF; IF tsQ%ISOPEN THEN CLOSE tsQ; END IF; IF dfQ%ISOPEN THEN CLOSE dfQ; END IF; IF tfQ%ISOPEN THEN CLOSE tfQ; END IF; IF rtQ%ISOPEN THEN CLOSE rtQ; END IF; IF orlQ%ISOPEN THEN CLOSE orlQ; END IF; IF grspQ%ISOPEN THEN CLOSE grspQ; END IF; IF bpq%ISOPEN THEN CLOSE bpq; END IF; -- resync not successful and hence next resync shouldn't prevented last_resync_cksum := NULL; 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; ELSE deb('fetchTs - '||tsRec.ts_name||' ('||to_char(tsRec.ts#)||') '|| to_char(tsRec.create_scn)); END IF; END fetchTs; PROCEDURE addTs( ts_name IN VARCHAR2 ,ts# IN NUMBER ,create_scn IN NUMBER ,create_time IN DATE ,rbs_count IN NUMBER ,included_in_database_backup IN VARCHAR2 ,bigfile IN VARCHAR2 ,temporary IN VARCHAR2 ,encrypt_in_backup IN VARCHAR2 ) IS BEGIN deb('addTs - tablespace '||ts_name||' ('||to_char(ts#)||') '|| to_char(create_scn)); INSERT INTO ts (dbinc_key, ts#, ts_name, create_scn, create_time, included_in_database_backup, bigfile, temporary, encrypt_in_backup) VALUES (this_dbinc_key, ts#, ts_name, create_scn, create_time, included_in_database_backup, bigfile, temporary, encrypt_in_backup); INSERT INTO tsatt(dbinc_key, ts#, create_scn, start_ckp_key, rbs_count) VALUES (this_dbinc_key, ts#, create_scn, this_ckp_key, rbs_count); END addTs; PROCEDURE dropTs( -- private to package body ts# IN NUMBER ,create_scn IN NUMBER ,drop_scn IN NUMBER ,drop_time IN DATE ) IS BEGIN deb('dropTs - tablespace '||to_char(ts#)||' - '||to_char(create_scn)); 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; PROCEDURE renameTs( ts_name IN VARCHAR2 ,dbinc_key IN NUMBER ,ts# IN NUMBER ,create_scn IN NUMBER ) IS BEGIN UPDATE ts SET ts.ts_name = renameTs.ts_name WHERE ts.dbinc_key = renameTs.dbinc_key AND ts.ts# = renameTs.ts# AND ts.create_scn = renameTs.create_scn; END renameTs; FUNCTION beginTableSpaceResync( high_ts_recid IN NUMBER, force IN BOOLEAN DEFAULT FALSE) RETURN BOOLEAN IS BEGIN checkResync; -- if the force is TRUE that means we want resync even if righ_ts_recid -- is smaller than high_ts_recid. -- if the high_ts_recid in the controlfile is equal to the high_ts_recid -- stored in the rcvcat then the tablespace information in the controlfile -- has not changed since the previous resync, so there is no reason to -- resync it. If the high_ts_recid has been incremented since the previous -- resync then the tablespace information needs to be resynced again. SELECT high_ts_recid INTO last_ts_recid FROM node WHERE site_key = this_site_key; IF (high_ts_recid = last_ts_recid AND NOT force) THEN deb('beginTableSpaceResync - Resync of tablespaces not needed'); RETURN FALSE; ELSIF (high_ts_recid > last_ts_recid OR last_ts_recid IS NULL OR high_ts_recid IS NULL OR force) THEN deb('beginTableSpaceResync - Catalog ts_recid: '||last_ts_recid); last_ts_recid := high_ts_recid; OPEN tsQ; -- just open that cursor please fetchTs; -- do priming read last_ts# := -1; -- initialize for ordering assert if resync_reason = RESYNC_REASON_TS then fullResyncAction.active := TRUE; fullResyncAction.valid := TRUE; fullResyncAction.objtype := RESYNC_OBJECT_TABLESPACE; else fullResyncAction.active := FALSE; end if; RETURN TRUE; ELSE raise_application_error(-20035, 'Invalid high recid'); END IF; END beginTableSpaceResync; PROCEDURE checkTableSpace( ts_name IN VARCHAR2 ,ts# IN NUMBER ,create_scn IN NUMBER ,create_time IN DATE ,rbs_count IN NUMBER DEFAULT NULL ,included_in_database_backup IN VARCHAR2 DEFAULT NULL ,bigfile IN VARCHAR2 DEFAULT NULL ,temporary IN VARCHAR2 DEFAULT NULL ,encrypt_in_backup IN VARCHAR2 DEFAULT NULL ) IS -- Bug 1478785. -- 8.0.5- rman versions does not accept default value as string. -- To maintain compatibility pass NULL as default value. idb varchar2(3) := nvl(included_in_database_backup, 'YES'); -- actual default value bf varchar2(3) := nvl(bigfile, 'NO'); -- actual default value tmp varchar2(3) := nvl(temporary, 'NO'); -- actual default value ts_changed boolean := FALSE; 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; -- If temporary arguments is NOT NULL, then client is aware of resyncing -- temporary tablespace. Otherwise, not. In otherwords, this is a -- indication whether the client >= 10gR2 or not because we introduced -- temporary tablespace resync in 10gR2. IF (temporary IS NOT NULL) THEN do_temp_ts_resync := TRUE; 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. -- If temp tablespace are not resynced(is a < 10gR2 client), then we cannot -- mark them dropped until they use a 10gR2 or later rman client. -- WHILE (ts# > tsRec.ts#) LOOP IF (tsRec.temporary = 'NO' OR -- is a permanent tablespace do_temp_ts_resync) THEN -- is a 10gR2 or later rman client dropTs(tsRec.ts#, tsRec.create_scn, this_ckp_scn, this_ckp_time); incResyncActions(RESYNC_ACTION_DROP, tsRec.ts#, tsRec.ts_name); END IF; fetchTs; END LOOP; IF (ts# < tsRec.ts#) THEN -- this tablespace is new and must be inserted to rcvcat addTs(ts_name, ts#, create_scn, create_time, rbs_count, idb, bf, tmp, encrypt_in_backup); incResyncActions(RESYNC_ACTION_ADD, ts#, ts_name); 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 matches IF (create_time <> tsRec.create_time) THEN raise_application_error(-20043, 'Invalid tablespace create time'); END IF; -- if ts# and create_time match, but ts_name does not match, assume -- Tablespace name was renamed IF (ts_name <> tsRec.ts_name) THEN renameTs(ts_name, this_dbinc_key, ts#, create_scn); incResyncActions(RESYNC_ACTION_RENAME, tsRec.ts#, ts_name); END IF; -- if included_in_database_backup field has changed then update ts table -- note that the initial value may be null, hence the nvl function IF (idb <> nvl(tsRec.included_in_database_backup,'XX')) THEN UPDATE ts SET ts.included_in_database_backup = checkTableSpace.included_in_database_backup WHERE ts.dbinc_key = this_dbinc_key AND ts.ts# = tsRec.ts# AND ts.create_scn = tsRec.create_scn; ts_changed := TRUE; END IF; -- if encrypt_in_backup field has changed then update ts table -- note that the initial value or new value may be null. IF (tsRec.encrypt_in_backup is null and encrypt_in_backup is not null OR tsRec.encrypt_in_backup is not null and encrypt_in_backup is null OR tsRec.encrypt_in_backup <> encrypt_in_backup) THEN UPDATE ts SET ts.encrypt_in_backup = checkTableSpace.encrypt_in_backup WHERE ts.dbinc_key = this_dbinc_key AND ts.ts# = tsRec.ts# AND ts.create_scn = tsRec.create_scn; ts_changed := TRUE; 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); ts_changed := TRUE; END IF; if ts_changed then incResyncActions(RESYNC_ACTION_CHANGE, tsRec.ts#, tsRec.ts_name); end if; ELSIF (create_scn = 0 AND tmp = 'YES') THEN -- this must be a pre-10gR2 tempfile where create_scn was not set. -- We can only track one tempfile with create_scn as 0 in current -- incarnation. dropTs(tsRec.ts#, tsRec.create_scn, create_scn, create_time); DELETE FROM ts WHERE ts.dbinc_key = this_dbinc_key AND ts.ts# = checkTableSpace.ts# AND ts.create_scn = 0; addTs(ts_name, ts#, create_scn, create_time, rbs_count, idb, bf, tmp, encrypt_in_backup); incResyncActions(RESYNC_ACTION_CHANGE, tsRec.ts#, ts_name); ELSE IF (tmp = 'YES') THEN -- bug# 7215002. If temporary ts# reuses permanent ts#, then mark -- it dropped before adding temp ts#. IF (tsRec.temporary = 'NO') THEN dropTs(tsRec.ts#, tsRec.create_scn, create_scn, create_time); END IF; -- temporary tablespace create_scn is derived from lowest tempfile -- create scn. Tempfile between primary and standby can have a -- different create scn and hence different ts_create_scn. So, -- delete any duplicate ts entry before adding this new ts. -- See bug# 5934290 for details DELETE FROM ts WHERE ts.dbinc_key = this_dbinc_key AND ts.ts# = checkTablespace.ts# AND ts.temporary = 'YES'; deb('Deleting tablespace entry for ts#=' || ts# || ', ts_name=' || ts_name); addTs(ts_name, ts#, create_scn, create_time, rbs_count, idb, bf, tmp, encrypt_in_backup); deb('Added tablespace entry for ts#=' || ts# || ', ts_name=' || ts_name); incResyncActions(RESYNC_ACTION_RECREATE, ts#, ts_name); ELSE IF (create_scn > tsRec.create_scn) THEN -- this tablespace has been recreated, mark the old one dropped -- and insert the new one into recovery catalog -- -- ###If first tempfile is dropped, then the create_scn of -- tablespace is advanced. So, query thinks that tablespace is -- dropped and added. -- Some extra work but I do not think it could cause any -- problems. dropTs(tsRec.ts#, tsRec.create_scn, create_scn, create_time); addTs(ts_name, ts#, create_scn, create_time, rbs_count, idb, bf, tmp, encrypt_in_backup); incResyncActions(RESYNC_ACTION_RECREATE, tsRec.ts#, ts_name); 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; END IF; END IF; fetchTS; -- get next row from TS cursor END IF; -- (ts# < tsRec.ts) END checkTableSpace; PROCEDURE endTableSpaceResync IS BEGIN checkResync; -- If temp tablespace are not resynced(is a < 10gR2 client), then we cannot -- mark them dropped until they use a 10gR2 or later rman client. -- WHILE (tsRec.ts# < MAXNUMVAL) LOOP -- while extra tablespaces in rcvcat IF (tsRec.temporary = 'NO' OR -- is a permanent tablespace do_temp_ts_resync) THEN -- is a 10gR2 or later rman client dropTs(tsRec.ts#, tsRec.create_scn, this_ckp_scn, this_ckp_time); incResyncActions(RESYNC_ACTION_DROP, tsRec.ts#, tsRec.ts_name); END IF; 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 node SET high_ts_recid = nvl(last_ts_recid, high_ts_recid) WHERE site_key = this_site_key; last_ts_recid := NULL; -- reset high_tf_recid so that first resync with 10gR2 rman client would -- trigger a temporary tablespace resync. IF (NOT do_temp_ts_resync) THEN UPDATE node SET high_tf_recid = 0 WHERE site_key = this_site_key; END IF; END endTableSpaceResync; /*-----------------* * Datafile Resync * *-----------------*/ PROCEDURE fetchDF IS -- private to package body BEGIN FETCH dfQ INTO dfRec; IF dfQ%NOTFOUND THEN dfRec.file# := MAXNUMVAL; -- indicate end-of-fetch CLOSE dfQ; END IF; END fetchDF; PROCEDURE addDF(file# IN NUMBER, -- private to package body fname IN VARCHAR2, create_time IN DATE, create_scn IN NUMBER, blocks IN NUMBER, block_size IN NUMBER, ts# IN NUMBER, stop_scn IN NUMBER, stop_time IN DATE, read_only IN number, rfile# IN NUMBER, foreign_dbid IN number, foreign_create_scn IN number, foreign_create_time IN date, plugged_readonly IN varchar2, plugin_scn IN number, plugin_reset_scn IN number, plugin_reset_time IN date, create_thread IN number, create_size IN number) IS ts_create_scn NUMBER; local_df_key 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 numbers are reused -- If the data file is already known to the catalog, get its df_key; -- otherwise, assign a new df_key. -- There may be multiple rows in DFATT with same DF_KEY. The column values -- DF_KEY along with DBINC_KEY forms the unique column values. Hence -- we need to use distinct in below query. BEGIN select distinct df_key into local_df_key from df, dbinc where file# = addDF.file# and create_scn = addDF.create_scn and plugin_scn = addDF.plugin_scn and foreign_dbid = addDF.foreign_dbid and ts# = addDF.ts# and df.dbinc_key = dbinc.dbinc_key and dbinc.db_key = this_db_key; EXCEPTION WHEN no_data_found THEN select rman_seq.nextval into local_df_key from dual; END; -- -- Bug 1332121: Insert 0 for blocks rather than NULL for new records -- of datafile as 'list' command in 8.1.5- RMAN will fail with ORA-1405. -- INSERT INTO df(dbinc_key, file#, create_scn, create_time, ts#, ts_create_scn, block_size, stop_scn, stop_time, read_only, rfile#, df_key, blocks, foreign_dbid, foreign_create_scn, foreign_create_time, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, create_thread, create_size) VALUES(this_dbinc_key, file#, create_scn, create_time, ts#, ts_create_scn, block_size, stop_scn, stop_time, read_only, rfile#, local_df_key, nvl(blocks, 0), foreign_dbid, foreign_create_scn, foreign_create_time, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, create_thread, create_size); -- Note that we have only one row per df_key in site_dfatt as we track only -- the latest changes at a site. So, the row if already exists should reflect -- any changes. BEGIN INSERT INTO site_dfatt(df_key, fname, site_key) VALUES(local_df_key, fname, this_site_key); EXCEPTION WHEN dup_val_on_index THEN -- Fix the file name if it has changed. UPDATE site_dfatt SET fname = addDf.fname WHERE site_key = this_site_key AND df_key = local_df_key; END; END addDf; PROCEDURE setDatafileSize(file# IN number ,create_scn IN number ,blocks IN number ,plugin_scn IN number default 0) IS BEGIN IF (this_dbinc_key is NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; update df set df.blocks = setDatafileSize.blocks where dbinc_key = this_dbinc_key and df.file# = setDatafileSize.file# and df.create_scn = setDatafileSize.create_scn and df.plugin_scn = setDatafileSize.plugin_scn; deb('setDatafileSize - commit, release locks'); commit; END setDatafileSize; PROCEDURE dropDf( -- private to package body file# IN NUMBER ,create_scn IN NUMBER ,plugin_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 AND df.plugin_scn = dropDf.plugin_scn; END dropDf; FUNCTION beginDataFileResyncForStandby( high_df_recid IN number ) return boolean IS BEGIN checkResync; SELECT high_df_recid INTO last_df_recid FROM node WHERE node.site_key = this_site_key; deb('high_df_recid='||high_df_recid||',last_df_recid='||last_df_recid); IF last_full_ckp_scn IS NULL THEN deb('beginDataFileResyncForStandby - no full resync'); raise_application_error(-20079, 'full resync from primary database is not done'); END IF; -- resync to catalog only when the previous stored water mark is different -- or the first time the node is seen. Otherwise the file names in -- catalog precedence over control file. IF (high_df_recid > last_df_recid OR last_df_recid IS NULL) THEN last_df_recid := high_df_recid; last_file# := -1; -- initialize for ordering assert -- If standby is ahead of last full resync, open dfQ cursor to see when -- full resync is required. IF this_ckp_scn > last_full_ckp_scn THEN OPEN dfQ; fetchDf; -- do priming read END IF; RETURN TRUE; END IF; deb('no need to resync datafile names for '||this_db_unique_name|| ' standby site'); RETURN FALSE; END; PROCEDURE checkDataFileForStandby(file# IN NUMBER, fname IN VARCHAR2, create_scn IN NUMBER, create_time IN DATE, blocks IN NUMBER, block_size IN NUMBER, ts# IN NUMBER, rfile# IN NUMBER, stop_scn IN NUMBER, read_only IN NUMBER, foreign_dbid IN NUMBER, plugin_scn IN NUMBER) IS local_df_key NUMBER; BEGIN 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 standby is ahead of last time we did full resync, then signal -- need full resync error as needed. IF this_ckp_scn > last_full_ckp_scn THEN IF (file# != dfRec.file#) THEN IF (file# > dfRec.file#) THEN deb('checkDataFileResyncForStandby - droped file#=' ||dfRec.file#); ELSE -- note that dfRec.file# = MAXNUMVAL is also handled here... deb('checkDataFileResyncForStandby - added file#=' || file#); END IF; raise_application_error(-20079, 'full resync from primary database is not done'); ELSE 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 OR read_only < dfRec.read_only OR read_only is null and dfRec.read_only is not null OR read_only is not null and dfRec.read_only is null) THEN deb('checkDataFileResyncForStandby - change for file#=' || file#); raise_application_error(-20079, 'full resync from primary database is not done'); END IF; END IF; fetchDF; END IF; BEGIN -- must find the df_key select distinct df_key into local_df_key from df, dbinc where file# = checkDataFileForStandby.file# and create_scn = checkDataFileForStandby.create_scn and plugin_scn = checkDataFileForStandby.plugin_scn and foreign_dbid = checkDataFileForStandby.foreign_dbid and ts# = checkDataFileForStandby.ts# and df.dbinc_key = dbinc.dbinc_key and dbinc.db_key = this_db_key; EXCEPTION -- something is wrong!!! how can entries from df table disappear when -- there are some rows in site_dfatt for specific datafile. WHEN no_data_found THEN raise_application_error(-20999, 'Internal error in checkDataFileForStandby - 1 '); END; -- Note that we have only 1 row per df_key in site_dfatt as we track only -- the latest changes at a site. So, the row if exists should reflect -- any changes. BEGIN INSERT INTO site_dfatt(df_key, fname, site_key) VALUES(local_df_key, checkDataFileForStandby.fname, this_site_key); EXCEPTION WHEN dup_val_on_index THEN -- Fix the file name if it has changed. UPDATE site_dfatt SET fname = checkDataFileForStandby.fname WHERE site_key = this_site_key AND df_key = local_df_key; END; END; PROCEDURE endDataFileResyncForStandby IS BEGIN checkResync; -- check if there are any datafiles in rcvcat that the client did not check IF (this_ckp_scn > last_full_ckp_scn AND dfRec.file# < MAXNUMVAL) THEN deb('endDataFileResyncForStandby - dropped file# > ' || dfRec.file#); raise_application_error(-20079, 'full resync from primary database is not done'); IF dfQ%ISOPEN THEN CLOSE dfQ; END IF; END IF; -- set the state variable to indicate that datafile resync is done dfRec.file# := NULL; -- update high_df_resync for the next resync UPDATE node SET high_df_recid = last_df_recid WHERE node.site_key = this_site_key; last_df_recid := NULL; END; 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 node WHERE site_key = this_site_key; IF (high_df_recid = last_df_recid) THEN deb('beginDataFileResync - Resync of datafiles not needed'); RETURN FALSE; ELSIF (high_df_recid > last_df_recid OR last_df_recid IS NULL) THEN deb('beginDataFileResync - Catalog df_recid: '||nvl(last_df_recid, 0)); last_df_recid := high_df_recid; OPEN dfQ; fetchDf; -- do priming read last_file# := -1; -- initialize for ordering assert if resync_reason = RESYNC_REASON_DF then fullResyncAction.valid := TRUE; fullResyncAction.active := TRUE; fullResyncAction.objtype := RESYNC_OBJECT_DATAFILE; else fullResyncAction.active := FALSE; end if; RETURN TRUE; ELSE raise_application_error(-20035, 'Invalid high recid'); END IF; END beginDataFileResync; PROCEDURE checkDataFile(file# IN NUMBER, fname IN VARCHAR2, create_scn IN NUMBER, create_time IN DATE, blocks IN NUMBER, block_size IN NUMBER, ts# IN NUMBER, stop_scn IN NUMBER, read_only IN NUMBER, stop_time IN DATE DEFAULT NULL, rfile# IN NUMBER DEFAULT NULL, aux_fname IN VARCHAR2 DEFAULT NULL, foreign_dbid IN number DEFAULT 0, foreign_create_scn IN number DEFAULT 0, foreign_create_time IN date DEFAULT NULL, plugged_readonly IN varchar2 DEFAULT 'NO', plugin_scn IN number DEFAULT 0, plugin_reset_scn IN number DEFAULT 0, plugin_reset_time IN date DEFAULT NULL, create_thread IN number DEFAULT NULL, create_size IN number DEFAULT NULL) IS local_df_key NUMBER; changedauxname boolean; 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 (plugged_readonly = 'NO' AND create_scn > this_ckp_scn) THEN raise_application_error(-20052, 'Invalid datafile create SCN'); ELSIF (plugged_readonly = 'YES' AND plugin_scn > this_ckp_scn) THEN raise_application_error(-20055, 'Invalid datafile plugin 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 ca not find out the -- exact drop scn. WHILE (file# > dfRec.file#) LOOP deb('checkDatafile - dropping file#: '||to_char(dfRec.file#)); dropDf(dfRec.file#, dfRec.create_scn, dfRec.plugin_scn, this_ckp_scn, this_ckp_time); incResyncActions(RESYNC_ACTION_DROP, dfRec.file#, dfRec.fname); fetchDf; END LOOP; IF (file# < dfRec.file#) THEN -- this datafile is new and must be inserted into rcvcat deb('checkDatafile - adding file#: '||to_char(file#)); addDF(file#, fname, create_time, create_scn, blocks, block_size, ts#, stop_scn, stop_time, read_only, rfile#, foreign_dbid, foreign_create_scn, foreign_create_time, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, create_thread, create_size); -- set the database clonename (alias aux_name) -- Note that in case that RMAN is 9.0 or greater then aux_fname cannot -- be NULL. So, if aux_fname is NULL we will not change it. IF (aux_fname is not NULL) THEN setCloneName(file#, create_scn, aux_fname, NULL, changedauxname); END IF; incResyncActions(RESYNC_ACTION_ADD, file#, fname); ELSE -- (file# = dfRec.file#) IF (create_scn = dfRec.create_scn AND plugin_scn = dfRec.plugin_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; SELECT df_key INTO local_df_key FROM df WHERE file# = checkDataFile.file# AND create_scn = checkDataFile.create_scn AND plugin_scn = checkDataFile.plugin_scn AND foreign_dbid = checkDataFile.foreign_dbid AND ts# = checkDataFile.ts# AND dbinc_key = this_dbinc_key; -- if create_size or create_thread is not know, update these -- values now for all entries in df for this datafile only once. IF ((create_thread is not null AND dfRec.create_thread is null) OR (create_size is not null AND dfRec.create_size is null)) THEN UPDATE df SET create_thread = checkDataFile.create_thread, create_size = checkDataFile.create_size WHERE df.df_key = local_df_key; END IF; -- if fname has changed then update dfatt -- mark datafile copies with same name deleted too### IF (fname <> dfRec.fname OR dfRec.fname is NULL) 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 and dfRec.fname is not null) THEN deb('checkDatafile - new datafilename is old auxname'); setCloneName(dfRec.file#, dfRec.create_scn, dfRec.fname, dfRec.clone_fname, changedauxname, dfRec.plugin_scn); END IF; incResyncActions(RESYNC_ACTION_RENAME, dfRec.file#, fname); UPDATE site_dfatt SET fname = checkDataFile.fname WHERE site_key = this_site_key AND df_key = local_df_key; -- if no rows were updated by above update cmd, it means the file -- name at this site is not known to catalog. So add one here. IF sql%rowcount = 0 THEN INSERT INTO site_dfatt (df_key, fname, site_key) VALUES(local_df_key, checkDataFile.fname, this_site_key); END IF; END IF; -- If the stop SCN or blocks has changed, update the df record. IF ((blocks <> dfrec.blocks) OR (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 IF blocks <> dfRec.blocks THEN deb('checkDatafile - size changed for file#: '|| to_char(file#)||' from '||to_char(dfRec.blocks)||' to '|| to_char(blocks)); incResyncActions(RESYNC_ACTION_RESIZE, dfRec.file#, fname); ELSE deb('checkDatafile - stopSCN changed for file#: '|| to_char(file#)||' from '|| nvl(to_char(dfRec.stop_scn), 'NULL')||' to '|| nvl(to_char(checkDatafile.stop_scn), 'NULL')); incResyncActions(RESYNC_ACTION_CHANGE, dfRec.file#, fname); END IF; UPDATE df SET stop_scn = checkDataFile.stop_scn, stop_time = checkDataFile.stop_time, read_only = checkDataFile.read_only, blocks = checkDataFile.blocks WHERE df.dbinc_key = this_dbinc_key AND df.file# = dfRec.file# AND df.create_scn = dfRec.create_scn AND df.plugin_scn = dfRec.plugin_scn; ELSE deb('checkDatafile - stopSCN remains the same for file#: '|| to_char(file#)); END IF; -- If the aux_fname has changed, update the df record. -- Note that in case that RMAN is 9.0 or greater then aux_fname -- cannot be NULL. IF (aux_fname is not NULL) THEN setCloneName(dfRec.file#, dfRec.create_scn, aux_fname, dfRec.clone_fname, changedauxname, dfRec.plugin_scn); IF changedauxname THEN incResyncActions(RESYNC_ACTION_CHANGE, dfRec.file#, fname); END IF; END IF; ELSIF ((case when plugged_readonly = 'NO' then create_scn else plugin_scn end) > (case when dfRec.plugged_readonly = 'NO' then dfRec.create_scn else dfRec.plugin_scn end)) THEN -- this datafile has been recreated or has been converted from -- plugged read only to read write. -- We could probably assert that the tablespace that contained the old -- incarnation of this datafile has been dropped, but I would -- 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. deb('checkDatafile - file#: '||to_char(file#)||' recreated'); dropDf(dfRec.file#, dfRec.create_scn, dfRec.plugin_scn, create_scn, create_time); addDf(file#, fname, create_time, create_scn, blocks, block_size, ts#, stop_scn, stop_time, read_only, rfile#, foreign_dbid, foreign_create_scn, foreign_create_time, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, create_thread, create_size); incResyncActions(RESYNC_ACTION_RECREATE, dfRec.file#, fname); 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 -- 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. IF (plugged_readonly = 'NO') THEN raise_application_error(-20052, 'Invalid datafile create SCN'); ELSE raise_application_error(-20055, 'Invalid datafile plugin SCN'); END IF; 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 did not check WHILE (dfRec.file# < MAXNUMVAL) LOOP -- if we ever allow drop datafile, replace error signalling with dropDf dropDf(dfRec.file#, dfRec.create_scn, dfRec.plugin_scn, this_ckp_scn, this_ckp_time); incResyncActions(RESYNC_ACTION_DROP, dfRec.file#, dfRec.fname); 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 node SET high_df_recid = last_df_recid WHERE site_key = this_site_key; last_df_recid := NULL; END endDataFileResync; /*-----------------* * Tempfile Resync * *-----------------*/ PROCEDURE fetchTf IS -- private to package body BEGIN -- if already fetched everything, just return IF tfRec.file# = MAXNUMVAL THEN return; END IF; FETCH tfQ INTO tfRec; IF tfQ%NOTFOUND THEN tfRec.file# := MAXNUMVAL; -- indicate end-of-fetch CLOSE tfQ; END IF; END fetchTf; PROCEDURE addTf(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, rfile# IN NUMBER, autoextend IN VARCHAR2, max_size IN NUMBER, next_size IN NUMBER) IS ts_create_scn NUMBER; local_tf_key NUMBER; BEGIN BEGIN SELECT create_scn INTO ts_create_scn FROM ts WHERE ts.dbinc_key = this_dbinc_key AND ts.ts# = addTf.ts# AND ts.drop_scn IS NULL; -- in case ts numbers are reused EXCEPTION WHEN no_data_found THEN -- if this tempfile was never resynced from primary, then -- we won't resync the tempfile info from standby, because we don't -- do tablespace resync from standby cf. IF (this_cf_type = 'STANDBY' AND this_db_unique_name is not null) THEN RETURN; END IF; END; -- If the data file is already known to the catalog, get its tf_key; -- otherwise, assign a new tf_key BEGIN SELECT distinct tf_key INTO local_tf_key FROM tf, dbinc WHERE file# = addTf.file# AND create_scn = addTf.create_scn AND (create_time = addTf.create_time or create_time is null AND addTf.create_time is null) AND ts# = addTf.ts# AND rfile# = addTf.rfile# AND tf.dbinc_key = dbinc.dbinc_key AND dbinc.db_key = this_db_key; EXCEPTION WHEN no_data_found THEN SELECT rman_seq.nextval INTO local_tf_key FROM dual; END; BEGIN INSERT INTO tf(dbinc_key, file#, create_scn, create_time, ts#, ts_create_scn, block_size, rfile#, tf_key) VALUES(this_dbinc_key, file#, create_scn, create_time, ts#, ts_create_scn, block_size, rfile#, local_tf_key); EXCEPTION WHEN dup_val_on_index THEN -- If create_scn = 0, it must be a pre-10gR2 tempfile where create_scn -- was not set. We can only track one tempfile with create_scn as 0 -- in current incarnation. Hence, update the old ones. IF create_scn = 0 THEN UPDATE tf SET create_time = addTf.create_time, ts# = addTf.ts#, ts_create_scn = addTf.ts_create_scn, block_size = addTf.block_size, rfile# = addTf.rfile# WHERE dbinc_key = this_dbinc_key AND file# = addTf.file# AND create_scn= addTf.create_scn; END IF; -- we can see an existing entry for tempfile in Data guard environment, -- when resyncing from primary in following cases: -- 1) when current primary has tempfile that was dropped from old primary -- i.e. standby cf has tempfile and switchover was done after the -- the tempfile was dropped from old primary. -- 2) when a primary created a tempfile that re-used the file number -- which marked the tempfile from old primary as dropped. Now after -- swithover back again, we find the tempfile in new primary during -- resync that was marked dropped earlier. -- In these cases, we want to mark the temp file as undropped, which is -- done below by marking drop_scn and drop_time in site_tfatt as null. END; -- Note that we have only one row per tf_key in site_tfatt as we track only -- the latest changes at a site. So, the row if already exists should reflect -- any changes. Also, unmark the file as not dropped. BEGIN INSERT INTO site_tfatt(tf_key, fname, site_key, blocks, autoextend, max_size, next_size) VALUES(local_tf_key, fname, this_site_key, nvl(addTf.blocks, 0), addTf.autoextend, addTf.max_size, addTf.next_size); -- mark tempfile copies with same name deleted too### EXCEPTION WHEN dup_val_on_index THEN -- Update existing record. UPDATE site_tfatt SET fname = addTf.fname, blocks = nvl(addTf.blocks, 0), autoextend = addTf.autoextend, max_size = addTf.max_size, next_size = addTf.next_size, drop_scn = NULL, drop_time = NULL WHERE site_key = this_site_key AND tf_key = local_tf_key; END; END addTf; PROCEDURE dropTf( -- private to package body tf_key IN NUMBER ,drop_scn IN NUMBER ,drop_time IN DATE ) IS BEGIN UPDATE site_tfatt SET drop_scn = dropTf.drop_scn, drop_time = dropTf.drop_time WHERE this_site_key = site_key AND tf_key = dropTf.tf_key; END dropTf; FUNCTION tempFileToResync( high_tf_recid IN NUMBER ) RETURN BOOLEAN IS tf_recid number; BEGIN checkResync; SELECT high_tf_recid INTO tf_recid FROM node WHERE site_key = this_site_key; IF (high_tf_recid = tf_recid) THEN RETURN FALSE; ELSIF (high_tf_recid > tf_recid OR tf_recid IS NULL) THEN RETURN TRUE; ELSE raise_application_error(-20035, 'Invalid high recid'); END IF; END tempFileToResync; -- The tempfile resync is unified with bug fix 6653570. So, the calls -- for standby tempfile resync are just a wrapper over existing -- tempfile resync code at primary database. FUNCTION beginTempFileResyncForStandby( high_tf_recid IN NUMBER ) RETURN BOOLEAN IS BEGIN RETURN beginTempFileResync (high_tf_recid); END beginTempFileResyncForStandby; -- Tempfiles are not same as datafiles on primary and standby. User can add -- new temp files at primary database only when it is opened, and there is no -- "add datafile" redo added for this action. However, when a new temporary -- tablespace is created, "add tablespace" redo is added which is propagated -- to standby also. Any new temp files added to primary are not propagated to -- standby. Only the names that are already existing in primary control file -- are created at standby during standby creation. The file name convert -- parameter apply to the temp files at standby. PROCEDURE checkTempFileForStandby (file# IN NUMBER, fname IN VARCHAR2, create_scn IN NUMBER, create_time IN DATE, blocks IN NUMBER, block_size IN NUMBER, ts# IN NUMBER, rfile# IN NUMBER, autoextend IN VARCHAR2, max_size IN NUMBER, next_size IN NUMBER) IS local_tf_key NUMBER; BEGIN checkTempFile(file#, fname, create_scn, create_time, blocks, block_size, ts#, rfile#, autoextend, max_size, next_size); END checkTempFileForStandby; PROCEDURE endTempFileResyncForStandby IS BEGIN endTempFileResync; END endTempFileResyncForStandby; FUNCTION beginTempFileResync( high_tf_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_tf_recid INTO last_tf_recid FROM node WHERE site_key = this_site_key; IF (high_tf_recid = last_tf_recid) THEN deb('beginTempFileResync - Resync of tempfiles not needed'); RETURN FALSE; ELSIF (high_tf_recid > last_tf_recid OR last_tf_recid IS NULL) THEN deb('beginTempFileResync - Catalog tf_recid: '||nvl(last_tf_recid, 0)); last_tf_recid := high_tf_recid; OPEN tfQ; fetchTf; -- do priming read last_file# := -1; -- initialize for ordering assert if resync_reason = RESYNC_REASON_TF then fullResyncAction.active := TRUE; fullResyncAction.valid := TRUE; fullResyncAction.objtype := RESYNC_OBJECT_TEMPFILE; else fullResyncAction.active := FALSE; end if; RETURN TRUE; ELSE raise_application_error(-20035, 'Invalid high recid'); END IF; END beginTempFileResync; PROCEDURE checkTempFile(file# IN NUMBER, fname IN VARCHAR2, create_scn IN NUMBER, create_time IN DATE, blocks IN NUMBER, block_size IN NUMBER, ts# IN NUMBER, rfile# IN NUMBER, autoextend IN VARCHAR2, max_size IN NUMBER, next_size IN NUMBER) IS local_tf_key NUMBER; BEGIN IF (tfRec.file# IS NULL) THEN -- assert beginTempFileResync was called raise_application_error(-20050, 'Tempfile 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 --Bug 5939669: DBPITR can result in tempfile create_scn > ckp_scn --IF (create_scn > this_ckp_scn) THEN -- raise_application_error(-20052, 'Invalid tempfile create SCN'); --END IF; -- if the tempfile in tfRec has a lower file# than the tempfile -- we are currently checking, it means that the tempfile in tfRec 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 ca not find out the -- exact drop scn. WHILE (file# > tfRec.file#) LOOP dropTf(tfRec.tf_key, this_ckp_scn, this_ckp_time); incResyncActions(RESYNC_ACTION_DROP, tfRec.file#, tfRec.fname); fetchTf; END LOOP; IF (file# < tfRec.file#) THEN addTf(file#, fname, create_time, create_scn, blocks, block_size, ts#, rfile#, autoextend, max_size, next_size); incResyncActions(RESYNC_ACTION_ADD, tfRec.file#, fname); ELSE -- (file# = tfRec.file#) IF (create_scn = 0) THEN addTf(file#, fname, create_time, create_scn, blocks, block_size, ts#, rfile#, autoextend, max_size, next_size); incResyncActions(RESYNC_ACTION_CHANGE, file#, fname); ELSIF (create_scn = tfRec.create_scn) THEN -- this is an existing tempfile which is already recorded in rcvcat -- check that create_time and ts# match IF (create_time <> tfRec.create_time) THEN raise_application_error(-20053, 'Invalid tempfile create time'); END IF; IF (ts# <> tfRec.ts#) THEN raise_application_error(-20054, 'Invalid tempfile ts#'); END IF; -- make any attribute changes, if any addTf(file#, fname, create_time, create_scn, blocks, block_size, ts#, rfile#, autoextend, max_size, next_size); IF (fname <> tfRec.fname OR tfRec.fname is NULL) THEN incResyncActions(RESYNC_ACTION_RENAME, file#, fname); END IF; -- if blocks/autoextend/max_size/next_size have changed then -- update tf. IF (blocks <> tfrec.blocks OR autoextend <> tfrec.autoextend OR max_size <> tfrec.max_size OR next_size <> tfrec.next_size ) THEN IF blocks <> tfrec.blocks THEN incResyncActions(RESYNC_ACTION_RESIZE, file#, fname); ELSE incResyncActions(RESYNC_ACTION_CHANGE, file#, fname); END IF; END IF; ELSIF (create_scn > tfRec.create_scn) THEN -- this tempfile has been been recreated -- We could probably assert that the tablespace that contained the old -- incarnation of this tempfile has been dropped, but I would -- rather not be that strict. -- The old incarnation of the tempfile 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 tempfile existed at the same scn time. dropTf(tfRec.tf_key, create_scn, create_time); addTf(file#, fname, create_time, create_scn, blocks, block_size, ts#, rfile#, autoextend, max_size, next_size); incResyncActions(RESYNC_ACTION_RECREATE, file#, fname); ELSE -- (create_scn < tfRec.create_scn) -- The client passed us a create SCN for this tempfile that is -- less than the SCN in the rcvcat. I.e., the target database -- controlfile now contains a previous incarnation of this tempfile. -- 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 tempfile create SCN'); END IF; fetchTf; -- get next row from Tf cursor END IF; -- (file# = tfRec.file#) END checkTempFile; PROCEDURE endTempFileResync IS BEGIN checkResync; -- check if there are any tempfiles in rcvcat that the client did not check deb('endTempFileResync - entering with tempfile number'||tfRec.file#); WHILE (tfRec.file# < MAXNUMVAL) LOOP dropTf(tfRec.tf_key, this_ckp_scn, this_ckp_time); incResyncActions(RESYNC_ACTION_DROP, tfRec.file#, tfRec.fname); fetchTf; deb('endTempFileResync - dropping tempfile '||tfRec.file#); END LOOP; -- set the state variable to indicate that tempfile resync is done tfRec.file# := NULL; -- update high_tf_resync for the next resync UPDATE node SET high_tf_recid = last_tf_recid WHERE site_key = this_site_key; last_tf_recid := NULL; END endTempFileResync; /*---------------------* * 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 node WHERE site_key = this_site_key; IF (high_rt_recid = last_rt_recid) THEN deb('beginThreadResync - Resync of redo threads not needed'); RETURN FALSE; ELSIF (high_rt_recid > last_rt_recid OR last_rt_recid IS NULL) THEN deb('beginThreadResync - Catalog rt_recid: '||nvl(last_rt_recid, 0)); last_rt_recid := high_rt_recid; OPEN rtQ; fetchRt; -- do priming read last_thread# := -1; if resync_reason = RESYNC_REASON_THR then fullResyncAction.valid := TRUE; fullResyncAction.active := TRUE; fullResyncAction.objtype := RESYNC_OBJECT_REDOTHREAD; else fullResyncAction.active := FALSE; end if; 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#); incResyncActions(RESYNC_ACTION_DROP, rtRec.thread#, to_char(NULL)); 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); incResyncActions(RESYNC_ACTION_ADD, thread#, to_char(NULL)); 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#; incResyncActions(RESYNC_ACTION_CHANGE, rtRec.thread#, to_char(NULL)); 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 node SET high_rt_recid = last_rt_recid WHERE site_key = this_site_key; last_rt_recid := NULL; END endThreadResync; /*------------------------* * Online Redo Log resync * *------------------------*/ -- String compare indepedent of NLS_COMP. Assumes the recover.txt -- cursor also uses NLS_COMP=ANSI and NLS_SORT=ASCII7. -- This is used in 1) online redo log resync 2) guaranteed restore point -- resync and 3) standby redo log resync -- -- Returns 0 if n1 = n2 -- 1 if n1 > n2 -- -1 if n1 < n2 -- NULL if n1 or n2 is NULL -- FUNCTION nlsnamecmp(n1 IN varchar2, n2 IN varchar2) RETURN NUMBER IS CURSOR nlsnamecmp_c(n1 varchar2, n2 varchar2) IS SELECT name FROM (SELECT n1 name FROM dual UNION ALL SELECT n2 name FROM dual) ORDER BY nlssort(name, 'NLS_COMP=ANSI NLS_SORT=ASCII7'); ln1 varchar2(1024); ln2 varchar2(1024); BEGIN if (n1 is null or n2 is null) then return null; elsif (n1 = n2) then return 0; elsif (n1 = chr(1) or n2 = chr(255)) then return -1; elsif (n2 = chr(1) or n1 = chr(255)) then return 1; end if; open nlsnamecmp_c(n1, n2); fetch nlsnamecmp_c into ln1; fetch nlsnamecmp_c into ln2; close nlsnamecmp_c; if (ln1 = n1) then return -1; end if; return 1; END nlsnamecmp; 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 ,bytes IN NUMBER ,type IN VARCHAR2 ) IS thread_not_found EXCEPTION; PRAGMA EXCEPTION_INIT(thread_not_found, -2291); BEGIN INSERT INTO orl (dbinc_key, thread#, group#, fname, bytes, type, site_key) VALUES (this_dbinc_key, thread#, group#, fname, bytes, type, this_site_key); EXCEPTION WHEN thread_not_found THEN raise_application_error(-20079, 'full resync from primary database is not done'); 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.site_key = this_site_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 node WHERE site_key = this_site_key; IF (high_orl_recid = last_orl_recid) THEN deb('beginOnlineRedoLogResync - Resync of online logs not needed'); RETURN FALSE; ELSIF (high_orl_recid > last_orl_recid OR last_orl_recid IS NULL) THEN deb('beginOnlineRedoLogResync - Catalog orl_recid: '|| nvl(last_orl_recid, 0)); last_orl_recid := high_orl_recid; OPEN orlQ; fetchOrl; last_fname := chr(1); -- assume chr(1) is less than any name if resync_reason = RESYNC_REASON_ORL then fullResyncAction.active := TRUE; fullResyncAction.valid := TRUE; fullResyncAction.objtype := RESYNC_OBJECT_ONLINELOG; else fullResyncAction.active := FALSE; end if; 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 ,bytes IN NUMBER DEFAULT NULL ,type IN VARCHAR2 DEFAULT 'ONLINE' ) IS BEGIN IF (orlRec.fname IS NULL) THEN raise_application_error(-20061, 'Redo resync not started'); END IF; IF (nlsnamecmp(last_fname, fname) >= 0) THEN raise_application_error(-20036, 'Invalid record order'); END IF; last_fname := fname; WHILE (nlsnamecmp(fname, orlRec.fname) > 0) LOOP -- if we get here the online log has disappered from the controlfile -- this can happen only if the controlfile is recreated -- mark the online log as dropped in the rcvcat dropOrl(orlRec.fname); incResyncActions(RESYNC_ACTION_DROP, to_number(NULL), orlRec.fname); fetchOrl; END LOOP; IF (nlsnamecmp(fname, orlRec.fname) < 0) THEN -- this online log is new and must be inserted into rcvcat addOrl(thread#, group#, fname, bytes, type); incResyncActions(RESYNC_ACTION_ADD, to_number(NULL), fname); ELSE -- (fname = orlRec.fname) UPDATE orl SET thread# = checkOnlineRedoLog.thread#, group# = checkOnlineRedoLog.group#, bytes = checkOnlineRedoLog.bytes, type = checkOnlineRedoLog.type WHERE orl.dbinc_key = this_dbinc_key AND orl.fname = checkOnlineRedoLog.fname AND orl.site_key = this_site_key; incResyncActions(RESYNC_ACTION_CHANGE, to_number(NULL), orlRec.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); incResyncActions(RESYNC_ACTION_DROP, to_number(NULL), orlRec.fname); fetchOrl; END LOOP; orlRec.fname := NULL; -- update high_orl_resync for the next resync UPDATE node SET high_orl_recid = last_orl_recid WHERE site_key = this_site_key; last_orl_recid := NULL; END endOnlineRedoLogResync; /*---------------------------------* * Guaranteed restore point Resync * *---------------------------------*/ PROCEDURE fetchGrsp IS BEGIN FETCH grspQ INTO grspRec; IF grspQ%NOTFOUND THEN grspRec.rspname := chr(255); -- assume chr(255) is greater than any name CLOSE grspQ; END IF; END fetchGrsp; PROCEDURE addGrsp( rspname IN VARCHAR2 ,from_scn IN NUMBER ,to_scn IN NUMBER ,dbinc_key IN NUMBER ,create_time IN DATE ,rsp_time IN DATE ,guaranteed IN VARCHAR2 ) IS BEGIN INSERT INTO grsp (dbinc_key, rspname, from_scn, to_scn, creation_time, rsptime, guaranteed, site_key) VALUES (dbinc_key, rspname, from_scn, to_scn, create_time, rsp_time, guaranteed, this_site_key); END addGrsp; PROCEDURE dropGrsp( rspname IN VARCHAR2) IS BEGIN DELETE FROM grsp WHERE grsp.rspname = dropGrsp.rspname AND grsp.site_key = this_site_key; END dropGrsp; FUNCTION beginGuaranteedRPResync( high_grsp_recid IN NUMBER ) RETURN BOOLEAN IS BEGIN checkResync; SELECT node.high_grsp_recid INTO last_grsp_recid FROM node WHERE site_key = this_site_key; IF (high_grsp_recid = last_grsp_recid) THEN RETURN FALSE; ELSIF (high_grsp_recid > last_grsp_recid OR last_grsp_recid IS NULL) THEN last_grsp_recid := high_grsp_recid; OPEN grspQ; fetchGrsp; last_rspname := chr(1); -- assume chr(1) is less than any name RETURN TRUE; ELSE raise_application_error(-20035, 'Invalid high recid'); END IF; END beginGuaranteedRPResync; PROCEDURE checkGuaranteedRP( rspname IN VARCHAR2 ,from_scn IN NUMBER ,to_scn IN NUMBER ,resetlogs_change# IN NUMBER ,resetlogs_time IN DATE ,create_time IN DATE DEFAULT NULL ,rsp_time IN DATE DEFAULT NULL ,guaranteed IN VARCHAR2 DEFAULT 'YES' ) IS dbinc_key number; BEGIN IF (grspRec.rspname IS NULL) THEN raise_application_error(-20099, 'restore point resync not started'); END IF; IF (nlsnamecmp(last_rspname, rspname) >= 0) THEN raise_application_error(-20036, 'Invalid record order'); END IF; last_rspname := rspname; dbinc_key := checkIncarnation(resetlogs_change#, resetlogs_time); WHILE (nlsnamecmp(rspname, grspRec.rspname) > 0) LOOP -- if we get here the guaranteed restore point has disappered from the -- controlfile this can happen only if the controlfile is recreated -- mark the restore point as dropped in the rcvcat dropGrsp(grspRec.rspname); fetchGrsp; END LOOP; IF (nlsnamecmp(rspname, grspRec.rspname) < 0) THEN -- this restore point is new and must be inserted into rcvcat addGrsp(rspname, from_scn, to_scn, dbinc_key, create_time, rsp_time, guaranteed); ELSE -- (rspname = grspRec.rspname) -- this is an existing restore point, just update the information UPDATE grsp SET from_scn = checkGuaranteedRP.from_scn, to_scn = checkGuaranteedRP.to_scn, rsptime = checkGuaranteedRP.rsp_time, guaranteed = checkGuaranteedRP.guaranteed, dbinc_key = dbinc_key WHERE grsp.rspname = checkGuaranteedRP.rspname AND grsp.site_key = this_site_key; fetchGrsp; END IF; END checkGuaranteedRP; PROCEDURE endGuaranteedRPResync IS BEGIN WHILE (grspRec.rspname != chr(255)) LOOP -- if we get here the restore point has disappered from the controlfile -- this can happen only if the controlfile is recreated or dropped dropGrsp(grspRec.rspname); fetchGrsp; END LOOP; grspRec.rspname := NULL; -- update high_grsp_resync for the next resync UPDATE node SET high_grsp_recid = last_grsp_recid WHERE site_key = this_site_key; last_grsp_recid := NULL; END endGuaranteedRPResync; /*-----------------------------------* * RMAN Configuration records resync * *-----------------------------------*/ FUNCTION beginConfigResync( high_conf_recid IN NUMBER ) RETURN NUMBER IS BEGIN checkResync; SELECT high_conf_recid INTO last_conf_recid FROM node WHERE site_key = this_site_key; IF (high_conf_recid = last_conf_recid) THEN RETURN CONFIGRESYNC_NO; -- no resync needed ELSIF (last_conf_recid IS NULL OR high_conf_recid > last_conf_recid) THEN last_conf_recid := high_conf_recid; RETURN CONFIGRESYNC_TORC; -- we need resync from CF to RC ELSE last_conf_recid := high_conf_recid; RETURN CONFIGRESYNC_TOCF; -- we need resync from RC to CF END IF; END beginConfigResync; PROCEDURE endConfigResync IS BEGIN -- update high_conf_recid for the next resync UPDATE node SET high_conf_recid = last_conf_recid WHERE site_key = this_site_key; last_conf_recid := NULL; END endConfigResync; FUNCTION beginConfigResync2( high_conf_recid IN NUMBER ) RETURN NUMBER IS to_CF boolean := FALSE; to_Catalog boolean := FALSE; local_force_resync2cf VARCHAR2(3) := 'NO'; curr_cf_version_time DATE; conf_count NUMBER; BEGIN checkResync; SELECT node.high_conf_recid, node.force_resync2cf, cf_create_time INTO last_conf_recid, local_force_resync2cf, curr_cf_version_time FROM node WHERE site_key = this_site_key; -- If local_force_resync2cf is set then we have to resync into controlfile. IF (local_force_resync2cf = 'YES') THEN to_CF := TRUE; END IF; -- If this is the first time the site is exposed to catalog always -- get the existing configuration from the control file. IF (last_cf_version_time is NULL) THEN SELECT COUNT(*) INTO conf_count FROM CONF WHERE site_key = this_site_key; IF conf_count = 0 THEN to_Catalog := TRUE; END IF; END IF; -- If this is not same as last control file... perform resync based on -- recid. For current control file, trust control file if it has more -- configuration recid than catalog. For backup, always trust recovery -- catalog. IF (last_cf_version_time <> curr_cf_version_time) THEN IF (this_cf_type = 'CURRENT') THEN IF high_conf_recid > last_conf_recid THEN to_Catalog := TRUE; ELSIF (high_conf_recid < last_conf_recid) THEN to_CF := TRUE; END IF; ELSE to_CF := TRUE; END IF; END IF; -- If recovery catalog configuration recid is smaller than recid in -- controlfile (that is high_conf_stamp), then we will resync from -- controlfile to catalog. -- Otherwise, if last_conf_recidis greater, the data will go into -- controlfile. IF (last_cf_version_time = curr_cf_version_time) THEN IF (high_conf_recid > last_conf_recid) THEN to_Catalog := TRUE; ELSIF (high_conf_recid < last_conf_recid) THEN to_CF := TRUE; END IF; END IF; -- If no records are to be moved from control file to/from catalog, no -- resync needed. IF (NOT to_Catalog AND NOT to_CF) THEN RETURN CONFIGRESYNC_NO; END IF; -- Initialize water marks for next resync, timestamps are fixed as we -- resync individual configuration records. last_conf_recid := high_conf_recid; -- check if we need to fix only control file IF (NOT to_Catalog AND to_CF) THEN RETURN CONFIGRESYNC_TOCF; END IF; -- check if we need to only resync to catalog IF (to_Catalog AND NOT to_CF) THEN RETURN CONFIGRESYNC_TORC; END IF; -- Check if we need to do two way updates, i.e. to control file and to -- recovery catalog. IF (to_Catalog AND to_CF) THEN RETURN CONFIGRESYNC_TORC_TOCF; END IF; END beginConfigResync2; PROCEDURE endConfigResync2(sync_to_cf_pending IN boolean DEFAULT FALSE) IS db_role node.database_role%type; cf_pending number := 0; BEGIN IF sync_to_cf_pending THEN cf_pending := 1; END IF; IF (force_resync2cf = 'YES') THEN deb('endConfigResync2 - force_resync2cf = TRUE'); -- Set bit which forces resync from cf to TRUE for all -- others nodes except this one. UPDATE node SET node.force_resync2cf = 'YES' WHERE node.db_key = this_db_key AND site_key <> this_site_key; END IF; -- Update high_conf_recid in the node table only for this -- db_unique_name. This means that we will not cause resync for other -- nodes. -- Also, update force_resync2cf to FALSE, because we are sure that -- we have resynced everything. UPDATE node SET node.high_conf_recid = last_conf_recid, node.force_resync2cf = decode(cf_pending, 1, 'YES', 'NO') WHERE site_key = this_site_key; deb('endConfigResync2 - last_conf_recid='||last_conf_recid); force_resync2cf := 'NO'; last_conf_recid := NULL; END endConfigResync2; PROCEDURE getConfig( conf# OUT number ,name IN OUT varchar2 ,value IN OUT varchar2 ,first IN boolean) IS eof boolean := FALSE; BEGIN -- call getConfig from rcvman package. Note that in this we call -- recovery catalog version! dbms_rcvman.getConfig(conf#, name, value, first); END getConfig; PROCEDURE updateRestorePoint( lowscn IN NUMBER ,highscn IN NUMBER DEFAULT NULL -- next scn by another name ) IS nextscn number; refs number; BEGIN -- Default nextscn (not provided for datafile backups) IF (highscn is null) THEN nextscn := lowscn + 1; ELSE nextscn := highscn; END IF; -- Just set all the restore points in this range to NULL, for unknown. -- Routine cleanupNRS will set proper YES/NO value. UPDATE nrsp r SET LONG_TERM = NULL WHERE r.to_scn >= lowscn AND r.to_scn <= nextscn AND r.long_term IS NOT NULL AND r.site_key = this_site_key; deb('updateRestorePoint - (lowscn ' || lowscn || ' - highscn ' || nextscn || ') rows updated ' || sql%rowcount); END updateRestorePoint; /*-------------------------* * Redo Log History resync * *-------------------------*/ FUNCTION beginLogHistoryResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN -- find the highest log history recid that has been recorded in rcvcat -- and return it to the caller SELECT high_rlh_recid INTO last_rlh_recid FROM node WHERE site_key = this_site_key; ELSE last_rlh_recid := sessionWaterMarks.high_rlh_recid; END IF; RETURN last_rlh_recid; END beginLogHistoryResync; FUNCTION getLogHistoryLowSCN RETURN NUMBER IS lowSCN number; BEGIN checkResync; SELECT nvl(max(low_scn), 0) INTO lowSCN FROM rlh WHERE rlh.dbinc_key = this_dbinc_key; RETURN lowSCN; END getLogHistoryLowSCN; 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 ,reset_scn IN number default NULL ,reset_time IN date default NULL ) 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; IF (last_dbinc_key is NULL or reset_scn is NULL) THEN deb('checkLogHistory - Init last_dbinc_key'); last_dbinc_key := this_dbinc_key; select reset_scn, reset_time into last_reset_scn, last_reset_time from dbinc where dbinc_key = this_dbinc_key; END IF; IF (reset_scn IS NOT NULL and reset_time IS NOT NULL) THEN IF (reset_scn <> last_reset_scn or reset_time <> last_reset_time) THEN BEGIN deb('checkLogHistory - new last_dbinc_key'); deb('checkLogHistory - for reset_time ' || checkLogHistory.reset_time || ' reset_scn ' || checkLogHistory.reset_scn || ' this_db_key ' || this_db_key); select dbinc_key into last_dbinc_key from dbinc where reset_time = checkLogHistory.reset_time and reset_scn = checkLogHistory.reset_scn and db_key = this_db_key; last_reset_scn := reset_scn; last_reset_time := reset_time; EXCEPTION WHEN others THEN raise_application_error(-29999, 'Unknown Incarnation'); END; END IF; END IF; deb('checkLogHistory - last_dbinc_key='||last_dbinc_key|| ' reset_scn '||reset_scn || ' reset_time '||reset_time); BEGIN INSERT INTO rlh( rlh_key, dbinc_key, rlh_recid, rlh_stamp, thread#, sequence#, low_scn, low_time, next_scn) VALUES( rman_seq.nextval, last_dbinc_key, rlh_recid, rlh_stamp, thread#, sequence#, low_scn, low_time, next_scn); EXCEPTION WHEN dup_val_on_index THEN -- We already have a rlh for the same incarnation with same thread#, -- sequence# and low_scn RETURN; END; END checkLogHistory; PROCEDURE endLogHistoryResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN -- set the high_rlh_recid for the next resync UPDATE node SET high_rlh_recid = last_rlh_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_rlh_recid := last_rlh_recid; last_rlh_recid := NULL; END endLogHistoryResync; /*-------------------------* * Archived Log resync * *-------------------------*/ FUNCTION beginArchivedLogResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_al_recid INTO last_al_recid FROM node WHERE site_key = this_site_key; ELSE last_al_recid := sessionWaterMarks.high_al_recid; END IF; RETURN last_al_recid; END beginArchivedLogResync; PROCEDURE deleteDuplicateAL(recid IN NUMBER, stamp IN NUMBER, fname in VARCHAR2) IS lfname al.fname%TYPE; BEGIN lfname := fname; IF lfname is null THEN BEGIN SELECT fname INTO lfname from AL WHERE al_recid = recid AND al_stamp = stamp AND al.dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key); EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique key is dbinc_key, al_recid, al_stamp RETURN; END; END IF; -- Mark any previous archived logs with the same name except the one -- with given recid/stamp as deleted. DELETE al WHERE al.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND al.fname = lfname AND ((nvl(al.site_key, this_site_key) = this_site_key) OR (logs_shared = TRUE#)) AND al.fname_hashkey = substr(lfname,1,10)||substr(lfname,-10) AND NOT (al.al_recid = recid AND al.al_stamp = stamp ); END deleteDuplicateAL; PROCEDURE checkArchivedLog( al_recid IN NUMBER ,al_stamp IN NUMBER ,thread# IN NUMBER ,sequence# IN NUMBER ,reset_scn IN NUMBER ,reset_time IN DATE ,low_scn IN NUMBER ,low_time IN DATE ,next_scn IN NUMBER ,next_time IN DATE ,blocks IN NUMBER ,block_size IN NUMBER ,fname IN VARCHAR2 ,archived IN VARCHAR2 ,completion_time IN DATE ,status IN VARCHAR2 ,is_standby IN VARCHAR2 ,dictionary_begin IN VARCHAR2 default NULL ,dictionary_end IN VARCHAR2 default NULL ,is_recovery_dest_file IN VARCHAR2 default 'NO' ,compressed IN VARCHAR2 default 'NO' ,creator IN VARCHAR2 default NULL ,terminal IN VARCHAR2 default 'NO' ) 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 deb('checkArchivedLog - ignoring record kccdivts='||kccdivts); 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 -- calling the procedure is expensive, we can optimize as in case of resyncs -- for offr and rlh. This optimization can be done within checkIncarnation. my_dbinc_key := checkIncarnation(reset_scn, reset_time); BEGIN IF (status = 'D') THEN -- Do not bother to insert this record. NULL; ELSE INSERT INTO al (al_key, dbinc_key, al_recid, al_stamp, thread#, sequence#, low_scn, low_time, next_scn, next_time, fname, fname_hashkey, archived, blocks, block_size, completion_time, status, is_standby, dictionary_begin, dictionary_end, is_recovery_dest_file, compressed, creator, terminal, site_key) VALUES (rman_seq.nextval, my_dbinc_key, al_recid, al_stamp, thread#, sequence#, low_scn, low_time, next_scn, next_time, fname, substr(fname,1,10)||substr(fname, -10), archived, blocks, checkArchivedLog.block_size, completion_time, status, is_standby, dictionary_begin, dictionary_end, is_recovery_dest_file, compressed, creator, terminal, this_site_key); deleteDuplicateAL(al_recid, al_stamp, fname); END IF; -- Note that also cleared entries are inserted into rcvcat. -- Update log history entry. If fname is null and it is not archived -- then the log was cleared. Otherwise, this could as well be a deleted -- archived log file. IF checkArchivedLog.archived = 'N' then 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; END IF; EXCEPTION WHEN dup_val_on_index THEN deb('checkArchivedLog - Inside dup_val_on_index exception'); -- this archived log already exist in rcvcat. Get the existing archived -- record to validate it. SELECT * INTO local FROM al WHERE al.dbinc_key = my_dbinc_key AND (al.is_standby = checkArchivedLog.is_standby OR (al.is_standby is NULL AND checkArchivedLog.is_standby is NULL)) AND al.al_recid = checkArchivedLog.al_recid AND al.al_stamp = checkArchivedLog.al_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> local.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- check that fname matches IF (fname <> local.fname) THEN deb('checkArchivedLog - input fname ['||fname||']; local.fname ['|| local.fname || ']'); raise_application_error(-20080, 'Invalid archived log name'); END IF; END; END checkArchivedLog; PROCEDURE endArchivedLogResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_al_recid = last_al_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_al_recid := last_al_recid; last_al_recid := NULL; END endArchivedLogResync; /*-------------------------* * Offline range resync * *-------------------------*/ FUNCTION beginOfflineRangeResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_offr_recid INTO last_offr_recid FROM node WHERE site_key = this_site_key; ELSE last_offr_recid := sessionWaterMarks.high_offr_recid; END IF; RETURN last_offr_recid; END beginOfflineRangeResync; PROCEDURE checkOfflineRange( offr_recid IN NUMBER ,offr_stamp IN NUMBER ,file# IN NUMBER ,create_scn IN NUMBER ,offline_scn IN NUMBER ,online_scn IN NUMBER ,online_time IN DATE ,cf_create_time IN DATE ,reset_scn IN number default NULL ,reset_time IN date default NULL ) IS local offr%rowtype; -- Banand Jan-01-2006 - Rewrote completly routine, after dropping constraint -- offr_u1(dbinc_key, offr_recid, offr_stamp) BEGIN IF (last_offr_recid IS NULL AND offr_recid IS NOT NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; deb('Checkofflinerange - '|| ' recid: '|| nvl(to_char(offr_recid), 'NULL')|| ' stamp: '|| nvl(to_char(offr_stamp), 'NULL')|| ' file#: '|| file#|| ' create_scn: '|| nvl(to_char(create_scn), 'NULL')|| ' offline_scn: '|| offline_scn || ' online_scn: '|| online_scn|| ' online_time: '|| online_time|| ' cf_create_time: '|| cf_create_time|| ' reset_scn:'|| nvl(reset_scn, -1)); last_offr_recid := offr_recid; IF (last_dbinc_key is NULL OR reset_scn IS NULL) THEN deb('checkOfflineRange - Init dbinc_key: '||this_dbinc_key); last_dbinc_key := this_dbinc_key; SELECT reset_scn, reset_time INTO last_reset_scn, last_reset_time FROM dbinc WHERE dbinc_key = this_dbinc_key; END IF; IF (reset_scn IS NOT NULL and reset_time IS NOT NULL) THEN IF (reset_scn <> last_reset_scn or reset_time <> last_reset_time) THEN BEGIN deb('checkOfflineRange - new incarnation detected'|| ' reset_scn: '|| reset_scn|| ' last_reset_scn: '|| last_reset_scn); SELECT dbinc_key INTO last_dbinc_key FROM dbinc WHERE reset_time = checkOfflineRange.reset_time AND reset_scn = checkOfflineRange.reset_scn AND db_key = this_db_key; last_reset_scn := reset_scn; last_reset_time := reset_time; EXCEPTION WHEN others THEN raise_application_error(-20070, 'Unknown Incarnation'); END; END IF; END IF; deb('checkOfflineRange - dbinc_key is: '||last_dbinc_key); deb('checkOfflineRange - Looking if offline range record already '|| 'exists in OFFR'); BEGIN -- We must get either one row or no rows, otherwise it is error... SELECT distinct file#, create_scn, offline_scn, online_scn, online_time INTO local.file#, local.create_scn, local.offline_scn, local.online_scn, local.online_time FROM offr WHERE dbinc_key = last_dbinc_key AND file# = checkOfflineRange.file# AND create_scn = checkOfflineRange.create_scn AND offline_scn = checkOfflineRange.offline_scn; IF local.online_scn <> checkOfflineRange.online_scn THEN deb('checkOfflineRange - Online_scn OK?'|| ' online_scn: ' || online_scn || ' local.online_scn: ' || local.online_scn); -- raise_application_error(-20087, 'Invalid online SCN'); END IF; IF local.online_time <> checkOfflineRange.online_time THEN deb('checkOfflineRange - Online_time OK?'|| ' online_time: ' || online_time || ' local.online_time: ' || local.online_time); -- raise_application_error(-20089, 'Invalid online time'); END IF; EXCEPTION WHEN no_data_found THEN NULL; -- offline range record not yet known to catalog, go to insert WHEN too_many_rows THEN RAISE; -- there must not be more then on offline range with same -- dbinc_key, file#, create_scn, and offline_scn WHEN others THEN RAISE; END; 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, last_dbinc_key, offr_recid, nvl(offr_stamp,0), file#, create_scn, offline_scn, online_scn, online_time, cf_create_time); incResyncActions(RESYNC_ACTION_CHANGE, file#, to_char(NULL)); deb('checkOfflineRange - Succesfully inserted new OFFR.'); EXCEPTION WHEN dup_val_on_index THEN deb('checkOfflineRange - record already exists'); IF offr_recid > 0 AND offr_stamp > 0 THEN deb('checkOfflineRange - update new offr_recid, offr_stamp, '|| 'online_scn and online_time'); UPDATE OFFR SET offr_recid = checkOfflineRange.offr_recid, offr_stamp = checkOfflineRange.offr_stamp, online_scn = checkOfflineRange.online_scn, online_time= checkOfflineRange.online_time WHERE dbinc_key = last_dbinc_key AND file# = checkOfflineRange.file# AND create_scn = checkOfflineRange.create_scn AND offline_scn = checkOfflineRange.offline_scn AND cf_create_time = checkOfflineRange.cf_create_time; incResyncActions(RESYNC_ACTION_CHANGE, file#, to_char(NULL)); END IF; END; deb('checkOfflineRange - exiting'); END; PROCEDURE endOfflineRangeResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_offr_recid = last_offr_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_offr_recid := last_offr_recid; 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 it has no backup pieces (either all pieces -- are deleted, or this was a backup validate). Otherwise it is marked 'O' -- (other). -- Here we want to look at all backup pieces, not just accessible at this -- site, before updating backup set status. This has to be done so, because -- a backup set's pieces can belong to different sites and all pieces may -- not accessible at this site may be accessible to some other site. PROCEDURE updateBackupSetRec(bs_key IN NUMBER) IS total_pieces NUMBER; backup_validate VARCHAR2(3); available_pieces NUMBER; new_status VARCHAR2(1); bskeep NUMBER; bstype VARCHAR2(1); low NUMBER := NULL; high NUMBER := NULL; bs_site_key NUMBER := NULL; pieces_on_msite NUMBER; new_site_key NUMBER; BEGIN SELECT pieces,input_file_scan_only, keep_options, bck_type, site_key INTO total_pieces,backup_validate, bskeep, bstype, bs_site_key FROM bs WHERE bs.bs_key = updateBackupSetRec.bs_key; IF nvl(backup_validate,'NO') <> 'YES' THEN 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; END IF; -- if all pieces are not on one site, set site_key to null IF bs_site_key IS NULL OR bs_site_key <> this_site_key THEN SELECT count(distinct site_key) INTO pieces_on_msite FROM bp WHERE bs_key = updateBackupSetRec.bs_key; IF pieces_on_msite = 1 THEN SELECT distinct site_key INTO new_site_key FROM BP WHERE bs_key = updateBackupSetRec.bs_key; END IF; -- update site_key in BS to new_site_key or null UPDATE bs SET site_key = new_site_key WHERE bs.bs_key = updateBackupSetRec.bs_key; END IF; IF (total_pieces = 0 or backup_validate = 'YES') THEN -- Bug 1467871: Remove dummy records inserted in 8.1.6- versions RMAN new_status := 'D'; ELSIF (available_pieces = total_pieces) THEN new_status := 'A'; ELSE BEGIN -- set new_status to 'O' (other) if some non-deleted rows found SELECT 'O' INTO new_status FROM bp WHERE bp.bs_key = updateBackupSetRec.bs_key AND bp.status != 'D' AND rownum < 2; EXCEPTION WHEN no_data_found THEN new_status := 'D'; -- all pieces are deleted or not there END; END IF; IF new_status in ('O', 'A') OR backup_validate = 'YES' THEN UPDATE bs SET status = new_status WHERE bs.bs_key = updateBackupSetRec.bs_key; ELSE -- Manage LONG_TERM flag on restore points that might be useful for this IF (bskeep > 0 and bstype = 'L') THEN SELECT min(low_scn), max(next_scn) INTO low, high FROM brl WHERE bs_key = updateBackupSetRec.bs_key; END IF; IF (bskeep > 0 and bstype = 'D') THEN SELECT min(ckp_scn) INTO low FROM bdf WHERE bs_key = updateBackupSetRec.bs_key; END IF; -- 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; -- Alter the restore point table to reflect deleted backupSet IF (low IS NOT NULL) THEN updateRestorePoint(low, high); END IF; END IF; END updateBackupSetRec; FUNCTION beginBackupSetResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_bs_recid INTO last_bs_recid FROM node WHERE site_key = this_site_key; ELSE last_bs_recid := sessionWaterMarks.high_bs_recid; END IF; RETURN last_bs_recid; END beginBackupSetResync; PROCEDURE checkBackupSet( bs_recid IN NUMBER ,bs_stamp IN NUMBER ,set_stamp IN NUMBER ,set_count IN NUMBER ,bck_type IN VARCHAR2 ,incr_level IN NUMBER DEFAULT NULL ,pieces IN NUMBER ,start_time IN DATE ,completion_time IN DATE ,controlfile_included IN VARCHAR2 DEFAULT NULL ,input_file_scan_only IN VARCHAR2 DEFAULT NULL ,keep_options IN NUMBER DEFAULT 0 ,keep_until IN DATE DEFAULT NULL ,block_size IN NUMBER DEFAULT NULL ,multi_section IN VARCHAR2 DEFAULT NULL ) IS local bs%rowtype; newbskey number; BEGIN IF (last_bs_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (bs_recid < last_bs_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (bs_recid > last_bs_recid + 1) THEN -- there is gap in backup set records NULL; END IF; last_bs_recid := bs_recid; IF (bs_stamp < kccdivts) THEN deb('checkBackupSet - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; IF (bck_type NOT IN ('D','I','L') OR bck_type IS NULL) THEN raise_application_error(-20090, 'Invalid backup set type'); END IF; IF (incr_level NOT IN (0,1,2,3,4) OR (bck_type NOT IN ('D','I') AND incr_level <> 0)) THEN raise_application_error(-20091, 'Invalid backup set level'); END IF; BEGIN select rman_seq.nextval into newbskey from dual; -- insert the backup set with status 'D'. Backup piece resync will mark -- the backup set available if all pieces are found. INSERT INTO bs (bs_key, db_key, bs_recid, bs_stamp, set_stamp, set_count, bck_type, incr_level, pieces, start_time, completion_time, status, controlfile_included, input_file_scan_only, keep_options, keep_until, block_size, site_key, multi_section) VALUES (newbskey, this_db_key, bs_recid, bs_stamp, set_stamp, set_count, bck_type, incr_level, pieces, start_time, completion_time, 'D', decode(controlfile_included, 'SBY','STANDBY','YES','BACKUP','NONE'), input_file_scan_only, keep_options, keep_until, block_size, this_site_key, decode(multi_section,'YES','Y',null)); cntbs := cntbs + 1; updatebs(cntbs) := newbskey; EXCEPTION WHEN dup_val_on_index THEN deb('checkBackupSet - Inside dup_val_on_index exception'); -- backup set is already in rcvcat SELECT * INTO local FROM bs WHERE bs.db_key = this_db_key AND bs.set_stamp = checkBackupSet.set_stamp AND bs.set_count = checkBackupSet.set_count; -- Total pieces is a simple guess during backuppiece inspection. -- Update total pieces if this guess is greater than previous one. -- IF (pieces > local.pieces) THEN UPDATE bs SET bs.pieces = checkBackupSet.pieces WHERE bs.db_key = this_db_key AND bs.bs_key = local.bs_key; -- validate this backupset during sanityCheck cntbs:= cntbs + 1; updatebs(cntbs) := local.bs_key; END IF; -- Detect here if the backup set contains same signature or different -- If different throw raise_application_error -- start_time is based on set_stamp, hence the old condition is bogus... -- TODO -- If site_key is null, update with this_site_key IF local.site_key IS NULL THEN UPDATE bs SET site_key = this_site_key WHERE bs.db_key = this_db_key AND bs.bs_key = local.bs_key; END IF; END; END checkBackupSet; PROCEDURE endBackupSetResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN -- update high_bs_recid for the next resync UPDATE node SET high_bs_recid = last_bs_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_bs_recid := last_bs_recid; last_bs_recid := NULL; END endBackupSetResync; /*-------------------------* * Backup piece resync * *-------------------------*/ FUNCTION beginBackupPieceResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_bp_recid INTO last_bp_recid FROM node WHERE site_key = this_site_key; ELSE last_bp_recid := sessionWaterMarks.high_bp_recid; END IF; RETURN last_bp_recid; END beginBackupPieceResync; PROCEDURE deleteDuplicateBP(recid IN NUMBER, stamp IN NUMBER, bs_key IN NUMBER, device_type IN VARCHAR2, handle IN VARCHAR2) IS ldevice_type bp.device_type%TYPE; lhandle bp.device_type%TYPE; BEGIN ldevice_type := device_type; lhandle := handle; IF ldevice_type IS NULL OR lhandle IS NULL THEN BEGIN SELECT device_type, handle INTO ldevice_type, lhandle FROM BP WHERE bp.db_key = this_db_key AND bp_recid = recid AND bp_stamp = stamp AND ((disk_backups_shared = TRUE# AND bp.device_type = 'DISK') OR (tape_backups_shared = TRUE# AND bp.device_type <> 'DISK') OR (this_site_key = nvl(bp.site_key, this_site_key))) AND deleteDuplicateBP.bs_key = bp.bs_key; EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique key is bs_key, recid, stamp RETURN; END; END IF; -- If there is a piece on same device_type and handle we assume that it -- was written over by current piece and we mark it deleted. -- mark the matching backup pieces as deleted in a loop, so we mark all -- their backup sets too. FOR bprec IN bpq(ldevice_type, lhandle, recid, stamp) LOOP DELETE bp WHERE bp.bp_key = bprec.bp_key; updateBackupSetRec(bprec.bs_key); -- update the backupset status END LOOP; END deleteDuplicateBP; 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 ,bytes IN NUMBER default NULL ,is_recovery_dest_file IN VARCHAR2 default 'NO' ,rsr_recid IN NUMBER default NULL ,rsr_stamp IN NUMBER default NULL ,compressed IN VARCHAR2 default 'NO' ,encrypted IN VARCHAR2 default 'NO' ,backed_by_osb IN VARCHAR2 default 'NO' ) IS localbs bs%rowtype; localbp bp%rowtype; localrsr rsr%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 deb('checkBackupPiece - ignoring record kccdivts='||kccdivts); 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 * into localbs from bs WHERE bs.db_key = this_db_key AND bs.set_stamp = checkBackupPiece.set_stamp AND bs.set_count = checkBackupPiece.set_count; EXCEPTION WHEN no_data_found THEN IF status != 'D' THEN select rman_seq.nextval into localbs.bs_key from dual; INSERT INTO bs (bs_key, db_key, bs_recid, bs_stamp, set_stamp, set_count, bck_type, incr_level, pieces, start_time, completion_time, status, controlfile_included, site_key, multi_section) VALUES -- Since we do not 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 do not use NULL is because -- old RMAN versions do not use a null indicator when they select -- this column. (localbs.bs_key, this_db_key, 0, 0, checkBackupPiece.set_stamp, checkBackupPiece.set_count, NULL, NULL, checkBackupPiece.piece#, checkBackupPiece.start_time, checkBackupPiece.completion_time, 'O', 'NONE', this_site_key, NULL); cntbs := cntbs + 1; updatebs(cntbs) := localbs.bs_key; ELSE -- no backupset records available RETURN; END IF; END; -- backupset records status are updated at end of resync during -- sanityCheck. So, why go beyond this point if status is 'D' ???? IF (status = 'D') THEN cntbs:= cntbs + 1; updatebs(cntbs) := localbs.bs_key; RETURN; END IF; -- got inserted by no_data_found exception IF (localbs.bs_recid is null OR localbs.bs_recid = 0) 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 = checkBackupPiece.piece# WHERE bs.bs_key = localbs.bs_key AND bs.bck_type IS NULL; END IF; -- Find the owning rsr row and get its key. BEGIN SELECT rsr_key INTO localrsr.rsr_key FROM rsr WHERE rsr.dbinc_key = this_dbinc_key AND (rsr.site_key = this_site_key OR rsr.site_key is null AND this_site_key is NULL) AND rsr.rsr_stamp = checkBackupPiece.rsr_stamp AND rsr.rsr_recid = checkBackupPiece.rsr_recid; EXCEPTION WHEN no_data_found THEN -- no rsr record avaiable - ignore NULL; END; 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, bytes, is_recovery_dest_file, rsr_key, compressed, site_key, encrypted, backed_by_osb) 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, bytes, is_recovery_dest_file, localrsr.rsr_key, compressed, this_site_key, decode(encrypted, 'YES', 'Y', 'N'), decode(backed_by_osb, 'YES', 'Y', 'N')); deleteDuplicateBP(bp_recid, bp_stamp, localbs.bs_key, device_type, handle); -- validate the backup set. updateBackupSetRec(localbs.bs_key); EXCEPTION WHEN dup_val_on_index THEN deb('checkBackupPiece - Inside dup_val_on_index exception'); -- check if the backup piece record is already in rcvcat -- must get one record ... SELECT * INTO localbp FROM bp WHERE bp.bs_key = localbs.bs_key AND bp.bp_recid = checkBackupPiece.bp_recid AND bp.bp_stamp = checkBackupPiece.bp_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> localbp.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- check the piece# IF (piece# <> localbp.piece#) THEN raise_application_error(-20093, 'Invalid piece#'); END IF; -- If site_key is null, update with this_site_key IF localbp.site_key IS NULL THEN UPDATE bp SET site_key = this_site_key WHERE bp.bs_key = localbs.bs_key AND bp.bp_recid = checkBackupPiece.bp_recid AND bp.bp_stamp = checkBackupPiece.bp_stamp; END IF; END; END checkBackupPiece; PROCEDURE endBackupPieceResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_bp_recid = last_bp_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_bp_recid := last_bp_recid; last_bp_recid := NULL; END endBackupPieceResync; /*-------------------------* * Backup Datafile resync * *-------------------------*/ PROCEDURE addBackupControlFile( bs_key IN NUMBER ,bcf_recid IN NUMBER ,bcf_stamp IN NUMBER ,dbinc_key IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,create_time IN DATE ,min_offr_recid IN NUMBER ,blocks IN NUMBER ,block_size IN NUMBER ,controlfile_type IN VARCHAR2 ,cfile_abck_year IN number ,cfile_abck_mon_day IN number ,cfile_abck_seq IN number ) IS local bcf%rowtype; BEGIN BEGIN INSERT INTO bcf(bcf_key, bs_key, dbinc_key, bcf_recid, bcf_stamp, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, controlfile_type, blocks, autobackup_date, autobackup_sequence) VALUES (rman_seq.nextval, bs_key, dbinc_key, bcf_recid, bcf_stamp, ckp_scn, ckp_time, create_time, min_offr_recid,block_size, controlfile_type, blocks, decode(cfile_abck_year, 0, to_date(NULL), to_date(to_char(cfile_abck_year)|| lpad(to_char(cfile_abck_mon_day), 4, '0'), 'YYYYMMDD', 'NLS_CALENDAR=Gregorian')), cfile_abck_seq); EXCEPTION WHEN dup_val_on_index THEN deb('addBackupControlfile - Inside dup_val_on_index exception'); -- this backup controlfile record already exists in rcvcat, must find -- record. SELECT ckp_scn, ckp_time, bcf_recid, bcf_stamp INTO local.ckp_scn, local.ckp_time, local.bcf_recid, local.bcf_stamp FROM bcf WHERE bcf.bs_key = addBackupControlFile.bs_key; -- check the ckp_scn and ckp_time IF (ckp_scn <> local.ckp_scn or ckp_time <> local.ckp_time) THEN deb('addBackupControlfile - ckp_scn '||ckp_scn||' ckp_time '|| to_char(ckp_time)); deb('addBackupControlfile - lckp_scn '||local.ckp_scn||' lckp_time '|| to_char(local.ckp_time)); raise_application_error(-20095, 'Invalid ckp_scn or ckp_time'); END IF; -- fix recid/stamp if they do not match IF local.bcf_recid <> bcf_recid or local.bcf_stamp <> bcf_stamp THEN UPDATE bcf set bcf_recid = addBackupControlFile.bcf_recid, bcf_stamp = addBackupControlFile.bcf_stamp WHERE bcf.bs_key = addBackupControlFile.bs_key; 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 ,blocks_read IN NUMBER ,create_time IN DATE ,marked_corrupt IN NUMBER ,used_chg_track IN VARCHAR2 ,used_optim IN VARCHAR2 ,foreign_dbid IN number ,plugged_readonly IN varchar2 ,plugin_scn IN number ,plugin_reset_scn IN number ,plugin_reset_time IN date ,section_size IN number ) 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, blocks_read, create_time, marked_corrupt, used_chg_track, used_optim, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, section_size) 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, nvl(blocks_read, datafile_blocks), create_time, marked_corrupt, decode(used_chg_track, 'YES', 'Y', 'N'), decode(used_optim, 'YES', 'Y', 'N'), foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, section_size); EXCEPTION WHEN dup_val_on_index THEN deb('addBackupDatafile - Inside dup_val_on_index exception'); -- this backup datafile record already exist in rcvcat -- must exist a record with same create_scn SELECT dbinc_key, create_scn, bdf_recid, bdf_stamp, plugin_scn INTO local.dbinc_key, local.create_scn,local.bdf_recid, local.bdf_stamp, local.plugin_scn FROM bdf WHERE bdf.bs_key = addBackupDataFile.bs_key AND bdf.file# = addBackupDataFile.file#; -- check the dbinc_key and creation scn IF (dbinc_key <> local.dbinc_key) THEN raise_application_error(-20096, 'Invalid dbinc_key'); END IF; IF (create_scn <> local.create_scn AND plugin_scn <> local.plugin_scn) THEN raise_application_error(-20097, 'Invalid create scn'); END IF; -- fix recid/stamp if they do not match IF bdf_recid <> local.bdf_recid or bdf_stamp <> local.bdf_stamp THEN UPDATE bdf set bdf_recid = addBackupDataFile.bdf_recid, bdf_stamp = addBackupDataFile.bdf_stamp WHERE bdf.bs_key = addBackupDataFile.bs_key; END IF; END; END addBackupDataFile; FUNCTION beginBackupDataFileResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_bdf_recid INTO last_bdf_recid FROM node WHERE site_key = this_site_key; ELSE last_bdf_recid := sessionWaterMarks.high_bdf_recid; END IF; RETURN last_bdf_recid; END beginBackupDataFileResync; PROCEDURE checkBackupDataFile( bdf_recid IN NUMBER ,bdf_stamp IN NUMBER ,set_stamp IN NUMBER ,set_count IN NUMBER ,file# IN NUMBER ,create_scn IN NUMBER ,create_time IN DATE ,reset_scn IN NUMBER ,reset_time IN DATE ,incr_level IN NUMBER ,incr_scn IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,abs_fuzzy_scn IN NUMBER ,datafile_blocks IN NUMBER ,blocks IN NUMBER ,block_size IN NUMBER ,min_offr_recid IN NUMBER ,completion_time IN DATE ,controlfile_type IN VARCHAR2 DEFAULT NULL ,cfile_abck_year IN NUMBER DEFAULT NULL -- contains marked_corrupt for datafiles ,cfile_abck_mon_day IN NUMBER DEFAULT NULL -- contains media_corrupt for datafiles ,cfile_abck_seq IN NUMBER DEFAULT NULL -- contains logical_corrupt for datafiles ,chk_last_recid IN BOOLEAN DEFAULT TRUE ,blocks_read IN NUMBER DEFAULT NULL ,used_chg_track IN VARCHAR2 DEFAULT 'NO' ,used_optim IN VARCHAR2 DEFAULT 'NO' ,foreign_dbid IN number DEFAULT 0 ,plugged_readonly IN varchar2 DEFAULT 'NO' ,plugin_scn IN number DEFAULT 0 ,plugin_reset_scn IN number DEFAULT 0 ,plugin_reset_time IN date DEFAULT NULL ,section_size IN number DEFAULT NULL ) IS bs_key NUMBER; dbinc_key NUMBER; BEGIN -- chk_last_recid is FALSE during bdfbp resync (see recover.txt) IF chk_last_recid THEN 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; END IF; IF (bdf_stamp < kccdivts) THEN deb('In checkBackupDatafile, ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- Write a seperate function to return backup set key by taking -- set_stamp and set_count... take into account the node specific info -- in that function. TODO -- find the key of the backup set BEGIN SELECT bs_key INTO bs_key FROM bs WHERE bs.db_key = this_db_key AND bs.set_stamp = checkBackupDataFile.set_stamp AND bs.set_count = checkBackupDataFile.set_count; EXCEPTION WHEN no_data_found THEN -- Bug 1467871: bs_key should be inserted either in -- checkBackupSet (or) checkBackupPiece -- Exception would occur only when backupset records and -- backuppiece records ages out. Ignore silently these -- orphaned backup datafile records rather than inserting a 'D' -- record in BS table which will create dummy records with -- 8.1.6- version of RMAN and this catalog version. return; END; BEGIN -- update only those bs records created in no_data_found exception -- of checkBackupPiece, checkBackupDataFile and checkBackupRedoLog IF (checkBackupDatafile.incr_level > 0) THEN UPDATE bs SET bs.incr_level = checkBackupDataFile.incr_level, bs.bck_type = 'I' WHERE bs.bs_key = checkBackupDataFile.bs_key AND bs.bck_type IS NULL; ELSE UPDATE bs SET bs.incr_level = checkBackupDataFile.incr_level, bs.bck_type = 'D' WHERE bs.bs_key = checkBackupDataFile.bs_key AND bs.bck_type IS NULL; END IF; IF (file# = 0 and controlfile_type is not null) then UPDATE bs SET bs.controlfile_included= decode(checkBackupDatafile.controlfile_type,'B','BACKUP', 'S','STANDBY', 'NONE') WHERE bs.bs_key = checkBackupDataFile.bs_key AND bs.controlfile_included = 'NONE'; END IF; END; -- find the database incarnation key dbinc_key := checkIncarnation(reset_scn, reset_time); IF (file# = 0) THEN addBackupControlFile(bs_key, bdf_recid, bdf_stamp, dbinc_key, ckp_scn, ckp_time, create_time, min_offr_recid, blocks, block_size, controlfile_type, cfile_abck_year, cfile_abck_mon_day, cfile_abck_seq); ELSE addBackupDataFile(bs_key, bdf_recid, bdf_stamp, file#, create_scn, dbinc_key, incr_level, incr_scn, ckp_scn, ckp_time, abs_fuzzy_scn, datafile_blocks, blocks, block_size, completion_time, blocks_read, create_time, cfile_abck_year, used_chg_track, used_optim, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time, section_size); END IF; END checkBackupDataFile; PROCEDURE endBackupDataFileResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_bdf_recid = last_bdf_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_bdf_recid := last_bdf_recid; last_bdf_recid := NULL; END endBackupDataFileResync; /*-----------------------* * Backup SPFILE resync * *-----------------------*/ FUNCTION beginBackupSpFileResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_bsf_recid INTO last_bsf_recid FROM node WHERE site_key = this_site_key; ELSE last_bsf_recid := sessionWaterMarks.high_bsf_recid; END IF; RETURN last_bsf_recid; END beginBackupSpFileResync; PROCEDURE addBackupSpFile( bs_key IN NUMBER ,bsf_recid IN NUMBER ,bsf_stamp IN NUMBER ,modification_time IN DATE ,bytes IN NUMBER ,db_unique_name IN VARCHAR2 ) IS local bsf%rowtype; BEGIN deb('addBackupSpfile'); INSERT INTO bsf(bsf_key, bs_key, db_key, bsf_recid, bsf_stamp, modification_time, bytes, db_unique_name) VALUES (rman_seq.nextval, bs_key, this_db_key, bsf_recid, bsf_stamp, modification_time, bytes, db_unique_name); EXCEPTION WHEN dup_val_on_index THEN deb('addBackupSpfile - Inside dup_val_on_index exception'); -- this backup SPFILE record already exists in rcvcat -- must find a record with same backup set... SELECT * INTO local FROM bsf WHERE bsf.bs_key = addBackupSpFile.bs_key; -- check if the modification time differs IF (modification_time <> local.modification_time) THEN raise_application_error(-20101, 'Invalid modification_time'); END IF; -- check if the site_key differs IF (db_unique_name <> local.db_unique_name) THEN raise_application_error(-20101, 'Invalid db_unique_name=' || db_unique_name || 'expected db_unique_name=' || local.db_unique_name); END IF; -- fix recid/stamp if they do not match IF local.bsf_recid <> bsf_recid or local.bsf_stamp <> bsf_stamp THEN UPDATE bsf set bsf_recid = addBackupSpFile.bsf_recid, bsf_stamp = addBackupSpFile.bsf_stamp WHERE bsf.bs_key = addBackupSpFile.bs_key; END IF; END addBackupSpFile; PROCEDURE checkBackupSpFile( bsf_recid IN NUMBER ,bsf_stamp IN NUMBER ,set_stamp IN NUMBER ,set_count IN NUMBER ,modification_time IN DATE ,bytes IN NUMBER ,chk_last_recid IN BOOLEAN default TRUE ,db_unique_name IN varchar2 DEFAULT NULL ) IS bs_key NUMBER; site_key NUMBER; BEGIN IF chk_last_recid THEN IF (last_bsf_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (bsf_recid < last_bsf_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (bsf_recid > last_bsf_recid + 1) THEN -- there is gap in backup set records NULL; END IF; last_bsf_recid := bsf_recid; END IF; -- find the key of the backup set BEGIN SELECT bs_key INTO bs_key FROM bs WHERE bs.db_key = this_db_key AND bs.set_stamp = checkBackupSpFile.set_stamp AND bs.set_count = checkBackupSpFile.set_count; EXCEPTION WHEN no_data_found THEN return; END; addBackupSpFile(bs_key, bsf_recid, bsf_stamp, modification_time, bytes, db_unique_name); END checkBackupSpFile; PROCEDURE endBackupSpFileResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_bsf_recid = last_bsf_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_bsf_recid := last_bsf_recid; last_bsf_recid := NULL; END endBackupSpFileResync; /*-------------------------* * Backup Redo Log resync * *-------------------------*/ FUNCTION beginBackupRedoLogResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_brl_recid INTO last_brl_recid FROM node WHERE site_key = this_site_key; ELSE last_brl_recid := sessionWaterMarks.high_brl_recid; END IF; RETURN last_brl_recid; END beginBackupRedoLogResync; PROCEDURE checkBackupRedoLog( brl_recid IN NUMBER ,brl_stamp IN NUMBER ,set_stamp IN NUMBER ,set_count IN NUMBER ,thread# IN NUMBER ,sequence# IN NUMBER ,reset_scn IN NUMBER ,reset_time IN DATE ,low_scn IN NUMBER ,low_time IN DATE ,next_scn IN NUMBER ,next_time IN DATE ,blocks IN NUMBER ,block_size IN NUMBER ,chk_last_recid IN BOOLEAN DEFAULT TRUE ,terminal IN VARCHAR2 DEFAULT 'NO' ) IS local brl%rowtype; bskeep number; BEGIN -- ignore BRL entries with zero timestamp, they are added due to bug 5971763 -- when user is doing backup of logs and multiple archive log destination -- is set. IF brl_stamp = 0 THEN deb('checkBackupRedoLog: ignoring this record as brl_stamp is 0'); RETURN; END IF; -- chk_last_recid is FALSE during brlbp resync (see recover.txt) IF chk_last_recid THEN 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; 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,keep_options INTO local.bs_key, bskeep FROM bs WHERE bs.db_key = this_db_key AND bs.set_stamp = checkBackupRedoLog.set_stamp AND bs.set_count = checkBackupRedoLog.set_count; EXCEPTION WHEN no_data_found THEN RETURN; 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.bs_key = local.bs_key AND bs.bck_type IS NULL; END; -- find the dbinc_key local.dbinc_key := checkIncarnation(reset_scn, reset_time); BEGIN INSERT INTO brl (brl_key, dbinc_key, brl_recid, brl_stamp, thread#, sequence#, low_scn, low_time, next_scn, next_time, blocks, block_size, bs_key, terminal) 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, terminal); EXCEPTION WHEN dup_val_on_index THEN deb('checkBackupRedoLog - Inside dup_val_on_index exception'); -- the backup redo log record already exists -- must get one record SELECT low_scn, brl_recid, brl_stamp INTO local.low_scn, local.brl_recid, local.brl_stamp FROM brl WHERE brl.bs_key = local.bs_key AND brl.thread# = checkBackupRedoLog.thread# AND brl.sequence# = checkBackupRedoLog.sequence#; -- check the low_scn IF (low_scn <> local.low_scn) THEN raise_application_error(-20098, 'Invalid low scn'); END IF; -- fix recid/stamp if they do not match IF local.brl_recid <> brl_recid or local.brl_stamp <> brl_stamp THEN UPDATE brl set brl_recid = checkBackupRedoLog.brl_recid, brl_stamp = checkBackupRedoLog.brl_stamp WHERE brl.bs_key = local.bs_key AND brl.thread# = checkBackupRedoLog.thread# AND brl.sequence# = checkBackupRedoLog.sequence#; END IF; END; IF (bskeep > 0) THEN updateRestorePoint(low_scn, next_scn); END IF; END checkBackupRedoLog; PROCEDURE endBackupRedoLogResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_brl_recid = last_brl_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_brl_recid := last_brl_recid; last_brl_recid := NULL; END endBackupRedoLogResync; /*----------------------------* * Datafile Copy resync * *----------------------------*/ PROCEDURE deleteDuplicateCCF(recid IN NUMBER, stamp IN NUMBER, fname IN VARCHAR2) IS lfname ccf.fname%TYPE; BEGIN lfname := fname; IF lfname IS NULL THEN BEGIN SELECT fname INTO lfname FROM ccf WHERE ccf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND ccf_recid = recid AND ccf_stamp = stamp; EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique_key is dbinc_key, recid and stamp RETURN; END; END IF; -- Delete old copies as the new copy should have overwritten it DELETE ccf WHERE ccf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND ccf.fname = lfname AND ((disk_backups_shared = TRUE#) OR (this_site_key = nvl(ccf.site_key, this_site_key))) AND ccf.fname_hashkey = substr(lfname, 1, 10) || substr(lfname, -10) AND NOT (ccf.ccf_recid = recid AND ccf.ccf_stamp = stamp); END deleteDuplicateCCF; PROCEDURE addControlFileCopy( ccf_recid IN NUMBER ,ccf_stamp IN NUMBER ,fname IN VARCHAR2 ,tag IN VARCHAR2 ,dbinc_key IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,create_time IN DATE ,min_offr_recid IN NUMBER ,block_size IN NUMBER ,completion_time IN DATE ,status IN VARCHAR2 ,controlfile_type IN VARCHAR2 DEFAULT NULL ,keep_options IN NUMBER DEFAULT 0 ,keep_until IN DATE DEFAULT NULL ,is_recovery_dest_file IN VARCHAR2 ,rsr_key IN NUMBER DEFAULT NULL ,blocks IN NUMBER ) IS local ccf%rowtype; BEGIN BEGIN IF (status <> 'D') THEN INSERT INTO ccf(ccf_key, dbinc_key, ccf_recid, ccf_stamp, fname, fname_hashkey, tag, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, completion_time, status, controlfile_type, keep_options, keep_until, is_recovery_dest_file, rsr_key, blocks, site_key) VALUES (rman_seq.nextval, dbinc_key, ccf_recid, ccf_stamp, fname, substr(fname,1,10)||substr(fname,-10), tag, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, completion_time, status, controlfile_type, keep_options, keep_until, is_recovery_dest_file, rsr_key, blocks, this_site_key); deleteDuplicateCCF(ccf_recid, ccf_stamp, fname); END IF; EXCEPTION WHEN dup_val_on_index THEN deb('addControlFileCopy - Inside dup_val_on_index exception'); -- The controlfile copy exists already. SELECT * INTO local FROM ccf WHERE ccf.dbinc_key = addControlFileCopy.dbinc_key AND ccf.ccf_recid = addControlFileCopy.ccf_recid AND ccf.ccf_stamp = addControlFileCopy.ccf_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> local.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- check the ckp_scn IF (ckp_scn <> local.ckp_scn) THEN raise_application_error(-20095, 'Invalid ckp_scn'); END IF; -- If site_key is null, update with this_site_key IF local.site_key IS NULL THEN UPDATE ccf SET site_key = this_site_key WHERE ccf.dbinc_key = addControlFileCopy.dbinc_key AND ccf.ccf_recid = addControlFileCopy.ccf_recid AND ccf.ccf_stamp = addControlFileCopy.ccf_stamp; END IF; END; END addControlFileCopy; PROCEDURE deleteDuplicateCDF(recid IN NUMBER, stamp IN NUMBER, fname IN VARCHAR2) IS lfname cdf.fname%TYPE; BEGIN lfname := fname; IF lfname IS NULL THEN BEGIN SELECT fname INTO lfname FROM cdf WHERE cdf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND cdf_recid = recid AND cdf_stamp = stamp; EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique_key is dbinc_key, recid and stamp RETURN; END; END IF; -- Delete old copies as the new copy should have overwritten it DELETE cdf WHERE cdf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND cdf.fname = lfname AND ((disk_backups_shared = TRUE#) OR (this_site_key = nvl(cdf.site_key, this_site_key))) AND cdf.fname_hashkey = substr(lfname, 1, 10) || substr(lfname, -10) AND NOT (cdf.cdf_recid = recid AND cdf.cdf_stamp = stamp); END deleteDuplicateCDF; PROCEDURE addDataFileCopy( cdf_recid IN NUMBER ,cdf_stamp IN NUMBER ,fname IN VARCHAR2 ,tag IN VARCHAR2 ,file# IN NUMBER ,create_scn IN NUMBER ,dbinc_key IN NUMBER ,incr_level IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,onl_fuzzy IN VARCHAR2 ,bck_fuzzy IN VARCHAR2 ,abs_fuzzy_scn IN NUMBER ,rcv_fuzzy_scn IN NUMBER ,rcv_fuzzy_time IN DATE ,blocks IN NUMBER ,block_size IN NUMBER ,completion_time IN DATE ,status IN VARCHAR2 ,keep_options IN NUMBER ,keep_until IN DATE ,scanned IN VARCHAR2 ,is_recovery_dest_file IN VARCHAR2 ,rsr_key IN NUMBER ,create_time IN DATE ,marked_corrupt IN NUMBER ,foreign_dbid IN number ,plugged_readonly IN varchar2 ,plugin_scn IN number ,plugin_reset_scn IN number ,plugin_reset_time IN date ) IS local cdf%rowtype; BEGIN BEGIN IF (status <> 'D') THEN INSERT INTO cdf(cdf_key, dbinc_key, cdf_recid, cdf_stamp, file#, create_scn, fname, fname_hashkey, tag, incr_level, ckp_scn, ckp_time, onl_fuzzy, bck_fuzzy, abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, completion_time, status, keep_options, keep_until, scanned, is_recovery_dest_file, rsr_key, create_time, marked_corrupt, site_key, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time) VALUES (rman_seq.nextval, dbinc_key, cdf_recid, cdf_stamp, file#, create_scn, fname, substr(fname,1,10)||substr(fname, -10), tag, incr_level, ckp_scn, ckp_time, decode(onl_fuzzy,'YES','Y','NO','N'), decode(bck_fuzzy,'YES','Y','NO','N'), abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, completion_time, status, keep_options, keep_until, decode(scanned,'YES','Y','NO','N'), is_recovery_dest_file, rsr_key, create_time, marked_corrupt, this_site_key, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time); deleteDuplicateCDF(cdf_recid, cdf_stamp, fname); END IF; EXCEPTION WHEN dup_val_on_index THEN deb('addDataFileCopy - Inside dup_val_on_index exception'); SELECT * INTO local FROM cdf WHERE cdf.dbinc_key = addDataFileCopy.dbinc_key AND cdf.cdf_recid = addDataFileCopy.cdf_recid AND cdf.cdf_stamp = addDataFileCopy.cdf_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> local.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- 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 AND plugin_scn <> local.plugin_scn) THEN raise_application_error(-20097, 'Invalid create scn'); END IF; -- If site_key is null, update with this_site_key IF local.site_key IS NULL THEN UPDATE cdf SET site_key = this_site_key WHERE cdf.dbinc_key = addDataFileCopy.dbinc_key AND cdf.cdf_recid = addDataFileCopy.cdf_recid AND cdf.cdf_stamp = addDataFileCopy.cdf_stamp; END IF; END; END addDataFileCopy; FUNCTION beginDataFileCopyResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_cdf_recid INTO last_cdf_recid FROM node WHERE site_key = this_site_key; ELSE last_cdf_recid := sessionWaterMarks.high_cdf_recid; END IF; RETURN last_cdf_recid; END beginDataFileCopyResync; PROCEDURE checkDataFileCopy( cdf_recid IN NUMBER ,cdf_stamp IN NUMBER ,fname IN VARCHAR2 ,tag IN VARCHAR2 ,file# IN NUMBER ,create_scn IN NUMBER ,create_time IN DATE ,reset_scn IN NUMBER ,reset_time IN DATE ,incr_level IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,onl_fuzzy IN VARCHAR2 ,bck_fuzzy IN VARCHAR2 ,abs_fuzzy_scn IN NUMBER ,rcv_fuzzy_scn IN NUMBER ,rcv_fuzzy_time IN DATE ,blocks IN NUMBER ,block_size IN NUMBER ,min_offr_recid IN NUMBER ,completion_time IN DATE ,status IN VARCHAR2 ,controlfile_type IN VARCHAR2 DEFAULT NULL ,keep_options IN NUMBER DEFAULT 0 ,keep_until IN DATE DEFAULT NULL ,scanned IN VARCHAR2 DEFAULT 'NO' ,is_recovery_dest_file IN VARCHAR2 DEFAULT 'NO' ,rsr_recid IN number DEFAULT NULL ,rsr_stamp IN number DEFAULT NULL ,marked_corrupt IN number DEFAULT NULL ,foreign_dbid IN number DEFAULT 0 ,plugged_readonly IN varchar2 DEFAULT 'NO' ,plugin_scn IN number DEFAULT 0 ,plugin_reset_scn IN number DEFAULT 0 ,plugin_reset_time IN date DEFAULT NULL ) IS dbinc_key NUMBER; localrsr rsr%rowtype; 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 deb('checkBackupDatafileCopy - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- find the dbinc_key dbinc_key := checkIncarnation(reset_scn, reset_time); -- Find the owning rsr row and get its key. BEGIN SELECT rsr_key INTO localrsr.rsr_key FROM rsr WHERE rsr.dbinc_key = this_dbinc_key AND (rsr.site_key = this_site_key OR rsr.site_key is null AND this_site_key is null) AND rsr.rsr_stamp = checkDataFileCopy.rsr_stamp AND rsr.rsr_recid = checkDataFileCopy.rsr_recid; EXCEPTION WHEN no_data_found THEN -- no rsr record avaiable - ignore NULL; END; IF (file# = 0) THEN addControlFileCopy(cdf_recid, cdf_stamp, fname, tag, dbinc_key, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, completion_time, status, controlfile_type, keep_options, keep_until, is_recovery_dest_file, localrsr.rsr_key, blocks); ELSE addDataFileCopy(cdf_recid, cdf_stamp, fname, tag, file#, create_scn, dbinc_key, incr_level, ckp_scn, ckp_time, onl_fuzzy, bck_fuzzy, abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, completion_time, status, keep_options, keep_until, scanned, is_recovery_dest_file, localrsr.rsr_key, create_time, marked_corrupt, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time); END IF; END checkDataFileCopy; PROCEDURE endDataFileCopyResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_cdf_recid = last_cdf_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_cdf_recid := last_cdf_recid; last_cdf_recid := NULL; END endDataFileCopyResync; /*----------------------------* * Proxy Datafile resync * *----------------------------*/ PROCEDURE deleteDuplicateXCF(recid IN NUMBER, stamp IN NUMBER, device_type IN VARCHAR2, handle IN VARCHAR2) IS lhandle xcf.handle%TYPE; ldevice_type xcf.device_type%TYPE; BEGIN lhandle := handle; IF lhandle IS NULL OR ldevice_type IS NULL THEN BEGIN SELECT handle, device_type INTO lhandle, ldevice_type FROM xcf WHERE xcf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND xcf_recid = recid AND xcf_stamp = stamp; EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique_key is dbinc_key, recid and stamp RETURN; END; END IF; -- Delete old copies as the new copy should have overwritten it DELETE xcf WHERE xcf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND xcf.device_type = ldevice_type AND xcf.handle = lhandle AND ((tape_backups_shared = TRUE#) OR (this_site_key = nvl(xcf.site_key, this_site_key))) AND xcf.handle_hashkey = substr(ldevice_type, 1, 10) || substr(lhandle, 1, 10) || substr(lhandle, -10) AND NOT (xcf.xcf_recid = recid AND xcf.xcf_stamp = stamp); END deleteDuplicateXCF; PROCEDURE addProxyControlFile( dbinc_key IN NUMBER ,xcf_recid IN NUMBER ,xcf_stamp IN NUMBER ,tag IN VARCHAR2 ,ckp_scn IN NUMBER ,ckp_time IN DATE ,create_time IN DATE ,min_offr_recid IN NUMBER ,block_size IN NUMBER ,device_type IN VARCHAR2 ,handle IN VARCHAR2 ,comments IN VARCHAR2 ,media IN VARCHAR2 ,media_pool IN NUMBER ,start_time IN VARCHAR2 ,completion_time IN DATE ,status IN VARCHAR2 ,controlfile_type IN VARCHAR2 ,keep_options IN NUMBER ,keep_until IN DATE ,rsr_key IN NUMBER ,blocks IN NUMBER ) IS local xcf%rowtype; BEGIN BEGIN IF (status <> 'D') THEN INSERT INTO xcf(xcf_key, dbinc_key, xcf_recid, xcf_stamp, tag, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, device_type, handle, handle_hashkey, comments, media, media_pool, start_time, completion_time, status, controlfile_type, keep_options, keep_until, rsr_key, site_key) VALUES (rman_seq.nextval, dbinc_key, xcf_recid, xcf_stamp, tag, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, device_type, handle, substr(device_type,1,10)||substr(handle,1,10)||substr(handle,-10), comments, media, media_pool, start_time, completion_time, status, controlfile_type, keep_options, keep_until, rsr_key, this_site_key); deleteDuplicateXCF(xcf_recid, xcf_stamp, device_type, handle); END IF; EXCEPTION WHEN dup_val_on_index THEN deb('addProxyControlFile - Inside dup_val_on_index exception'); -- this proxy controlfile backup already exists in the recovery catalog SELECT * INTO local FROM xcf WHERE xcf.dbinc_key = addProxyControlFile.dbinc_key AND xcf.xcf_recid = addProxyControlFile.xcf_recid AND xcf.xcf_stamp = addProxyControlFile.xcf_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> local.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- check the ckp_scn IF (ckp_scn <> local.ckp_scn) THEN raise_application_error(-20095, 'Invalid ckp_scn'); END IF; -- If site_key is null, update with this_site_key IF local.site_key IS NULL THEN UPDATE xcf SET site_key = this_site_key WHERE xcf.dbinc_key = addProxyControlFile.dbinc_key AND xcf.xcf_recid = addProxyControlFile.xcf_recid AND xcf.xcf_stamp = addProxyControlFile.xcf_stamp; END IF; END; END addProxyControlFile; PROCEDURE deleteDuplicateXDF(recid IN NUMBER, stamp IN NUMBER, device_type IN VARCHAR2, handle IN VARCHAR2) IS lhandle xdf.handle%TYPE; ldevice_type xdf.device_type%TYPE; BEGIN lhandle := handle; IF lhandle IS NULL OR ldevice_type IS NULL THEN BEGIN SELECT handle, device_type INTO lhandle, ldevice_type FROM xdf WHERE xdf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND xdf_recid = recid AND xdf_stamp = stamp; EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique_key is dbinc_key, recid and stamp RETURN; END; END IF; -- Delete old copies as the new copy should have overwritten it DELETE xdf WHERE xdf.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND xdf.device_type = ldevice_type AND xdf.handle = lhandle AND ((tape_backups_shared = TRUE#) OR (this_site_key = nvl(xdf.site_key, this_site_key))) AND xdf.handle_hashkey = substr(ldevice_type, 1, 10) || substr(lhandle, 1, 10) || substr(lhandle, -10) AND NOT (xdf.xdf_recid = recid AND xdf.xdf_stamp = stamp); END deleteDuplicateXDF; PROCEDURE addProxyDataFile( dbinc_key IN NUMBER ,xdf_recid IN NUMBER ,xdf_stamp IN NUMBER ,tag IN VARCHAR2 ,file# IN NUMBER ,create_scn IN NUMBER ,incr_level IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,onl_fuzzy IN VARCHAR2 ,bck_fuzzy IN VARCHAR2 ,abs_fuzzy_scn IN NUMBER ,rcv_fuzzy_scn IN NUMBER ,rcv_fuzzy_time IN DATE ,blocks IN NUMBER ,block_size IN NUMBER ,device_type IN VARCHAR2 ,handle IN VARCHAR2 ,comments IN VARCHAR2 ,media IN VARCHAR2 ,media_pool IN NUMBER ,start_time IN VARCHAR2 ,completion_time IN DATE ,status IN VARCHAR2 ,keep_options IN NUMBER DEFAULT 0 ,keep_until IN DATE DEFAULT NULL ,rsr_key IN NUMBER ,create_time IN DATE ,foreign_dbid IN number ,plugged_readonly IN varchar2 ,plugin_scn IN number ,plugin_reset_scn IN number ,plugin_reset_time IN date ) IS local xdf%rowtype; BEGIN BEGIN IF (status <> 'D') THEN INSERT INTO xdf(xdf_key, dbinc_key, xdf_recid, xdf_stamp, file#, create_scn, tag, incr_level, ckp_scn, ckp_time, onl_fuzzy, bck_fuzzy, abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, device_type, handle, handle_hashkey, comments, media, media_pool, start_time, completion_time, status, keep_options, keep_until, rsr_key, site_key, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time) VALUES (rman_seq.nextval, dbinc_key, xdf_recid, xdf_stamp, file#, create_scn, tag, incr_level, ckp_scn, ckp_time, decode(onl_fuzzy,'YES','Y','NO','N'), decode(bck_fuzzy,'YES','Y','NO','N'), abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, device_type, handle, substr(device_type,1,10)||substr(handle,1,10)||substr(handle,-10), comments, media, media_pool, start_time, completion_time, status, keep_options, keep_until, rsr_key, this_site_key, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time); deleteDuplicateXDF(xdf_recid, xdf_stamp, device_type, handle); END IF; EXCEPTION WHEN dup_val_on_index THEN deb('addProxyDatafile - Inside dup_val_on_index exception'); SELECT * INTO local FROM xdf WHERE xdf.dbinc_key = addProxyDataFile.dbinc_key AND xdf.xdf_recid = addProxyDataFile.xdf_recid AND xdf.xdf_stamp = addProxyDataFile.xdf_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> local.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- 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 AND plugin_scn <> local.plugin_scn) THEN raise_application_error(-20097, 'Invalid create scn'); END IF; -- If site_key is null, update with this_site_key IF local.site_key IS NULL THEN UPDATE xdf SET site_key = this_site_key WHERE xdf.dbinc_key = addProxyDataFile.dbinc_key AND xdf.xdf_recid = addProxyDataFile.xdf_recid AND xdf.xdf_stamp = addProxyDataFile.xdf_stamp; END IF; END; END addProxyDataFile; PROCEDURE deleteDuplicateXAL(recid IN NUMBER, stamp IN NUMBER, device_type IN VARCHAR2, handle IN VARCHAR2) IS lhandle xal.handle%TYPE; ldevice_type xal.device_type%TYPE; BEGIN lhandle := handle; IF lhandle IS NULL OR ldevice_type IS NULL THEN BEGIN SELECT handle, device_type INTO lhandle, ldevice_type FROM xal WHERE xal.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND xal_recid = recid AND xal_stamp = stamp; EXCEPTION WHEN no_data_found THEN RETURN; WHEN too_many_rows THEN -- unique_key is dbinc_key, recid and stamp RETURN; END; END IF; -- Delete old copies as the new copy should have overwritten it DELETE xal WHERE xal.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND xal.device_type = ldevice_type AND xal.handle = lhandle AND ((tape_backups_shared = TRUE#) OR (this_site_key = nvl(xal.site_key, this_site_key))) AND xal.handle_hashkey = substr(ldevice_type, 1, 10) || substr(lhandle, 1, 10) || substr(lhandle, -10) AND NOT (xal.xal_recid = recid AND xal.xal_stamp = stamp); END deleteDuplicateXAL; PROCEDURE addProxyArchivedLog( dbinc_key IN NUMBER ,xal_recid IN NUMBER ,xal_stamp IN NUMBER ,tag IN VARCHAR2 ,thread# IN NUMBER ,sequence# IN NUMBER ,resetlogs_change# IN NUMBER ,resetlogs_time IN DATE ,first_change# IN NUMBER ,first_time IN DATE ,next_change# IN NUMBER ,next_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 ,rsr_key IN NUMBER ,terminal IN VARCHAR2 default 'NO' ,keep_until IN DATE default NULL ,keep_options IN NUMBER default 0 ) IS local xal%rowtype; BEGIN BEGIN IF (status <> 'D') THEN INSERT INTO xal(xal_key, dbinc_key, xal_recid, xal_stamp, tag, thread#, sequence#, low_scn, low_time, next_scn, next_time, blocks, block_size, device_type, handle, handle_hashkey, comments, media, media_pool, start_time, completion_time, status, rsr_key, terminal, keep_until, keep_options, site_key) VALUES (rman_seq.nextval, dbinc_key, xal_recid, xal_stamp, tag, thread#, sequence#, first_change#, first_time, next_change#, next_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, rsr_key, terminal, keep_until, keep_options, this_site_key); deleteDuplicateXAL(xal_recid, xal_stamp, device_type, handle); END IF; EXCEPTION WHEN dup_val_on_index THEN deb('addProxyArchivedLog - Inside dup_val_on_index exception'); SELECT * INTO local FROM xal WHERE xal.dbinc_key = addProxyArchivedLog.dbinc_key AND xal.xal_recid = addProxyArchivedLog.xal_recid AND xal.xal_stamp = addProxyArchivedLog.xal_stamp; -- change stamp and resync record, if client is site aware... IF client_site_aware AND this_site_key <> local.site_key THEN raise_application_error(-20081, 'change stamp for the record'); END IF; -- check the low_scn IF (first_change# <> local.low_scn) THEN raise_application_error(-20098, 'Invalid low scn'); END IF; -- If site_key is null, update with this_site_key IF local.site_key IS NULL THEN UPDATE xal SET site_key = this_site_key WHERE xal.dbinc_key = addProxyArchivedLog.dbinc_key AND xal.xal_recid = addProxyArchivedLog.xal_recid AND xal.xal_stamp = addProxyArchivedLog.xal_stamp; END IF; END; IF (keep_options > 0) THEN updateRestorePoint(first_change#, next_change#); END IF; END addProxyArchivedLog; -- Note that this function will be used to start the resync of both proxy -- datafiles and archived logs, because they both share the same recids. FUNCTION beginProxyResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_pc_recid INTO last_xdf_recid FROM node WHERE site_key = this_site_key; ELSE last_xdf_recid := sessionWaterMarks.high_pc_recid; END IF; last_xal_recid := last_xdf_recid; RETURN last_xdf_recid; END beginProxyResync; PROCEDURE checkProxyDataFile( xdf_recid IN NUMBER ,xdf_stamp IN NUMBER ,tag IN VARCHAR2 ,file# IN NUMBER ,create_scn IN NUMBER ,create_time IN DATE ,reset_scn IN NUMBER ,reset_time IN DATE ,incr_level IN NUMBER ,ckp_scn IN NUMBER ,ckp_time IN DATE ,onl_fuzzy IN VARCHAR2 ,bck_fuzzy IN VARCHAR2 ,abs_fuzzy_scn IN NUMBER ,rcv_fuzzy_scn IN NUMBER ,rcv_fuzzy_time IN DATE ,blocks IN NUMBER ,block_size IN NUMBER ,min_offr_recid IN NUMBER ,device_type IN VARCHAR2 ,handle IN VARCHAR2 ,comments IN VARCHAR2 ,media IN VARCHAR2 ,media_pool IN NUMBER ,start_time IN DATE ,completion_time IN DATE ,status IN VARCHAR2 ,controlfile_type IN VARCHAR2 DEFAULT NULL ,keep_options IN NUMBER DEFAULT 0 ,keep_until IN DATE DEFAULT NULL ,rsr_recid IN NUMBER DEFAULT NULL ,rsr_stamp IN NUMBER DEFAULT NULL ,foreign_dbid IN number DEFAULT 0 ,plugged_readonly IN varchar2 DEFAULT 'NO' ,plugin_scn IN number DEFAULT 0 ,plugin_reset_scn IN number DEFAULT 0 ,plugin_reset_time IN date DEFAULT NULL ) IS dbinc_key NUMBER; localrsr rsr%rowtype; 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 deb('checkProxyDatafile - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- Find the dbinc_key that this proxy backup belongs to. It is not -- necessarily this_dbinc_key. dbinc_key := checkIncarnation(reset_scn, reset_time); -- Find the owning rsr row and get its key. BEGIN SELECT rsr_key INTO localrsr.rsr_key FROM rsr WHERE rsr.dbinc_key = this_dbinc_key AND (rsr.site_key = this_site_key OR rsr.site_key is null AND this_site_key is null) AND rsr.rsr_stamp = checkProxyDataFile.rsr_stamp AND rsr.rsr_recid = checkProxyDataFile.rsr_recid; EXCEPTION WHEN no_data_found THEN -- No rsr record available - ignore. NULL; END; IF (file# = 0) THEN addProxyControlFile(dbinc_key, xdf_recid, xdf_stamp, tag, ckp_scn, ckp_time, create_time, min_offr_recid, block_size, device_type, handle, comments, media, media_pool, start_time, completion_time, status, controlfile_type, keep_options, keep_until, localrsr.rsr_key, blocks); ELSE addProxyDataFile(dbinc_key, xdf_recid, xdf_stamp, tag, file#, create_scn, incr_level, ckp_scn, ckp_time, onl_fuzzy, bck_fuzzy, abs_fuzzy_scn, rcv_fuzzy_scn, rcv_fuzzy_time, blocks, block_size, device_type, handle, comments, media, media_pool, start_time, completion_time, status, keep_options, keep_until, localrsr.rsr_key, create_time, foreign_dbid, plugged_readonly, plugin_scn, plugin_reset_scn, plugin_reset_time); END IF; END checkProxyDataFile; PROCEDURE checkProxyArchivedLog( xal_recid IN NUMBER ,xal_stamp IN NUMBER ,tag IN VARCHAR2 ,thread# IN NUMBER ,sequence# IN NUMBER ,resetlogs_change# IN NUMBER ,resetlogs_time IN DATE ,first_change# IN NUMBER ,first_time IN DATE ,next_change# IN NUMBER ,next_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 DATE ,completion_time IN DATE ,status IN VARCHAR2 ,rsr_recid IN NUMBER ,rsr_stamp IN NUMBER ,terminal IN VARCHAR2 default 'NO' ,keep_until IN DATE default NULL ,keep_options IN NUMBER default 0 ) IS dbinc_key NUMBER; localrsr rsr%rowtype; BEGIN IF (last_xal_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (xal_recid < last_xal_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_xal_recid := xal_recid; IF (xal_stamp < kccdivts) THEN deb('checkProxyArchivedLog - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- find the dbinc_key that this proxy backup belongs to. It is not -- necessarily this_dbinc_key. dbinc_key := checkIncarnation(resetlogs_change#, resetlogs_time); -- Find the owning rsr row and get its key. BEGIN SELECT rsr_key INTO localrsr.rsr_key FROM rsr WHERE rsr.dbinc_key = this_dbinc_key AND (rsr.site_key = this_site_key OR rsr.site_key is null AND this_site_key is null) AND rsr.rsr_stamp = checkProxyArchivedLog.rsr_stamp AND rsr.rsr_recid = checkProxyArchivedLog.rsr_recid; EXCEPTION WHEN no_data_found THEN -- No rsr record available - ignore. NULL; END; addProxyArchivedLog(dbinc_key, xal_recid, xal_stamp, tag, thread#, sequence#, resetlogs_change#, resetlogs_time, first_change#, first_time, next_change#, next_time, blocks, block_size, device_type, handle, comments, media, media_pool, start_time, completion_time, status, localrsr.rsr_key, terminal, keep_until, keep_options); END checkProxyArchivedLog; PROCEDURE endProxyResync IS last_pc_recid number := greatest(nvl(last_xdf_recid,0), nvl(last_xal_recid,0)); BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_pc_recid = last_pc_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_pc_recid := last_pc_recid; last_xdf_recid := NULL; last_xal_recid := NULL; END endProxyResync; /*----------------------------* * Corrupt Block resync * *----------------------------*/ FUNCTION beginBackupCorruptionResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_bcb_recid INTO last_bcb_recid FROM node WHERE site_key = this_site_key; ELSE last_bcb_recid := sessionWaterMarks.high_bcb_recid; END IF; RETURN last_bcb_recid; END beginBackupCorruptionResync; PROCEDURE checkBackupCorruption( bcb_recid IN NUMBER ,bcb_stamp IN NUMBER ,set_stamp IN NUMBER ,set_count IN NUMBER ,piece# IN NUMBER ,file# IN NUMBER ,block# IN NUMBER ,blocks IN NUMBER ,corrupt_scn IN NUMBER ,marked_corrupt IN VARCHAR2 ,corruption_type IN VARCHAR2 ) IS local bcb%rowtype; BEGIN IF (last_bcb_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (bcb_recid < last_bcb_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (bcb_recid > last_bcb_recid + 1) THEN -- there is gap in deleted object records -- not sure what we should do here NULL; END IF; last_bcb_recid := bcb_recid; IF (bcb_stamp < kccdivts) THEN deb('checkBackupCorruption - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- find the bdf_key to which this corrupt block belongs BEGIN SELECT bdf_key INTO local.bdf_key FROM bdf, bs WHERE bdf.bs_key = bs.bs_key AND bs.db_key = this_db_key AND bs.set_stamp = checkBackupCorruption.set_stamp AND bs.set_count = checkBackupCorruption.set_count AND bdf.file# = checkBackupCorruption.file#; EXCEPTION WHEN no_data_found THEN -- if bdf_key is not found, ignore this corrupt block RETURN; END; BEGIN INSERT INTO bcb (bdf_key, bcb_recid, bcb_stamp, piece#, block#, blocks, corrupt_scn, marked_corrupt, corruption_type) VALUES (local.bdf_key, bcb_recid, bcb_stamp, piece#, block#, blocks, corrupt_scn, decode(marked_corrupt,'YES','Y','NO','N'), corruption_type); EXCEPTION WHEN dup_val_on_index THEN -- the corrupt block is already in rcvcat, so do nothing RETURN; END; END checkBackupCorruption; PROCEDURE endBackupCorruptionResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_bcb_recid = last_bcb_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_bcb_recid := last_bcb_recid; last_bcb_recid := NULL; END endBackupCorruptionResync; FUNCTION beginCopyCorruptionResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_ccb_recid INTO last_ccb_recid FROM node WHERE site_key = this_site_key; ELSE last_ccb_recid := sessionWaterMarks.high_ccb_recid; END IF; RETURN last_ccb_recid; END beginCopyCorruptionResync; PROCEDURE checkCopyCorruption( ccb_recid IN NUMBER ,ccb_stamp IN NUMBER ,cdf_recid IN NUMBER ,cdf_stamp IN NUMBER ,file# IN NUMBER ,block# IN NUMBER ,blocks IN NUMBER ,corrupt_scn IN NUMBER ,marked_corrupt IN VARCHAR2 ,corruption_type IN VARCHAR2 ) IS local ccb%rowtype; BEGIN IF (last_ccb_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (ccb_recid < last_ccb_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (ccb_recid > last_ccb_recid + 1) THEN -- there is gap in deleted object records -- not sure what we should do here NULL; END IF; last_ccb_recid := ccb_recid; IF (ccb_stamp < kccdivts) THEN deb('checkCopyCorruption - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- find the cdf_key to which this corrupt block belongs BEGIN SELECT cdf_key INTO local.cdf_key FROM cdf WHERE cdf.dbinc_key = this_dbinc_key AND cdf.cdf_recid = checkCopyCorruption.cdf_recid AND cdf.cdf_stamp = checkCopyCorruption.cdf_stamp AND cdf.file# = checkCopyCorruption.file#; EXCEPTION WHEN no_data_found THEN -- if cdf_key is not found, ignore this corrupt block RETURN; END; BEGIN INSERT INTO ccb (cdf_key, ccb_recid, ccb_stamp, block#, blocks, corrupt_scn, marked_corrupt, corruption_type) VALUES (local.cdf_key, ccb_recid, ccb_stamp, block#, blocks, corrupt_scn, decode(marked_corrupt,'YES','Y','NO','N'), corruption_type); EXCEPTION WHEN dup_val_on_index THEN -- the corrupt block is already in rcvcat, so do nothing RETURN; END; END checkCopyCorruption; PROCEDURE endCopyCorruptionResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_ccb_recid = last_ccb_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_ccb_recid := last_ccb_recid; last_ccb_recid := NULL; END endCopyCorruptionResync; FUNCTION beginBlockCorruptionResync( low_bcr_recid IN number) RETURN NUMBER IS old_bcr_recid number; BEGIN checkResync; SELECT high_bcr_recid, low_bcr_recid INTO last_bcr_recid, old_bcr_recid FROM node WHERE site_key = this_site_key; -- If oldest recid doesn't match, then purge all the catalog entries. -- This is in order to avoid duplicate block ranges in bcr table for -- the records that are reused. -- -- NOTE!!! -- This has to be investigated later to check if it is possible to -- keep the block corruption ranges that are not known to controlfile -- IF (old_bcr_recid != low_bcr_recid) THEN DELETE bcr WHERE site_key = this_site_key AND bcr_recid < low_bcr_recid; UPDATE node SET low_bcr_recid = low_bcr_recid WHERE site_key = this_site_key; END IF; RETURN last_bcr_recid; END beginBlockCorruptionResync; PROCEDURE checkBlockCorruption( bcr_recid IN NUMBER ,bcr_stamp IN NUMBER ,file# IN NUMBER ,create_scn IN NUMBER ,create_time IN DATE ,block# IN NUMBER ,blocks IN NUMBER ,corrupt_scn IN NUMBER ,corruption_type IN VARCHAR2 ) IS local df%rowtype; BEGIN IF (last_bcr_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (bcr_recid < last_bcr_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (bcr_recid > last_bcr_recid + 1) THEN -- there is gap in deleted object records -- not sure what we should do here NULL; END IF; last_bcr_recid := bcr_recid; -- find the df_key to which this corrupt block belongs BEGIN SELECT distinct df.df_key INTO local.df_key FROM df, site_dfatt WHERE df.df_key = site_dfatt.df_key AND site_dfatt.site_key = this_site_key AND df.file# = checkBlockCorruption.file# AND df.create_scn = checkBlockCorruption.create_scn AND df.create_time = checkBlockCorruption.create_time; EXCEPTION WHEN no_data_found THEN -- if df_key is not found, ignore this corrupt block deb('checkBlockCorruption - no df_key found'); RETURN; END; deb('checkBlockCorruption - df_key=' || local.df_key); BEGIN INSERT INTO bcr (bcr_recid, bcr_stamp, df_key, site_key, block#, blocks, corrupt_scn, corruption_type) VALUES (bcr_recid, bcr_stamp, local.df_key, this_site_key, block#, blocks, corrupt_scn, corruption_type); EXCEPTION WHEN dup_val_on_index THEN -- the corrupt block is already in rcvcat, so do nothing RETURN; END; END checkBlockCorruption; PROCEDURE endBlockCorruptionResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_bcr_recid = last_bcr_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_bcr_recid := last_bcr_recid; last_bcr_recid := NULL; END endBlockCorruptionResync; /*----------------------------* * Deleted Object resync * *----------------------------*/ FUNCTION beginDeletedObjectResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_do_recid INTO last_do_recid FROM node WHERE site_key = this_site_key; ELSE last_do_recid := sessionWaterMarks.high_do_recid; END IF; RETURN last_do_recid; END beginDeletedObjectResync; PROCEDURE checkDeletedObject( do_recid IN NUMBER ,do_stamp IN NUMBER ,object_type IN VARCHAR2 ,object_recid IN NUMBER ,object_stamp IN NUMBER ,object_data IN NUMBER DEFAULT NULL ,object_fname IN VARCHAR2 DEFAULT NULL ,object_create_scn IN NUMBER DEFAULT NULL ,set_stamp IN NUMBER DEFAULT NULL ,set_count IN NUMBER DEFAULT NULL) IS local bp%rowtype; new_status VARCHAR2(1); rc boolean; keep_options number := NULL; keep_until date := NULL; BEGIN IF (last_do_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (do_recid < last_do_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (do_recid > last_do_recid + 1) THEN -- there is gap in deleted object records -- not sure what we should do here NULL; END IF; last_do_recid := do_recid; IF (do_stamp < kccdivts) THEN deb('checkDeletedObject - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- change/delete the objects. If the update fails to update a row, -- we do not care; it just means that the object has been deleted -- from or never inserted into rcvcat. IF (object_type like 'BACKUP SET%') THEN IF (object_type = 'BACKUP SET KEEP UNTIL') THEN keep_until := stamp2date(object_data); ELSIF (object_type = 'BACKUP SET KEEP OPTIONS') THEN keep_options := object_data; ELSE raise_application_error(-20999, 'Internal error in checkDeletedObject(): bad object_type '|| object_type); END IF; changeBackupSet(object_recid, object_stamp, keep_options, keep_until); END IF; IF (object_type like 'BACKUP PIECE%') THEN IF (object_type = 'BACKUP PIECE') THEN new_status := 'D'; ELSIF (object_type = 'BACKUP PIECE AVAILABLE') THEN new_status := 'A'; ELSIF (object_type = 'BACKUP PIECE EXPIRED') THEN new_status := 'X'; ELSIF (object_type = 'BACKUP PIECE UNAVAILABLE') THEN new_status := 'U'; ELSE raise_application_error(-20999, 'Internal error in checkDeletedObject(): bad object_type '|| object_type); END IF; changeBackupPiece(object_recid, object_stamp, new_status, set_stamp, set_count); END IF; IF (object_type like 'DATAFILE COPY%') THEN IF (object_type = 'DATAFILE COPY') THEN new_status := 'D'; ELSIF (object_type = 'DATAFILE COPY AVAILABLE') THEN new_status := 'A'; ELSIF (object_type = 'DATAFILE COPY EXPIRED') THEN new_status := 'X'; ELSIF (object_type = 'DATAFILE COPY UNAVAILABLE') THEN new_status := 'U'; ELSIF (object_type = 'DATAFILE COPY KEEP UNTIL') THEN new_status := NULL; keep_until := stamp2date(object_data); ELSIF (object_type = 'DATAFILE COPY KEEP OPTIONS') THEN new_status := NULL; keep_options := object_data; ELSE raise_application_error(-20999, 'Internal error in checkDeletedObject(): bad object_type '|| object_type); END IF; changeDatafileCopy(object_recid, object_stamp, new_status, keep_options, keep_until); END IF; IF (object_type like 'ARCHIVED LOG%') THEN IF (object_type = 'ARCHIVED LOG') THEN new_status := 'D'; ELSIF (object_type = 'ARCHIVED LOG AVAILABLE') THEN new_status := 'A'; ELSIF (object_type = 'ARCHIVED LOG EXPIRED') THEN new_status := 'X'; ELSIF (object_type = 'ARCHIVED LOG UNAVAILABLE') THEN new_status := 'U'; ELSE raise_application_error(-20999, 'Internal error in checkDeletedObject(): bad object_type '|| object_type); END IF; changeArchivedLog(object_recid, object_stamp, new_status); END IF; IF (object_type like 'PROXY COPY%') THEN IF (object_type = 'PROXY COPY') THEN new_status := 'D'; ELSIF (object_type = 'PROXY COPY AVAILABLE') THEN new_status := 'A'; ELSIF (object_type = 'PROXY COPY EXPIRED') THEN new_status := 'X'; ELSIF (object_type = 'PROXY COPY UNAVAILABLE') THEN new_status := 'U'; ELSIF (object_type = 'PROXY COPY KEEP UNTIL') THEN new_status := NULL; keep_until := stamp2date(object_data); ELSIF (object_type = 'PROXY COPY KEEP OPTIONS') THEN new_status := NULL; keep_options := object_data; ELSE raise_application_error(-20999, 'Internal error in checkDeletedObject(): bad object_type '|| object_type); END IF; changeProxyCopy(object_recid, object_stamp, new_status, keep_options, keep_until); END IF; IF (object_type = 'DATAFILE RENAME ON RESTORE') THEN deb('checkDeletedObject - renaming file#='||object_data||' to '|| object_fname); -- We should rename the datafile. We will do that by updating -- the current dfatt record. Note that this is ignored during -- full resync - in that case the filename is already resynced. -- Incase we could not find the df_key, ignore the file rename as -- the deleted object belongs to dropped file. DECLARE local_df_key NUMBER; BEGIN SELECT df_key INTO local_df_key FROM df WHERE dbinc_key = this_dbinc_key AND df.file# = object_data AND df.create_scn = object_create_scn; UPDATE site_dfatt SET fname = object_fname WHERE site_key = this_site_key AND df_key = local_df_key; IF (NOT SQL%FOUND) THEN deb('checkDeletedObject - doing an insert'); INSERT INTO site_dfatt (fname, df_key, site_key) VALUES (object_fname, local_df_key, this_site_key); END IF; EXCEPTION WHEN no_data_found THEN NULL; END; END IF; IF (object_type = 'PLUGGED READONLY RENAME') THEN deb('In checkDeletedObject, renaming plugged readonly file#='|| object_data||' to ' ||object_fname); -- This is similar to renaming a datafile but for a plugged readonly -- file. So, plugin scn should be compared. -- Incase we could not find the df_key, ignore the file rename as -- the deleted object belongs to dropped file. DECLARE local_df_key NUMBER; BEGIN SELECT df_key INTO local_df_key FROM df WHERE dbinc_key = this_dbinc_key AND df.file# = object_data AND df.plugin_scn = object_create_scn; UPDATE site_dfatt SET fname = object_fname WHERE site_key = this_site_key AND df_key = local_df_key; IF (NOT SQL%FOUND) THEN INSERT INTO site_dfatt (fname, df_key, site_key) VALUES (object_fname, local_df_key, this_site_key); END IF; EXCEPTION WHEN no_data_found THEN NULL; END; END IF; IF (object_type = 'TEMPFILE RENAME') THEN deb('checkDeletedObject - renaming temp file#='||object_data||' to'|| object_fname); -- We should rename the tempfile. We will do that by updating -- the current site_tfatt record. Note that this is ignored during -- full resync - in that case the filename is already resynced. -- Incase we could not find the tf_key, ignore the file rename as -- the deleted object belongs to dropped file. DECLARE local_tf_key NUMBER; BEGIN SELECT tf_key INTO local_tf_key FROM tf WHERE dbinc_key = this_dbinc_key AND tf.file# = object_data AND tf.create_scn = object_create_scn; UPDATE site_tfatt SET fname = object_fname WHERE site_key = this_site_key AND tf_key = local_tf_key; IF (NOT SQL%FOUND) THEN INSERT INTO site_tfatt (fname, tf_key, site_key) VALUES (object_fname, local_tf_key, this_site_key); END IF; EXCEPTION WHEN no_data_found THEN NULL; END; END IF; IF (object_type = 'DATABASE BLOCK CORRUPTION') THEN DELETE bcr WHERE site_key = this_site_key AND bcr_recid = object_recid AND bcr_stamp = object_stamp; END IF; IF (object_type = 'RESTORE POINT') THEN DELETE nrsp WHERE site_key = this_site_key AND nrsp_recid = object_recid AND nrsp_stamp = object_stamp; END IF; END checkDeletedObject; PROCEDURE endDeletedObjectResync IS BEGIN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_do_recid = last_do_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_do_recid := last_do_recid; last_do_recid := NULL; END endDeletedObjectResync; /*----------------------------* * RMAN Output resync * *----------------------------*/ FUNCTION beginRmanOutputResync(start_timestamp IN NUMBER) RETURN NUMBER IS BEGIN deb('beginRmanOutputResync - input instance start time='||start_timestamp); checkResync; -- We must have a node entry by now... other let it signal error SELECT inst_startup_stamp, high_rout_stamp into last_inst_startup_stamp, last_rout_stamp from node where node.db_key = this_db_key and ((this_db_unique_name is null and node.db_unique_name is null) or node.db_unique_name = this_db_unique_name); deb('beginRmanOutputResync - last_inst_startup_stamp='|| last_inst_startup_stamp||',last_rout_stamp='||last_rout_stamp); -- If instance has been re-start for this node, get all rows from -- v$rman_output, Otherwise get from last stamp. IF (last_inst_startup_stamp <> start_timestamp) THEN last_rout_stamp := sessionWaterMarks.high_rout_stamp; last_inst_startup_stamp := start_timestamp; END IF; RETURN last_rout_stamp; END beginRmanOutputResync; PROCEDURE checkRmanOutput( recid IN NUMBER ,stamp IN NUMBER ,session_recid IN NUMBER ,session_stamp IN NUMBER ,rman_status_recid IN NUMBER ,rman_status_stamp IN NUMBER ,output IN VARCHAR2) IS BEGIN deb('checkRmanOutput', RCVCAT_LEVEL_HI); -- advance last_rout_stamp if current record stamp is greater IF (last_rout_stamp < stamp) THEN last_rout_stamp := stamp; END IF; IF lrman_status_recid = rman_status_recid AND lrman_status_stamp = rman_status_stamp THEN goto rsr_key_known; END IF; deb('checkRmanOutput - Find rsr_ key'); BEGIN select rsr_key into lrsr_key from rsr where rsr.dbinc_key = this_dbinc_key and ((rsr.site_key = this_site_key) or (rsr.site_key is null AND this_site_key is null)) and rsr.rsr_recid = rman_status_recid and rsr.rsr_stamp = rman_status_stamp; EXCEPTION WHEN no_data_found THEN -- no command record avaiable - ignore this record deb('checkRmanOutput - ignoring following RMAN output row'); RETURN; END; <> IF lsession_recid = session_recid AND lsession_stamp = session_stamp THEN goto rout_skey_known; END IF; deb('checkRmanOutput - Find session key'); BEGIN -- when startup force is done within a single RMAN client session, -- multiple parent rsr_keys will be created by client since the client -- knows about previous server session also. This causes following query -- to return multiple rows in such cases, so we associate the o/p to the -- latest rman server session. select max(rsr_key) into lrout_skey from rsr, dbinc where rsr.dbinc_key = dbinc.dbinc_key and dbinc.db_key = this_db_key and (rsr.site_key = this_site_key or rsr.site_key is null AND this_site_key is null) and rsr.rsr_srecid = session_recid and rsr.rsr_sstamp = session_stamp and rsr.rsr_type = 'SESSION'; EXCEPTION WHEN no_data_found THEN -- no command record avaiable - ignore this record deb('checkRmanOutput - ignoring following RMAN output row, cause2'); RETURN; WHEN others THEN deb('checkRmanOutput - signal error for RMAN output row'); RAISE; END; <> IF lrout_skey is null THEN -- no command record avaiable - ignore this record deb('checkRmanOutput: skey not found, ignoring RMAN output row'); RETURN; END IF; BEGIN -- 5906892 trim column output to 130 chars insert into rout (db_key, rsr_key, rout_skey, rout_recid, rout_stamp, rout_text) values (this_db_key, lrsr_key, lrout_skey, recid, stamp, substrb(output, 1, krbmror_llength_bytes)); EXCEPTION WHEN dup_val_on_index THEN update rout set rout_text = substrb(output, 1, krbmror_llength_bytes) where db_key = this_db_key and rsr_key = lrsr_key and rout_skey = lrout_skey and rout_stamp = stamp and rout_recid = recid; END; lrman_status_recid := rman_status_recid; lrman_status_stamp := rman_status_stamp; lsession_recid := session_recid; lsession_stamp := session_stamp; END checkRmanOutput; PROCEDURE endRmanOutputResync IS BEGIN UPDATE node SET high_rout_stamp = last_rout_stamp, inst_startup_stamp = last_inst_startup_stamp WHERE node.db_key = this_db_key and ((this_db_unique_name is null and node.db_unique_name is null) or node.db_unique_name = this_db_unique_name); sessionWaterMarks.high_rout_stamp := last_rout_stamp; last_rout_stamp := NULL; last_inst_startup_stamp := NULL; lrsr_key := NULL; lrout_skey := NULL; lsession_recid := NULL; lsession_stamp := NULL; lrman_status_recid := NULL; lrman_status_stamp := NULL; END endRmanOutputResync; /*----------------------------* * RMAN Status resync * *----------------------------*/ FUNCTION beginRmanStatusResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_rsr_recid INTO last_rsr_recid FROM node WHERE site_key = this_site_key; ELSE last_rsr_recid := sessionWaterMarks.high_rsr_recid; END IF; RETURN last_rsr_recid; END beginRmanStatusResync; PROCEDURE checkRmanStatus( recid IN NUMBER ,stamp IN NUMBER ,parent_recid IN NUMBER ,parent_stamp IN NUMBER ,row_level IN NUMBER ,row_type IN VARCHAR2 ,command_id IN VARCHAR2 ,operation IN VARCHAR2 ,status IN VARCHAR2 ,mbytes_processed IN NUMBER ,start_time IN DATE ,end_time IN DATE ,ibytes IN NUMBER default null ,obytes IN NUMBER default null ,optimized IN VARCHAR2 default null ,otype IN VARCHAR2 default null ,session_recid IN NUMBER default null ,session_stamp IN NUMBER default null ,odevtype IN VARCHAR2 default null ,osb_allocated IN VARCHAR2 default 'NO') IS parent rsr%rowtype; BEGIN IF (last_rsr_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (recid < last_rsr_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (stamp < kccdivts) THEN RETURN; -- obsolete record from a backup controlfile END IF; parent.rsr_pkey := NULL; parent.rsr_l0key := NULL; -- Find the key of the parent (if any). The recid of the level 0 -- (RMAN session) is taken from the parent: if the parent is level 0, then -- we take its key, otherwise key of the level 0 row is in rsr_l0key. IF (checkRmanStatus.row_level > 0) THEN deb('checkRmanStatus - row_level='||to_char(checkRmanStatus.row_level)); BEGIN SELECT rsr_key, decode(rsr_level, 0, rsr_key, rsr_l0key) INTO parent.rsr_key, parent.rsr_l0key FROM rsr WHERE rsr.dbinc_key = this_dbinc_key AND (rsr.site_key = this_site_key OR rsr.site_key is null AND this_site_key is null) AND rsr.rsr_stamp = checkRmanStatus.parent_stamp AND rsr.rsr_recid = checkRmanStatus.parent_recid; EXCEPTION WHEN no_data_found THEN -- no parent record avaiable - ignore this record deb('checkRmanStatus - ignoring this record'); RETURN; END; END IF; BEGIN deb('checkRmanStatus - inserting into rsr'); deb('checkRmanStatus - this_dbinc_key:'||to_char(this_dbinc_key)); deb('checkRmanStatus - recid: '||to_char(recid)); deb('checkRmanStatus - stamp: '||to_char(stamp)); deb('checkRmanStatus - srecid: '||to_char(session_recid)); deb('checkRmanStatus - sstamp: '||to_char(session_stamp)); INSERT INTO rsr (rsr_key, dbinc_key, rsr_recid, rsr_stamp, rsr_pkey, rsr_l0key, rsr_level, rsr_type, rsr_oper, rsr_cmdid, rsr_status, rsr_mbytes, rsr_start, rsr_end, rsr_ibytes, rsr_obytes, rsr_optimized, rsr_otype, rsr_srecid, rsr_sstamp, rsr_odevtype, site_key, rsr_osb_allocated) VALUES (rman_seq.nextval, this_dbinc_key, recid, stamp, parent.rsr_key, parent.rsr_l0key, row_level, row_type, operation, command_id, status, mbytes_processed, start_time, end_time, ibytes, obytes, optimized, otype, session_recid, session_stamp, odevtype, this_site_key, decode(osb_allocated, 'YES', 'Y', 'N')); -- Cleanup the last rsr row which was inserted when catalog was not -- upgraded. DELETE rsr WHERE rsr.dbinc_key = this_dbinc_key AND rsr.rsr_recid = recid AND rsr.rsr_stamp = stamp AND ((this_site_key is not null AND rsr.site_key is NULL) OR (this_site_key is null and rsr.site_key is not null)); EXCEPTION WHEN dup_val_on_index THEN -- If the record already exist then just update the data. deb('checkRmanStatus - exception catch'); deb('checkRmanStatus - this_dbinc_key:'||to_char(this_dbinc_key)); deb('checkRmanStatus - recid: '||to_char(recid)); deb('checkRmanStatus - stamp: '||to_char(stamp)); deb('checkRmanStatus - srecid: '||to_char(session_recid)); deb('checkRmanStatus - sstamp: '||to_char(session_stamp)); UPDATE rsr SET rsr_pkey = parent.rsr_key, rsr_l0key = parent.rsr_l0key, rsr_level = row_level, rsr_type = row_type, rsr_oper = operation, rsr_cmdid = command_id, rsr_status = status, rsr_mbytes = mbytes_processed, rsr_start = start_time, rsr_end = end_time, rsr_ibytes = ibytes, rsr_obytes = obytes, rsr_optimized = optimized, rsr_otype = otype, rsr_odevtype = odevtype, rsr_osb_allocated = decode(osb_allocated, 'YES', 'Y', 'N') WHERE rsr.rsr_stamp = stamp AND rsr.rsr_recid = recid AND (rsr.site_key = this_site_key OR rsr.site_key is null and this_site_key is null) AND rsr.rsr_srecid = session_recid AND rsr.rsr_sstamp = session_stamp AND rsr.dbinc_key = this_dbinc_key; END; END checkRmanStatus; PROCEDURE endRmanStatusResync(recid number) IS BEGIN IF (last_rsr_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (recid < last_rsr_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_rsr_recid = recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_rsr_recid := recid; last_rsr_recid := NULL; END endRmanStatusResync; PROCEDURE updateRmanStatusRow( recid IN number ,stamp IN number ,mbytes IN number ,status IN binary_integer ) IS BEGIN IF (this_dbinc_key IS NULL) THEN return; END IF; UPDATE rsr SET rsr_status = decode(status, 1, 'RUNNING', 1+8, 'RUNNING WITH WARNINGS', 1+16, 'RUNNING WITH ERRORS', 1+8+16, 'RUNNING WITH ERRORS', 2, 'COMPLETED', 2+8, 'COMPLETED WITH WARNINGS', 2+16, 'COMPLETED WITH ERRORS', 2+8+16, 'COMPLETED WITH ERRORS', 'FAILED'), rsr_mbytes = mbytes WHERE rsr.rsr_stamp = stamp AND rsr.rsr_recid = recid AND (rsr.site_key = this_site_key OR rsr.site_key is null AND this_site_key is null) AND rsr.dbinc_key = this_dbinc_key; deb('updateRmanStatusRow - commit, release locks'); commit; END updateRmanStatusRow; /*-------------------* * Change Procedures * *-------------------*/ /* * In these change procedures, we don't check that we found the record, * because we are processing a DL record - i.e. we already processed the * other cf records and hence it might already be flagged deleted. This * applies to any status change because we might make it available and * then delete the object, so the object would be marked as deleted when * we process the object and the available status change (first DL) would * fail to find the object, so would a following DL that marks it deleted. */ PROCEDURE changeDatafileCopy( cdf_recid IN NUMBER ,cdf_stamp IN NUMBER ,status IN VARCHAR2 ,keep_options IN NUMBER DEFAULT NULL -- null means do not update ,keep_until IN DATE DEFAULT NULL ,osite_key IN number DEFAULT NULL -- old site_key for the record ,nsite_key IN number DEFAULT NULL -- null means do not update ) IS local dbinc%rowtype; fno cdf.file#%type; BEGIN IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; -- Determine if this is a controlfile copy or datafile copy. BEGIN -- bug 2719863: Add restriction on DBINC_KEY. SELECT file# into fno FROM cdf WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key) AND ((osite_key is null AND cdf.site_key is null) OR cdf.site_key = nvl(osite_key, cdf.site_key)) AND cdf.cdf_recid = changeDatafileCopy.cdf_recid AND cdf.cdf_stamp = changeDatafileCopy.cdf_stamp; EXCEPTION WHEN no_data_found THEN BEGIN -- bug 2719863: Add restriction on DBINC_KEY. SELECT 0 into fno FROM ccf WHERE dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key) AND ((osite_key is null AND ccf.site_key is null) OR ccf.site_key = nvl(osite_key, ccf.site_key)) AND ccf.ccf_recid = changeDatafileCopy.cdf_recid AND ccf.ccf_stamp = changeDatafileCopy.cdf_stamp; -- This is a controlfile, so call changeControlfileCopy(). changeControlfileCopy(cdf_recid, cdf_stamp, status, keep_options, keep_until, osite_key, nsite_key); RETURN; EXCEPTION WHEN no_data_found THEN RETURN; -- already deleted (we are processing a DL record) END; WHEN OTHERS THEN RAISE; END; IF status IS NULL THEN -- there is no need to change status so check if need -- to change keep attributes IF keep_until IS NOT NULL THEN UPDATE cdf SET keep_until = changeDatafileCopy.keep_until WHERE cdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND cdf.site_key is null) OR cdf.site_key = nvl(osite_key, cdf.site_key)) AND cdf.cdf_recid = changeDatafileCopy.cdf_recid AND cdf.cdf_stamp = changeDatafileCopy.cdf_stamp; END IF; IF keep_options IS NOT NULL THEN UPDATE cdf SET keep_options = changeDatafileCopy.keep_options WHERE cdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND cdf.site_key is null) OR cdf.site_key = nvl(osite_key, cdf.site_key)) AND cdf.cdf_recid = changeDatafileCopy.cdf_recid AND cdf.cdf_stamp = changeDatafileCopy.cdf_stamp; END IF; ELSIF status IN ('A','U','X') THEN -- 'see above comments for how file status field is changed' -- search on above string to find the relevant comments. UPDATE cdf SET status = changeDatafileCopy.status, site_key = nvl(nsite_key, site_key) WHERE cdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND cdf.site_key is null) OR cdf.site_key = nvl(osite_key, cdf.site_key)) AND cdf.cdf_recid = changeDatafileCopy.cdf_recid AND cdf.cdf_stamp = changeDatafileCopy.cdf_stamp; -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN deleteDuplicateCDF(cdf_recid, cdf_stamp, null); END IF; ELSIF status IN ('R','D') THEN DELETE FROM cdf WHERE cdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND cdf.site_key is null) OR cdf.site_key = nvl(osite_key, cdf.site_key)) AND cdf.cdf_recid = changeDatafileCopy.cdf_recid AND cdf.cdf_stamp = changeDatafileCopy.cdf_stamp; ELSE raise_application_error(-20100, 'Invalid status'); END IF; -- changeDatafileCopy is an atomic operation -- if called from deleted objects RESYNC do not release lock on db_inc, -- let endCkpt commit all changes. IF (this_ckp_key IS NULL) THEN deb('changeDatafileCopy - commit, release locks'); commit; END IF; END changeDatafileCopy; PROCEDURE changeControlfileCopy( cdf_recid IN NUMBER ,cdf_stamp IN NUMBER ,status IN VARCHAR2 ,keep_options IN NUMBER DEFAULT NULL -- null means do not update ,keep_until IN DATE DEFAULT NULL ,osite_key IN number DEFAULT NULL -- old site_key for the record ,nsite_key IN number DEFAULT NULL -- null means do not update ) IS local dbinc%rowtype; BEGIN IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; IF status IS NULL THEN -- there is no need to change status so check if need -- to change keep stuff IF keep_until IS NOT NULL THEN UPDATE ccf SET keep_until = changeControlfileCopy.keep_until WHERE ccf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND ccf.site_key is null) OR ccf.site_key = nvl(osite_key, ccf.site_key)) AND ccf.ccf_recid = changeControlfileCopy.cdf_recid AND ccf.ccf_stamp = changeControlfileCopy.cdf_stamp; END IF; IF keep_options IS NOT NULL THEN UPDATE ccf SET keep_options = changeControlfileCopy.keep_options WHERE ccf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND ccf.site_key is null) OR ccf.site_key = nvl(osite_key, ccf.site_key)) AND ccf.ccf_recid = changeControlfileCopy.cdf_recid AND ccf.ccf_stamp = changeControlfileCopy.cdf_stamp; END IF; ELSIF status IN ('A','U','X') THEN -- 'see above comments for how file status field is changed' -- search on above string to find the relevant comments. UPDATE ccf SET status = changeControlfileCopy.status, site_key = nvl(nsite_key, site_key) WHERE ccf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND ccf.site_key is null) OR ccf.site_key = nvl(osite_key,ccf.site_key)) AND ccf.ccf_recid = changeControlfileCopy.cdf_recid AND ccf.ccf_stamp = changeControlfileCopy.cdf_stamp; -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN deleteDuplicateCCF(cdf_recid, cdf_stamp, null); END IF; ELSIF status IN ('R','D') THEN DELETE FROM ccf WHERE ccf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND ccf.site_key is null) OR ccf.site_key = nvl(osite_key, ccf.site_key)) AND ccf.ccf_recid = changeControlfileCopy.cdf_recid AND ccf.ccf_stamp = changeControlfileCopy.cdf_stamp; ELSE raise_application_error(-20100, 'Invalid status'); END IF; -- changeControlfileCopy is an atomic operation -- if called from deleted objects RESYNC do not release lock on db_inc, -- let endCkpt commit all changes. IF (this_ckp_key IS NULL) THEN deb('changeControlfileCopy - commit, release locks'); commit; END IF; END changeControlfileCopy; PROCEDURE changeArchivedLog( al_recid IN NUMBER ,al_stamp IN NUMBER ,status IN VARCHAR2 ,osite_key IN NUMBER DEFAULT NULL -- old site_key for the record ,nsite_key IN NUMBER DEFAULT NULL -- null means do not update ) IS BEGIN IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; IF status IN ('A','U','X') THEN -- 'see above comments for how file status field is changed' is seen. -- search on above string to find the relevant comments. UPDATE al SET status = changeArchivedLog.status, site_key = nvl(nsite_key, site_key) 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 AND ((osite_key is null AND al.site_key is null) OR al.site_key = nvl(osite_key, al.site_key)); -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN deleteDuplicateAL(al_recid, al_stamp, null); END IF; ELSIF status IN ('R','D') THEN -- Bug 1186598 - always delete the row. -- see compatibility change in translateArchivedLogPattern -- and getArchivedLog DELETE FROM al WHERE al.dbinc_key IN (SELECT dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key) AND ((osite_key is null AND al.site_key is null) OR al.site_key = nvl(osite_key, al.site_key)) AND al.al_recid = changeArchivedLog.al_recid AND al.al_stamp = changeArchivedLog.al_stamp; ELSE raise_application_error(-20100, 'Invalid status'); END IF; -- changeArchivedLog is an atomic operation -- if called from deleted objects RESYNC do not release lock on db_inc, -- let endCkpt commit all changes. IF (this_ckp_key IS NULL) THEN deb('changeArchivedLog - commit, release locks'); commit; END IF; END changeArchivedLog; PROCEDURE changeBackupSet( recid IN number ,stamp IN number ,keep_options IN number -- null means do not update ,keep_until IN date ,osite_key IN number DEFAULT NULL -- old site_key for the record ,nsite_key IN number DEFAULT NULL -- null means do not update ) IS local bs%rowtype; CURSOR dflist IS SELECT * FROM bdf WHERE bdf.bs_key = local.bs_key; CURSOR rllist IS SELECT * FROM brl WHERE brl.bs_key = local.bs_key; BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; BEGIN SELECT * INTO local FROM bs WHERE bs.db_key = this_db_key AND bs.bs_recid = changeBackupSet.recid AND bs.bs_stamp = changeBackupSet.stamp; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; -- already deleted (we are processing a DL record) END; IF keep_until IS NOT NULL THEN UPDATE bs SET bs.keep_until = changeBackupSet.keep_until WHERE bs.bs_key = local.bs_key; END IF; IF keep_options IS NOT NULL THEN UPDATE bs SET bs.keep_options = changeBackupSet.keep_options WHERE bs.bs_key = local.bs_key; -- Find the datafile or redo log backups and adjust restore point as needed IF (local.bck_type = 'L') THEN FOR rlrec IN rllist LOOP updateRestorePoint(rlrec.low_scn, rlrec.next_scn); END LOOP; END IF; IF (local.bck_type = 'D') THEN FOR dfrec IN dflist LOOP updateRestorePoint(dfrec.ckp_scn, null); END LOOP; END IF; END IF; IF nsite_key is not null THEN UPDATE bs SET site_key = nsite_key WHERE bs_key = local.bs_key; UPDATE bp SET site_key = nsite_key WHERE bs_key = local.bs_key; END IF; -- changeBackupSet is an atomic operation -- if called from deleted objects RESYNC do not release lock on db_inc, -- let endCkpt commit all changes. IF (this_ckp_key IS NULL) THEN deb('changeBackupSet - commit, release locks'); commit; END IF; END changeBackupSet; PROCEDURE changeBackupPiece( bp_recid IN NUMBER ,bp_stamp IN NUMBER ,status IN VARCHAR2 ,set_stamp IN NUMBER DEFAULT NULL ,set_count IN NUMBER DEFAULT NULL ,osite_key IN number DEFAULT NULL -- old site_key for the record ,nsite_key IN number DEFAULT NULL -- null means do not update ) IS CURSOR bsQ IS SELECT bs_key FROM bp WHERE bp.db_key = this_db_key AND ((osite_key is null AND bp.site_key is null) OR bp.site_key = nvl(osite_key, bp.site_key)) AND bp.bp_recid = changeBackupPiece.bp_recid AND bp.bp_stamp = changeBackupPiece.bp_stamp; bsQrec bsQ%ROWTYPE; totalbp number; chgbskey number := NULL; BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; -- If set_stamp and set_count is valid, then find out the unique -- backupset that this change is refering to. IF (set_stamp is not null AND set_count is not null) THEN BEGIN SELECT bs_key INTO chgbskey FROM bs WHERE bs.db_key = this_db_key AND bs.set_stamp = changeBackupPiece.set_stamp AND bs.set_count = changeBackupPiece.set_count; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN; -- already deleted (we are processing a DL record) END; deb('changeBackupPiece - chgbskey=' || chgbskey); ELSE -- Bug-4531791: -- If set_stamp and set_count is NULL, then it is a record that was -- created in pre-11g version. We cannot identify the backupset uniquely. -- So, we have to live with deleting all the backuppieces that matches -- bp_recid and bp_stamp. SELECT count(*) INTO totalbp FROM bp WHERE bp.db_key = this_db_key AND ((osite_key is null AND bp.site_key is null) OR bp.site_key = nvl(osite_key, bp.site_key)) AND bp.bp_recid = changeBackupPiece.bp_recid AND bp.bp_stamp = changeBackupPiece.bp_stamp AND bp.bs_key = nvl(chgbskey, bp.bs_key); deb('changeBackupPiece - number of backupsets match ' || totalbp); IF totalbp = 0 then RETURN; -- already deleted (we are processing a DL record) END IF; END IF; IF status in ('A','U','X') THEN -- 'see above comments for how file status field is changed' -- search on above string to find the relevant comments. UPDATE bp SET status = changeBackupPiece.status, site_key = nvl(nsite_key, site_key) WHERE bp.db_key = this_db_key AND ((osite_key is null AND bp.site_key is null) OR bp.site_key = nvl(osite_key, bp.site_key)) AND bp.bp_recid = changeBackupPiece.bp_recid AND bp.bp_stamp = changeBackupPiece.bp_stamp AND bp.bs_key = nvl(chgbskey, bp.bs_key); -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN -- clear site_key in backup set record, so that it is re-calculated -- by updateBackupSetRec procedure IF chgbskey is not null THEN UPDATE bs SET site_key=null WHERE bs_key = chgbskey; ELSE UPDATE bs SET site_key=null WHERE bs_key in (SELECT bs_key FROM bp WHERE bp.db_key = this_db_key AND ((osite_key is null AND bp.site_key is null) OR bp.site_key = nvl(osite_key, bp.site_key)) AND bp.bp_recid = changeBackupPiece.bp_recid AND bp.bp_stamp = changeBackupPiece.bp_stamp); END IF; deleteDuplicateBP(bp_recid, bp_stamp, chgbskey, null, null); END IF; ELSIF status not in ('R', 'D') THEN raise_application_error(-20100, 'Invalid status'); END IF; IF (chgbskey IS NULL) THEN FOR bsQrec in bsQ LOOP -- bsQrec cursor will not return any row if you delete before -- opening cursor. IF status in ('R', 'D') THEN DELETE FROM bp WHERE bp.db_key = this_db_key AND ((osite_key is null AND bp.site_key is null) OR bp.site_key = nvl(osite_key, bp.site_key)) AND bp.bp_recid = changeBackupPiece.bp_recid AND bp.bp_stamp = changeBackupPiece.bp_stamp AND bp.bs_key = bsQrec.bs_key; END IF; -- revalidate the backup set updateBackupSetRec(bsQrec.bs_key); END LOOP; ELSE IF status in ('R', 'D') THEN DELETE FROM bp WHERE bp.db_key = this_db_key AND ((osite_key is null AND bp.site_key is null) OR bp.site_key = nvl(osite_key, bp.site_key)) AND bp.bp_recid = changeBackupPiece.bp_recid AND bp.bp_stamp = changeBackupPiece.bp_stamp AND bp.bs_key = chgbskey; END IF; updateBackupSetRec(chgbskey); END IF; -- changeBackupPiece is an atomic operation -- if called from deleted objects RESYNC do not release lock on db_inc, -- let endCkpt commit all changes. IF (this_ckp_key IS NULL) THEN deb('changeBackupPiece - commit, release locks'); commit; END IF; END changeBackupPiece; PROCEDURE changeProxyCopy( pc_recid IN NUMBER ,pc_stamp IN NUMBER ,status IN VARCHAR2 ,keep_options IN NUMBER DEFAULT NULL -- null means do not update ,keep_until IN DATE DEFAULT NULL ,osite_key IN number DEFAULT NULL -- old site_key for the record ,nsite_key IN number DEFAULT NULL -- null means do not update ) IS low_scn number; next_scn number; xobjid rowid; -- proxy object rowid BEGIN IF this_db_key IS NULL THEN raise_application_error(-20021, 'Database not set'); END IF; IF status IS NULL THEN -- There is no need to change status so check if need -- to change keep stuff. IF keep_until IS NOT NULL THEN UPDATE xdf SET xdf.keep_until = changeProxyCopy.keep_until WHERE xdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xdf.site_key is null) OR xdf.site_key = nvl(osite_key, xdf.site_key)) AND xdf.xdf_recid = changeProxyCopy.pc_recid AND xdf.xdf_stamp = changeProxyCopy.pc_stamp; -- If it wasn't a proxy datafile, maybe it's a proxy controlfile. IF sql%rowcount = 0 THEN UPDATE xcf SET xcf.keep_until = changeProxyCopy.keep_until WHERE xcf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xcf.site_key is null) OR xcf.site_key = nvl(osite_key, xcf.site_key)) AND xcf.xcf_recid = changeProxyCopy.pc_recid AND xcf.xcf_stamp = changeProxyCopy.pc_stamp; END IF; END IF; IF keep_options IS NOT NULL THEN SELECT min(ckp_scn), min(rowid) into low_scn, xobjid FROM xdf WHERE xdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xdf.site_key is null) OR xdf.site_key = nvl(osite_key, xdf.site_key)) AND xdf.xdf_recid = changeProxyCopy.pc_recid AND xdf.xdf_stamp = changeProxyCopy.pc_stamp; -- If it was a datafile, check for restore points and update the row IF xobjid IS NOT NULL THEN updateRestorePoint(low_scn, null); UPDATE xdf SET xdf.keep_options = changeProxyCopy.keep_options WHERE rowid = xobjid; ELSE -- If it was not a proxy datafile, maybe it is a proxy controlfile. UPDATE xcf SET xcf.keep_options = changeProxyCopy.keep_options WHERE xcf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xcf.site_key is null) OR xcf.site_key = nvl(osite_key, xcf.site_key)) AND xcf.xcf_recid = changeProxyCopy.pc_recid AND xcf.xcf_stamp = changeProxyCopy.pc_stamp; -- If it was not a proxy controlfile, maybe it is a proxy archivelog. IF sql%rowcount = 0 THEN SELECT min(xal.low_scn), min(xal.next_scn), min(rowid) into low_scn, next_scn, xobjid FROM xal WHERE xal.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xal.site_key is null) OR xal.site_key = nvl(osite_key, xal.site_key)) AND xal.xal_recid = changeProxyCopy.pc_recid AND xal.xal_stamp = changeProxyCopy.pc_stamp; -- Handle restore points and update the proxy archivelog IF xobjid IS NOT NULL THEN updateRestorePoint(low_scn, next_scn); UPDATE xal SET xal.keep_options = changeProxyCopy.keep_options WHERE rowid = xobjid; END IF; END IF; END IF; END IF; ELSIF status in ('A','U','X') THEN -- 'see above comments for how file status field is changed' -- search on above string to find the relevant comments. UPDATE xdf SET status = changeProxyCopy.status, site_key = nvl(nsite_key, site_key) WHERE xdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xdf.site_key is null) OR xdf.site_key = nvl(osite_key, xdf.site_key)) AND xdf.xdf_recid = changeProxyCopy.pc_recid AND xdf.xdf_stamp = changeProxyCopy.pc_stamp; -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN deleteDuplicateXDF(pc_recid, pc_stamp, null, null); END IF; -- if it was not a proxy datafile, maybe it is a proxy controlfile IF sql%rowcount = 0 THEN UPDATE xcf SET status = changeProxyCopy.status, site_key = nvl(nsite_key, site_key) WHERE xcf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xcf.site_key is null) OR xcf.site_key = nvl(osite_key, xcf.site_key)) AND xcf.xcf_recid = changeProxyCopy.pc_recid AND xcf.xcf_stamp = changeProxyCopy.pc_stamp; -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN deleteDuplicateXCF(pc_recid, pc_stamp, null, null); END IF; END IF; -- if it was not a proxy controlfile, maybe it is a proxy archivedlog IF sql%rowcount = 0 THEN UPDATE xal SET status = changeProxyCopy.status, site_key = nvl(nsite_key, site_key) WHERE xal.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xal.site_key is null) OR xal.site_key = nvl(osite_key, xal.site_key)) AND xal.xal_recid = changeProxyCopy.pc_recid AND xal.xal_stamp = changeProxyCopy.pc_stamp; -- when site ownership is grabbed, delete duplicate entries for this site IF sql%rowcount > 0 and nsite_key is not null THEN deleteDuplicateXAL(pc_recid, pc_stamp, null, null); END IF; END IF; ELSIF status IN ('R','D') THEN -- Get the checkpoint change# for restore points only if keep SELECT min(ckp_scn), min(rowid) into low_scn, xobjid FROM xdf WHERE xdf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xdf.site_key is null) OR xdf.site_key = nvl(osite_key, xdf.site_key)) AND xdf.xdf_recid = changeProxyCopy.pc_recid AND xdf.xdf_stamp = changeProxyCopy.pc_stamp; -- Delete datafile IF xobjid IS NOT NULL THEN updateRestorePoint(low_scn, null); DELETE FROM xdf WHERE rowid = xobjid; ELSE -- if it was not a proxy datafile, maybe it is a proxy controlfile DELETE FROM xcf WHERE xcf.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xcf.site_key is null) OR xcf.site_key = nvl(osite_key, xcf.site_key)) AND xcf.xcf_recid = changeProxyCopy.pc_recid AND xcf.xcf_stamp = changeProxyCopy.pc_stamp; -- if it was not a proxy controlfile, maybe it is a proxy archivedlog IF sql%rowcount = 0 THEN SELECT min(xal.low_scn), min(xal.next_scn), min(rowid) into low_scn, next_scn, xobjid FROM xal WHERE xal.dbinc_key in (select dbinc_key from dbinc where dbinc.db_key = this_db_key) AND ((osite_key is null AND xal.site_key is null) OR xal.site_key = nvl(osite_key, xal.site_key)) AND xal.xal_recid = changeProxyCopy.pc_recid AND xal.xal_stamp = changeProxyCopy.pc_stamp; IF xobjid IS NOT NULL THEN updateRestorePoint(low_scn, next_scn); DELETE FROM xal WHERE rowid = xobjid; END IF; END IF; END IF; ELSE raise_application_error(-20100, 'Invalid status'); END IF; -- changeProxyCopy is an atomic operation -- if called from deleted objects RESYNC do not release lock on db_inc, -- let endCkpt commit all changes. IF (this_ckp_key IS NULL) THEN deb('changeProxyCopy - commit, release locks'); commit; END IF; END changeProxyCopy; /*----------------------------* * Stored Script Procedures * *----------------------------*/ PROCEDURE createScript(name IN VARCHAR2) IS BEGIN createScript(name, NULL, FALSE); END; PROCEDURE createScript(name IN VARCHAR2, scr_com IN VARCHAR2, global IN boolean) IS foo NUMBER; dbkey NUMBER := this_db_key; BEGIN scr_key := NULL; -- for safety IF global THEN dbkey := NULL; scr_glob := TRUE; ELSE scr_glob := FALSE; IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; END IF; SELECT count(*) INTO foo FROM scr WHERE ((dbkey is not null and scr.db_key = dbkey) OR (dbkey is null and scr.db_key is null)) AND scr.scr_name = createScript.name; IF foo > 0 THEN raise_application_error(-20401, 'script '||name||' already exists'); END IF; INSERT INTO scr VALUES(rman_seq.nextval, dbkey, name, scr_com); SELECT rman_seq.currval INTO scr_key FROM dual; scr_line := 1; -- createScript is an atomic operation deb('createScript - commit, release locks'); commit; END; PROCEDURE replaceScript(name IN VARCHAR2) IS BEGIN replaceScript(name, NULL, FALSE); END; PROCEDURE replaceScript(name IN VARCHAR2, scr_com IN VARCHAR2, global IN boolean) IS dbkey NUMBER := this_db_key; BEGIN IF global THEN dbkey := NULL; scr_glob := TRUE; ELSE scr_glob := FALSE; IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; END IF; SELECT scr_key INTO scr_key FROM scr WHERE ((dbkey is not null and scr.db_key = dbkey) OR (dbkey is null and scr.db_key is null)) AND scr.scr_name = replaceScript.name; UPDATE scr SET scr_comment = scr_com WHERE scr.scr_key = dbms_rcvcat.scr_key; DELETE FROM scrl WHERE scrl.scr_key = dbms_rcvcat.scr_key; scr_line := 1; -- replaceScript is an atomic operation. deb('replaceScript - commit, release locks'); commit; EXCEPTION WHEN NO_DATA_FOUND THEN createScript(name, scr_com, global); END; PROCEDURE putLine(line IN VARCHAR2) IS BEGIN IF not scr_glob and 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(scr_key, linenum, text) VALUES(scr_key, scr_line, line); scr_line := scr_line + 1; END; PROCEDURE deleteScript(name IN VARCHAR2) IS BEGIN deleteScript(name, 0); END; PROCEDURE deleteScript(name IN VARCHAR2, glob IN NUMBER) IS dbkey NUMBER := this_db_key; BEGIN IF glob = 1 THEN dbkey := NULL; ELSE IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; END IF; SELECT scr_key INTO scr_key FROM scr WHERE ((dbkey is not null and scr.db_key = dbkey) OR (dbkey is null and scr.db_key is null)) AND scr.scr_name = deleteScript.name; DELETE FROM scr WHERE scr.scr_key = dbms_rcvcat.scr_key; scr_key := NULL; -- deleteScript is an atomic operation. deb('deleteScript - commit, release locks'); commit; EXCEPTION WHEN NO_DATA_FOUND THEN scr_key := NULL; raise_application_error(-20400, 'stored script not found'); END; PROCEDURE getScript(name IN VARCHAR2) IS BEGIN getScript(name, 0); END; PROCEDURE getScript(name IN VARCHAR2, glob IN NUMBER) IS dbkey NUMBER := this_db_key; BEGIN IF glob = 1 THEN dbkey := NULL; scr_glob := TRUE; ELSE scr_glob := FALSE; IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; END IF; SELECT scr_key INTO scr_key FROM scr WHERE ((dbkey is not null and scr.db_key = dbkey) OR (dbkey is null and scr.db_key is null)) 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 not scr_glob and this_db_key IS NULL THEN raise_application_error(-20021, 'Database not set'); END IF; IF NOT scrlQ%ISOPEN THEN raise_application_error(-20403, 'getScript not done'); END IF; FETCH scrlQ INTO scrl_row; IF scrlQ%NOTFOUND THEN -- end of fetch close scrlQ; return NULL; END IF; RETURN scrl_row.text; END; PROCEDURE commitChanges IS BEGIN deb('commitChanges - commit, release locks'); commit; END; -- version info -- Return all the protocol versions that we support, one at a time. -- Return them in ascending version number order. FUNCTION getPackageVersion RETURN VARCHAR2 IS BEGIN if version_counter > version_max_index then version_counter := 1; return null; end if; version_counter := version_counter + 1; return version_list(version_counter - 1); END; FUNCTION getCatalogVersion RETURN VARCHAR2 IS version rcver.version%type; BEGIN IF NOT rcverQ%ISOPEN THEN open rcverQ; END IF; FETCH rcverQ into version; IF rcverQ%NOTFOUND THEN -- end of fetch close rcverQ; return NULL; END IF; RETURN version; END; /*---------------------------------------* * Procedures for clone database support * *---------------------------------------*/ PROCEDURE setCloneName(file# IN NUMBER ,creation_change# IN NUMBER ,new_clone_fname IN VARCHAR2 ,old_clone_fname IN VARCHAR2 ,changedauxname OUT boolean ,plugin_change# IN NUMBER DEFAULT 0) IS lfname df.clone_fname%TYPE; BEGIN deb('setCloneName: file#=' || to_char(file#)|| ', creation_fname=' || to_char(nvl(creation_change#, ''))|| ', plugin_change#=' || to_char(nvl(plugin_change#, ''))|| ', old_clone_fname=' || old_clone_fname || ', new_clone_fname=' || new_clone_fname); changedauxname := FALSE; -- Note: if target database has been just upgraded to 9.0 then -- clone_fname is unknown and we will not change recovery catalog. IF (new_clone_fname = 'UNKNOWN') THEN RETURN; END IF; IF old_clone_fname is NULL THEN IF new_clone_fname = 'NONE' THEN -- No new auxname, just return RETURN; ELSE lfname := new_clone_fname; END IF; ELSE IF new_clone_fname = 'NONE' THEN lfname := NULL; ELSIF old_clone_fname = new_clone_fname THEN -- We already have the auxname, just return RETURN; ELSE lfname := new_clone_fname; END IF; END IF; UPDATE df SET df.clone_fname = lfname WHERE df.dbinc_key = this_dbinc_key AND df.file# = setCloneName.file# AND df.create_scn = setCloneName.creation_change# AND df.plugin_scn = setCloneName.plugin_change#; changedauxname := TRUE; deb('setCloneName - changed auxname for file# '||to_char(file#)|| ' from '||nvl(old_clone_fname, 'NULL')||' to '|| nvl(lfname, 'NULL')); EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(-20105, 'datafile missing'); END; -- We need need this function in RCVCAT PL/SQL package because recover.txt -- cannot access Recovery Catalog RCVMAN PL/SQL package. FUNCTION getCloneName( file# IN NUMBER ,creation_change# IN NUMBER ,plugin_change# IN NUMBER DEFAULT 0) RETURN VARCHAR2 IS ret df.clone_fname%TYPE; BEGIN -- call getCloneName from rcvman package. Note that in this we call -- recovery catalog version! ret := dbms_rcvman.getCloneName(file#, creation_change#, plugin_change#); RETURN ret; EXCEPTION WHEN NO_DATA_FOUND THEN raise_application_error(-20105, 'datafile missing'); END; /*-----------------------------------* * Procedures for RMAN configuration * *-----------------------------------*/ -- 9i setConfig PROCEDURE setConfig(conf# IN NUMBER ,name IN VARCHAR2 ,value IN VARCHAR2) IS BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; INSERT INTO conf( db_key, conf#, name, value, cleanup, db_unique_name, site_key) VALUES(this_db_key, conf#, name, value, 'YES', NULL, 0); EXCEPTION WHEN dup_val_on_index THEN UPDATE conf SET conf.name = name, conf.value = value WHERE conf.conf# = conf# AND conf.db_key = this_db_key; RETURN; END; -- setConfig used by 10i databases PROCEDURE setConfig2(conf# IN NUMBER ,name IN VARCHAR2 ,value IN VARCHAR2 ,nodespec IN BOOLEAN) IS lname conf.name%TYPE; lvalue conf.value%TYPE; BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; -- Now, add the configuration itself. Generic configuration have -- db_unique_name value as NULL and site_key as 0. IF (nodespec) THEN INSERT INTO conf( db_key, conf#, name, value, cleanup, db_unique_name, site_key) VALUES(this_db_key, conf#, name, value, 'NO', this_db_unique_name, this_site_key); ELSE INSERT INTO conf( db_key, conf#, name, value, cleanup, db_unique_name, site_key) VALUES(this_db_key, conf#, name, value, 'NO', NULL, 0); END IF; deb('setConfig - Added name=(' || name || '), value=(' || value || ') to node ' || this_db_unique_name || '('|| conf# ||')'); EXCEPTION -- We should never have a record with that would have different -- (name,value). Assert the condition. WHEN dup_val_on_index THEN select name, value into lname, lvalue from conf where conf.conf# = setConfig2.conf# AND conf.db_key = this_db_key AND db_unique_name = this_db_unique_name; IF (lname = name AND lvalue = value) THEN RETURN; END IF; deb('setConfig - lname=' || lname || ', lvalue=' || lvalue); RAISE; WHEN others THEN deb('setConfig - this_db_unique_name='||this_db_unique_name|| ', conf#='||conf#); RAISE; END; -- setConfig3 used by 11i RMAN client to set remote site specific -- configurations directly in recovery catalog. FUNCTION setConfig3(name IN VARCHAR2 ,value IN VARCHAR2 ,db_unique_name IN VARCHAR2) RETURN NUMBER IS lname conf.name%TYPE NOT NULL := name; lvalue conf.value%TYPE NOT NULL := value; ldbuname conf.db_unique_name%TYPE NOT NULL := upper(db_unique_name); lsite_key NUMBER; lconf_key NUMBER; BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; deb('setConfig3 - Remote setConfig for '||ldbuname); SELECT site_key into lsite_key from node where node.db_unique_name = ldbuname AND node.db_key = this_db_key; deb('setConfig3 - remote_site_key='||lsite_key); SELECT rman_seq.nextval INTO lconf_key FROM dual; -- Now, add the configuration itself. Generic configuration have -- db_unique_name value as NULL and site_key as 0. INSERT INTO conf( db_key, conf#, name, value, cleanup, db_unique_name, site_key) VALUES(this_db_key, lconf_key, name, value, 'NO', ldbuname, lsite_key); UPDATE node SET node.force_resync2cf = 'YES' WHERE node.db_key = this_db_key AND node.db_unique_name = ldbuname; deb('setConfig3 - commit, release locks'); COMMIT; deb('setConfig3 - Added name=(' || lname || '), value=(' || lvalue || ') to node ' || ldbuname || '('|| lconf_key ||')'); RETURN lconf_key; EXCEPTION WHEN OTHERS THEN deb('setConfig3 - rollback all, release locks'); ROLLBACK; RAISE; END; -- deleteConfig3 used by 11i RMAN client to delete remote site specific -- configurations directly in recovery catalog. PROCEDURE deleteConfig3(conf# IN NUMBER ,db_unique_name IN VARCHAR2) IS BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; -- Must delete one or zero record, otherwise it is an internal error -- zero records will be deleted when getConfig returns configurations -- from primary database. DELETE conf WHERE conf.db_key = this_db_key AND conf.db_unique_name = deleteConfig3.db_unique_name AND conf.conf# = deleteConfig3.conf#; IF sql%rowcount <> 1 AND sql%rowcount <> 0 THEN raise_application_error(-20999, 'Internal error in deleteConfig3, deleted rows= ' || sql%rowcount); END IF; UPDATE node SET node.force_resync2cf = 'YES' WHERE node.db_key = this_db_key AND node.db_unique_name = deleteconfig3.db_unique_name; deb('deleteConfig3 - commit, release locks'); COMMIT; EXCEPTION WHEN OTHERS THEN deb('deleteConfig3 - rollback all, release locks'); ROLLBACK; RAISE; END; PROCEDURE resetConfig IS BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; DELETE conf WHERE conf.db_key = this_db_key; EXCEPTION WHEN NO_DATA_FOUND THEN -- if db_key is not found, ignore this operation RETURN; END resetConfig; PROCEDURE resetConfig2 (nodespec IN BOOLEAN, high_conf_recid IN NUMBER DEFAULT NULL) IS BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; -- The configuration rows from preivous oracle version have -- cleanup set to 'YES'. These rows should be deleted - regardless -- it they are generic or node specific configurations. DELETE conf WHERE conf.db_key = this_db_key AND conf.cleanup = 'YES'; -- If nodespec is TRUE, then we will delete only configuration -- that mach our db_unique_name - these are node specific -- configurations. IF (nodespec) THEN DELETE conf WHERE conf.db_key = this_db_key AND conf.db_unique_name = this_db_unique_name; ELSE -- If we are deleting generic (not node specific), then we should -- force resync from recovery catalog to control file for all -- other database. force_resync2cf := 'YES'; DELETE conf WHERE conf.db_key = this_db_key AND conf.db_unique_name IS NULL; END IF; IF high_conf_recid IS NOT NULL THEN last_conf_recid := high_conf_recid; deb('resetConfig2 - updated last_conf_recid=' || last_conf_recid); END IF; EXCEPTION WHEN NO_DATA_FOUND THEN -- if no configurations to delete, ignore this operation RETURN; END resetConfig2; PROCEDURE deleteConfig(conf# IN NUMBER) IS BEGIN -- This function is not called in 9.2/10.2 onwards. Not sure when/why -- this function was introduced. It would be safe to assert in this function. raise_application_error(-20999, 'Internal error in deleteConfig should not be called '); END; /*-------------------------* * Catalog upgrade support * *-------------------------*/ /* NOTES: * * These procedures *must* tolerate being called *before* dbinc_key * has been set. */ /*-------------------* * Utility functions * *-------------------*/ PROCEDURE bsStatusRecalc(status IN varchar2) IS cursor bsQ(status varchar2) IS SELECT bs_key FROM bs WHERE bs.status = bsStatusRecalc.status -- bug 6055481 AND bs.db_key = this_db_key; bsQrec bsQ%ROWTYPE; BEGIN FOR bsQrec in bsQ(status) LOOP updateBackupSetRec(bsQrec.bs_key); END LOOP; END; PROCEDURE reNormalize(newname IN varchar2, oldname OUT varchar2) IS BEGIN IF newname IS NULL THEN -- initialize IF reNorm_dfatt_c%ISOPEN THEN CLOSE reNorm_dfatt_c; END IF; IF reNorm_orl_c%ISOPEN THEN CLOSE reNorm_orl_c; END IF; IF reNorm_al_c%ISOPEN THEN CLOSE reNorm_al_c; END IF; IF reNorm_bp_c%ISOPEN THEN CLOSE reNorm_bp_c; END IF; IF reNorm_ccf_c%ISOPEN THEN CLOSE reNorm_ccf_c; END IF; IF reNorm_cdf_c%ISOPEN THEN CLOSE reNorm_cdf_c; END IF; IF reNorm_tfatt_c%ISOPEN THEN CLOSE reNorm_tfatt_c; END IF; reNorm_state := RENORM_DFATT; ELSE -- update the previous row IF reNorm_state = RENORM_DFATT THEN UPDATE site_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; ELSIF reNorm_state = RENORM_TFATT THEN UPDATE site_tfatt SET fname = newname WHERE CURRENT OF reNorm_tfatt_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 := RENORM_TFATT; END IF; END IF; IF reNorm_state = RENORM_TFATT THEN IF NOT reNorm_tfatt_c%ISOPEN THEN OPEN reNorm_tfatt_c; END IF; FETCH reNorm_tfatt_c INTO oldname; IF reNorm_tfatt_c%NOTFOUND THEN CLOSE reNorm_tfatt_c; reNorm_state := NULL; oldname := NULL; deb('reNormalize - commit, release locks'); commit; END IF; END IF; END reNormalize; -- The sanityCheck procedure can be used for any cleaning up of the recovery -- catalog that can be done solely by examination of the recovery catalog -- itself. It is the last thing that is done during resync, before commit. -- Forward declaration of functions and procedures used by sanityCheck PROCEDURE cleanupResyncedBS; PROCEDURE cleanupCKP; PROCEDURE cleanupRLH; PROCEDURE cleanupRSR; PROCEDURE cleanupBV; PROCEDURE cleanupROUT; PROCEDURE cleanupNRS; PROCEDURE sanityCheck IS BEGIN cleanupResyncedBS; cleanupCKP; cleanupRLH; cleanupRSR; cleanupBV; cleanupROUT; cleanupNRS; END sanityCheck; PROCEDURE cleanupResyncedBS IS cnt number; BEGIN -- Check that no backup sets have just been resynced -- for which there are no pieces. This can happen when backup sets are -- created in advance of the pieces and later on, no backup piece is actually -- inserted. deb('cleanupResyncedBS - cntbs='||cntbs); IF cntbs is NULL THEN raise_application_error(-20107, 'invalid bskey counter'); END IF; FOR i IN 1 .. cntbs LOOP SELECT count(*) into cnt from bs where bs_key = updatebs(i); IF cnt > 0 THEN deb('cleanupResyncedBS - updating bs_key='||updatebs(i)); updateBackupSetRec(updatebs(i)); END IF; END LOOP; cntbs := 0; END cleanupResyncedBS; PROCEDURE cleanupCKP IS scn NUMBER; seq NUMBER; keep_ckp_key_1 NUMBER; keep_ckp_key_2 NUMBER; start_time DATE := sysdate; BEGIN -- Remove unneeded rows from the CKP table. We can remove all rows EXCEPT: -- The rows that are referenced by foreign key columns from other tables. -- For the query in beginckpt, the row with the highest ckp_key. -- The one or two rows that are needed by getCheckpoint. IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; dbms_rcvman.getCheckpoint(scn, seq, keep_ckp_key_1, keep_ckp_key_2); deb('cleanupCKP - scn=' || scn); deb('cleanupCKP - seq=' || seq); deb('cleanupCKP - keep_ckp_key_1=' || keep_ckp_key_1); deb('cleanupCKP - keep_ckp_key_2=' || keep_ckp_key_2); -- In this statement, the ckp2 query defines the rows that must not be -- deleted. All other rows can be deleted. The outer join of ckp1 and ckp2 -- 'inverts' the selection, leaving just the rows that can be deleted. delete from ckp where dbinc_key = this_dbinc_key and ckp_key in (select ckp_key1 from (select ckp_key ckp_key1 from ckp where dbinc_key = this_dbinc_key) ckp1, (select keep_ckp_key_1 ckp_key2 from dual union select keep_ckp_key_2 from dual union select nvl(max(ckp_key),0) from ckp where dbinc_key=this_dbinc_key union select start_ckp_key from tsatt where dbinc_key = this_dbinc_key union select nvl(end_ckp_key,0) from tsatt where dbinc_key = this_dbinc_key) ckp2 where ckp_key1 = ckp_key2(+) and ckp_key2 is null) and site_key = this_site_key; deb('cleanupCKP - deleted ' || sql%rowcount || ' rows from ckp table'); deb('cleanupCKP - took ' || ((sysdate - start_time) * 86400) || ' seconds'); END cleanupCKP; PROCEDURE cleanupRLH IS oldscn NUMBER; start_time DATE := sysdate; BEGIN -- Remove unneeded rows from the RLH table. We can remove RLH rows that -- have an scn older than the oldest scn of all the backups and copies. -- The RLH records are only used when computing the SCN of a time given -- to recover. If no backups/copies exist that have that SCN, the RLH -- records are not needed. IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; SELECT nvl(min(scn),power(2,64)-1) INTO oldscn FROM ( SELECT min(brl.low_scn) scn FROM brl WHERE brl.dbinc_key = this_dbinc_key UNION SELECT min(al.low_scn) FROM al WHERE al.dbinc_key = this_dbinc_key UNION SELECT min(xal.low_scn) FROM xal WHERE xal.dbinc_key = this_dbinc_key UNION SELECT min(bdf.ckp_scn) FROM bdf WHERE bdf.dbinc_key = this_dbinc_key UNION SELECT min(cdf.ckp_scn) FROM cdf WHERE cdf.dbinc_key = this_dbinc_key UNION SELECT min(xdf.ckp_scn) FROM xdf WHERE xdf.dbinc_key = this_dbinc_key UNION SELECT min(bcf.ckp_scn) FROM bcf WHERE bcf.dbinc_key = this_dbinc_key UNION SELECT min(ccf.ckp_scn) FROM ccf WHERE ccf.dbinc_key = this_dbinc_key UNION SELECT min(xcf.ckp_scn) FROM xcf WHERE xcf.dbinc_key = this_dbinc_key ); deb('cleanupRLH - scn='||oldscn); DELETE FROM rlh WHERE rlh.dbinc_key = this_dbinc_key AND next_scn < oldscn; deb('cleanupRLH - deleted ' || sql%rowcount || ' rows from rlh table'); deb('cleanupRLH - took ' || ((sysdate - start_time) * 86400) || ' seconds'); END cleanupRLH; PROCEDURE cleanupBV IS start_time DATE := sysdate; BEGIN -- This procedure deletes all 'backup validate' backups that were -- created more than 180 days ago. This is the only -- place that backup validate rows are ever deleted. DELETE FROM bs WHERE db_key = this_db_key AND ((input_file_scan_only='YES' AND SYSDATE - completion_time >= 180) OR (nvl(input_file_scan_only,'NO')='NO' AND status='D')); deb('cleanupBV - deleted ' || sql%rowcount || ' rows from bs table'); deb('cleanupBV - took ' || ((sysdate - start_time) * 86400) || ' seconds'); END cleanupBV; FUNCTION getDbid RETURN NUMBER IS dbid NUMBER; BEGIN SELECT db.db_id INTO dbid FROM db WHERE db_key = this_db_key AND curr_dbinc_key = this_dbinc_key; RETURN dbid; EXCEPTION WHEN no_data_found THEN raise_application_error(-20001, 'Database not found'); END getDbid; FUNCTION beginIncarnationResync(return_Recid in boolean DEFAULT FALSE) RETURN NUMBER IS local_kccdivts number; BEGIN checkResync; IF return_Recid THEN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_ic_recid INTO last_ic_recid FROM node WHERE site_key = this_site_key; ELSE last_ic_recid := sessionWaterMarks.high_ic_recid; END IF; RETURN last_ic_recid; ELSE IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT last_kccdivts INTO local_kccdivts FROM node WHERE site_key = this_site_key; ELSE local_kccdivts := 0; END IF; IF (local_kccdivts IS NULL) THEN local_kccdivts := 0; END IF; RETURN local_kccdivts; END IF; END beginIncarnationResync; -- Return the correct incarnation for the supplied resetlogs data, taking -- the current 'this_db_key' into account. If no matching incarnation is -- found, then an artificial one is added. FUNCTION checkIncarnation(reset_scn IN NUMBER, reset_time IN DATE, prior_reset_scn IN NUMBER DEFAULT NULL, prior_reset_time IN DATE DEFAULT NULL, db_name IN VARCHAR2 DEFAULT 'UNKNOWN') RETURN NUMBER IS local dbinc%rowtype; prior_dbinc_key number := NULL; BEGIN BEGIN SELECT dbinc_key, parent_dbinc_key, db_name INTO local.dbinc_key, local.parent_dbinc_key, local.db_name FROM dbinc WHERE dbinc.db_key = this_db_key AND dbinc.reset_scn = checkIncarnation.reset_scn AND dbinc.reset_time = checkIncarnation.reset_time; EXCEPTION WHEN no_data_found THEN local.dbinc_key := NULL; local.parent_dbinc_key := NULL; local.db_name := 'UNKNOWN'; END; IF (local.parent_dbinc_key IS NULL AND checkIncarnation.prior_reset_scn IS NOT NULL) THEN BEGIN SELECT dbinc_key INTO prior_dbinc_key FROM dbinc WHERE dbinc.db_key = this_db_key AND dbinc.reset_scn = checkIncarnation.prior_reset_scn AND dbinc.reset_time = checkIncarnation.prior_reset_time; EXCEPTION WHEN no_data_found THEN prior_dbinc_key := NULL; END; END IF; IF (local.dbinc_key IS NOT NULL) THEN -- parent_dbinc_key not filled up? IF (local.parent_dbinc_key IS NULL AND prior_dbinc_key IS NOT NULL) THEN UPDATE dbinc SET parent_dbinc_key = prior_dbinc_key WHERE dbinc.dbinc_key = local.dbinc_key; END IF; -- db_name not filled up? IF (local.db_name != 'UNKNOWN' AND checkIncarnation.db_name != 'UNKNOWN') THEN UPDATE dbinc SET db_name = checkIncarnation.db_name WHERE dbinc.dbinc_key = local.dbinc_key; END IF; RETURN local.dbinc_key; END IF; -- the database incarnation was not found, create an artificial one BEGIN INSERT INTO dbinc (dbinc_key, db_key, db_name, reset_scn, reset_time, parent_dbinc_key) VALUES (rman_seq.nextval, this_db_key, upper(checkIncarnation.db_name), checkIncarnation.reset_scn, checkIncarnation.reset_time, prior_dbinc_key); EXCEPTION WHEN dup_val_on_index THEN raise_application_error(-20009, 'Db incarnation already registered'); END; SELECT rman_seq.currval INTO local.dbinc_key FROM dual; RETURN local.dbinc_key; END checkIncarnation; PROCEDURE endIncarnationResync(high_kccdivts IN NUMBER, high_ic_recid IN NUMBER DEFAULT 0) IS BEGIN -- If incarnation records are resynced based on last_ic_recid, then they -- are treated as any other circular records. IF (last_ic_recid IS NOT NULL) THEN IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN -- set the high_ic_recid for the next resync UPDATE node SET high_ic_recid = endIncarnationResync.high_ic_recid, last_kccdivts = endIncarnationResync.high_kccdivts WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_ic_recid := high_ic_recid; last_ic_recid := NULL; ELSE IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET last_kccdivts = high_kccdivts WHERE site_key = this_site_key; END IF; END IF; -- recompute chain of incarnation recomputeDbincStatus(this_db_key, this_dbinc_key); END endIncarnationResync; /*-----------------------------* * Normal restore point Resync * *-----------------------------*/ FUNCTION beginRestorePointResync RETURN NUMBER IS BEGIN checkResync; IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN SELECT high_nrsp_recid INTO last_nrsp_recid FROM node WHERE site_key = this_site_key; ELSE last_nrsp_recid := sessionWaterMarks.high_nrsp_recid; END IF; RETURN last_nrsp_recid; END beginRestorePointResync; PROCEDURE checkRestorePoint( nrsp_recid IN NUMBER ,nrsp_stamp IN NUMBER ,nrsp_name IN VARCHAR2 ,reset_scn IN NUMBER ,reset_time IN DATE ,to_scn IN NUMBER ,nrsp_time IN DATE ,create_time IN DATE ,deleted IN NUMBER ) IS my_dbinc_key NUMBER; inscheck NUMBER; BEGIN IF (last_nrsp_recid IS NULL) THEN raise_application_error(-20037, 'Invalid last recid'); END IF; IF (nrsp_recid < last_nrsp_recid) THEN raise_application_error(-20036, 'Invalid record order'); END IF; IF (nrsp_recid > last_nrsp_recid + 1) THEN -- there is gap in deleted object records -- not sure what we should do here NULL; END IF; last_nrsp_recid := nrsp_recid; IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; IF (nrsp_stamp < kccdivts) THEN deb('checkRestorePoint - ignoring record kccdivts='||kccdivts); RETURN; -- obsolete record from a backup controlfile END IF; -- find the dbinc_key my_dbinc_key := checkIncarnation(reset_scn, reset_time); -- delete or insert records, there is no update IF (deleted = 1) THEN DELETE nrsp WHERE checkRestorePoint.nrsp_recid = nrsp_recid AND checkRestorePoint.nrsp_stamp = nrsp_stamp AND my_dbinc_key = nrsp.dbinc_key AND this_site_key = site_key; ELSE INSERT INTO nrsp (nrsp_recid ,nrsp_stamp ,rspname ,dbinc_key ,site_key ,to_scn ,rsptime ,creation_time ,long_term) VALUES (checkRestorePoint.nrsp_recid ,checkRestorePoint.nrsp_stamp ,checkRestorePoint.nrsp_name ,my_dbinc_key ,this_site_key ,checkRestorePoint.to_scn ,checkRestorePoint.nrsp_time ,checkRestorePoint.create_time ,NULL); -- UNKNOWN: cleanupNRS will reset to YES/NO END IF; EXCEPTION WHEN dup_val_on_index THEN deb('checkRestorePoint - Inside dup_val_on_index exception for' || ' recid ' || checkRestorePoint.nrsp_recid || ' stamp ' || checkRestorePoint.nrsp_stamp); SELECT min(nrsp.nrsp_recid) INTO inscheck FROM nrsp WHERE nrsp.nrsp_recid = checkRestorePoint.nrsp_recid AND nrsp.nrsp_stamp = checkRestorePoint.nrsp_stamp AND nrsp.dbinc_key = my_dbinc_key AND nrsp.site_key = this_site_key AND nrsp.rspname = checkRestorePoint.nrsp_name AND nrsp.to_scn = checkRestorePoint.to_scn; IF inscheck IS NULL THEN -- Some internal error to indicate no match raise_application_error(-20037, 'Invalid last recid'); END IF; RETURN; WHEN no_data_found THEN RETURN; WHEN others THEN RAISE; END checkRestorePoint; PROCEDURE endRestorePointResync(lowrecid IN number) IS lowscn number; BEGIN -- Save the low recid, so we can purge restore points in cleanup -- IF (lowrecid = 0) THEN low_nrsp_recid := NULL; ELSE low_nrsp_recid := lowrecid; END IF; -- Fix up last recid -- IF (this_cf_type = 'CURRENT' OR (this_cf_type = 'STANDBY' AND this_db_unique_name is not null)) THEN UPDATE node SET high_nrsp_recid = last_nrsp_recid WHERE site_key = this_site_key; END IF; sessionWaterMarks.high_nrsp_recid := last_nrsp_recid; last_nrsp_recid := NULL; END endRestorePointResync; PROCEDURE listScriptNames(glob IN number, allnames IN number) IS lglob number := NULL; lalln number := NULL; BEGIN deb('listScriptNames - List script Names called with glob: '|| nvl(to_char(glob), 'NULL')||'and allnames: '|| nvl(to_char(allnames), 'NULL')); IF glob = 1 then lglob := 1; END IF; IF allnames = 1 then lalln := 1; END IF; IF lscrnames_c%ISOPEN THEN deb('listScriptNames - Closing lscrnames_c cursor'); CLOSE lscrnames_c; END IF; deb('listScriptNames - Opening lscrnames_c cursor'); OPEN lscrnames_c(lglob, lalln); END listScriptNames; PROCEDURE getScriptNames(dbname OUT varchar2, scnm OUT varchar2, sccom OUT varchar2) IS ldum number := NULL; BEGIN IF NOT lscrnames_c%ISOPEN THEN raise_application_error(-20403, 'listScriptNames not done'); END IF; deb('getScriptNames - Fetching lscrnames_c cursor'); FETCH lscrnames_c INTO ldum, dbname, scnm, sccom; IF lscrnames_c%NOTFOUND THEN deb('getScriptNames - Closing lscrnames_c cursor'); CLOSE lscrnames_c; raise no_data_found; END IF; END getScriptNames; -- The procedure cleanupRSR (deletes) all RC_RMAN_STATUS row -- older than 90 days which do not have any corresponding pieces. PROCEDURE cleanupRSR IS nowTime date; BEGIN SELECT SYSDATE INTO nowTime from dual; IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; -- In this statement, the query defines the rows that must not be -- deleted. All other rows can be deleted. The outer join of rsr and -- other tables 'inverts' the selection, leaving just the rows that -- can be deleted. DELETE FROM rsr WHERE rsr_end < nowTime-60 AND rsr.dbinc_key IN (select dbinc_key from dbinc where dbinc.db_key = this_db_key); deb('cleanupRSR - deleted ' || sql%rowcount || ' rows from rsr table'); deb('cleanupRSR - took ' || ((sysdate - nowTime) * 86400) || ' seconds'); END cleanupRSR; -- The procedure cleanupROUT (deletes) all RC_RMAN_OUTPUT rows corresponding -- to job older than 30 days. PROCEDURE cleanupROUT IS start_time date; high_stamp number; high_session_key number; BEGIN IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; start_time := SYSDATE; high_stamp := date2stamp(start_time-7); SELECT max(rsr_key) into high_session_key FROM rsr, dbinc WHERE dbinc.db_key = this_db_key AND rsr.dbinc_key = dbinc.dbinc_key AND rsr.rsr_stamp < high_stamp; deb('cleanupROUT select took ' || ((sysdate - start_time) * 86400) || ' seconds'); -- Delete rows from rout table for jobs older than 7 days. If high_session_key IS NOT NULL THEN DELETE FROM rout WHERE db_key = this_db_key AND rout_skey <= high_session_key; deb('cleanupROUT deleted ' || sql%rowcount || ' rows from rout table'); END IF; deb('cleanupROUT took ' || ((sysdate - start_time) * 86400) || ' seconds'); END cleanupROUT; -- Clean up Normal Restore Points -- PROCEDURE cleanupNRS IS start_time date; BEGIN deb('cleanupNRS - low_nrsp_recid is ' || NVL(TO_CHAR(low_nrsp_recid), 'NULL')); start_time := SYSDATE; -- Restore points maintain a unique relationship with backups based on -- SCN values. Anytime a backup changes in a way that may affect the -- keep attribute, any restore points near or at that SCN value may be -- affected. The LONG_TERM column indicates when a restore point may be -- used to restore a keep backup. So when a backup is -- added/dropped/changed and has keep attributes, I call -- updateRestorePoint to set the LONG_TERM column to NULL (unknown). -- This routine, which runs after all the backups have been resync'd into -- the catalog, will run a complex query to set the LONG_TERM column -- correctly. -- If not for the RC_RESTORE_POINT view, LONG_TERM would only be important -- for rows below low_nrsp_recid UPDATE nrsp SET LONG_TERM = 'YES' WHERE long_term IS NULL AND this_site_key = site_key AND nrsp_recid in (SELECT nrsp.nrsp_recid FROM bs, brl, nrsp WHERE bs.bs_key = brl.bs_key AND bs.keep_options > 0 AND brl.low_scn <= nrsp.to_scn AND brl.next_scn > nrsp.to_scn AND this_site_key = bs.site_key AND this_site_key = nrsp.site_key UNION SELECT nrsp.nrsp_recid FROM xal, nrsp WHERE xal.keep_options > 0 AND xal.low_scn <= nrsp.to_scn AND xal.next_scn > nrsp.to_scn AND this_site_key = xal.site_key AND this_site_key = nrsp.site_key UNION SELECT nrsp_recid FROM bs, bdf, nrsp WHERE bs.bs_key = bdf.bs_key AND bs.keep_options > 0 AND bdf.ckp_scn = nrsp.to_scn+1 AND this_site_key = bs.site_key AND this_site_key = nrsp.site_key UNION SELECT nrsp_recid FROM xdf, nrsp WHERE xdf.keep_options > 0 AND xdf.ckp_scn = nrsp.to_scn+1 AND this_site_key = xdf.site_key AND this_site_key = nrsp.site_key); deb('cleanupNRS - updated ' || sql%rowcount || ' rows to LONG_TERM = YES'); -- Any unknown rows left are not long term. UPDATE nrsp SET LONG_TERM = 'NO' WHERE long_term IS NULL AND this_site_key = site_key; deb('cleanupNRS - updated ' || sql%rowcount || ' rows to LONG_TERM = NO'); -- Remove all restore points not in the catalog -- and not supporting a backup (long_term) DELETE nrsp WHERE nrsp_recid < low_nrsp_recid AND long_term = 'NO' AND site_key = this_site_key; low_nrsp_recid := NULL; deb('cleanupNRS - deleted ' || sql%rowcount || ' rows from nrsp table'); deb('cleanupNRS - took ' || ((sysdate - start_time) * 86400) || ' seconds'); END; PROCEDURE updateOldestFlashbackSCN ( oldest_flashback_scn IN NUMBER -- obsolete column ,oldest_flashback_time IN DATE DEFAULT NULL ) IS tmp NUMBER; BEGIN deb('updateOldestFlashbackSCN - guaranteed_flashback_scn=' || nvl(to_char(oldest_flashback_scn), 'NULL') || ' flashback_time=' || nvl(to_char(oldest_flashback_time), 'NULL')); -- no guaranteed restore point and no flashback time IF (oldest_flashback_scn IS NULL AND oldest_flashback_time IS NULL) THEN DELETE FROM fb WHERE db_unique_name = this_db_unique_name AND dbinc_key = this_dbinc_key; RETURN; END IF; BEGIN SELECT 0 INTO tmp FROM fb WHERE db_unique_name = this_db_unique_name AND dbinc_key = this_dbinc_key; EXCEPTION WHEN no_data_found THEN INSERT INTO fb (dbinc_key, db_unique_name, oldest_flashback_scn, oldest_flashback_time) VALUES (this_dbinc_key, this_db_unique_name, oldest_flashback_scn, oldest_flashback_time); RETURN; WHEN others THEN RAISE; END; UPDATE fb SET oldest_flashback_scn = updateOldestFlashbackSCN.oldest_flashback_scn, oldest_flashback_time = updateOldestFlashbackSCN.oldest_flashback_time WHERE db_unique_name = this_db_unique_name AND dbinc_key = this_dbinc_key; END updateOldestFlashbackSCN; FUNCTION getDbinc RETURN NUMBER IS BEGIN IF (this_dbinc_key IS NULL) THEN raise_application_error(-20020, 'Database incarnation not set'); END IF; RETURN this_dbinc_key; END getDbinc; -- This function will be used by old version of RMAN client. It should have -- actually been in PRVTRMNU.SQL, but for some reason was added here... So -- lets leave this alone FUNCTION isDuplicateRecord(recid IN NUMBER ,stamp IN NUMBER ,type IN VARCHAR2) RETURN BOOLEAN IS rec_count NUMBER; BEGIN checkResync; IF (type = 'AL') THEN SELECT count(*) INTO rec_count FROM al, dbinc WHERE dbinc.db_key = this_db_key AND al.dbinc_key = dbinc.dbinc_key AND isDuplicateRecord.recid = al.al_recid AND isDuplicateRecord.stamp = al.al_stamp AND al.site_key = this_site_key; ELSIF (type = 'BP') THEN SELECT count(*) INTO rec_count FROM bp WHERE bp.db_key = this_db_key AND isDuplicateRecord.recid = bp.bp_recid AND isDuplicateRecord.stamp = bp.bp_stamp AND bp.site_key = this_site_key; ELSIF (type = 'DC') THEN SELECT count(*) INTO rec_count FROM cdf, dbinc WHERE dbinc.db_key = this_db_key AND cdf.dbinc_key = dbinc.dbinc_key AND isDuplicateRecord.recid = cdf.cdf_recid AND isDuplicateRecord.stamp = cdf.cdf_stamp AND cdf.site_key = this_site_key; IF (rec_count = 0) THEN SELECT count(*) INTO rec_count FROM ccf, dbinc WHERE dbinc.db_key = this_db_key AND ccf.dbinc_key = dbinc.dbinc_key AND isDuplicateRecord.recid = ccf.ccf_recid AND isDuplicateRecord.stamp = ccf.ccf_stamp AND ccf.site_key = this_site_key; END IF; ELSE raise_application_error(-20999, 'Internal error in isDuplicateRecord(): bad type '|| type); END IF; IF rec_count > 0 THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END isDuplicateRecord; -- There are following cases when this function will return TRUE: -- 1) open resetlogs which resets watermark. -- 2) resync from controlfilecopy. -- 3) resync from backup controlfile (like BCV split mirror backup) -- 4) as a workaround for slow resync bug (as one reported in bug# 5899994) -- FUNCTION doDuplicateMining RETURN BOOLEAN IS last_recid number; BEGIN checkResync; IF (this_cf_type != 'CURRENT' and this_cf_type != 'STANDBY') THEN RETURN TRUE; END IF; -- For pre-10g database, the node table doesn't maintain watermarks -- for standby as db_unique_name wasn't present. IF (this_cf_type = 'STANDBY' and this_db_unique_name is NULL) THEN RETURN TRUE; END IF; -- just check one of the water marks to find out if water marks are -- maintained. If no water marks are maintained, then we need to do -- duplicate mining to find the last resync timestamp SELECT high_rlh_recid INTO last_recid FROM node WHERE site_key = this_site_key; IF (last_recid = 0) THEN deb('doDuplicateMining returns TRUE'); RETURN TRUE; ELSE RETURN FALSE; END IF; END doDuplicateMining; PROCEDURE unregisterSite(db_unique_name IN VARCHAR2, incbcks IN BINARY_INTEGER ) IS lsite_key number; db_role node.database_role%TYPE; BEGIN deb('unregisterSite - remove meta-data for node '|| db_unique_name); IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; -- disallow unregistering connected target database IF this_db_unique_name = upper(db_unique_name) THEN raise_application_error(-20244, db_unique_name || ' can not unregister connected target database'); END IF; -- check if site is known to the recovery catalog. BEGIN select site_key, database_role into lsite_key, db_role from node where node.db_unique_name = upper(unregisterSite.db_unique_name) and node.db_key = this_db_key; EXCEPTION WHEN no_data_found THEN raise_application_error( -20243, upper(unregisterSite.db_unique_name) || ' db_unique_name unknown to recovery catalog:'); END; -- remove the site owner ship for backups owned by site being un-registered IF incbcks <> 0 THEN delete bs WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from bs table'); delete bp WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from bp table'); delete ccf WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from ccf table'); delete xcf WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from xcf table'); delete cdf WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from cdf table'); delete xdf WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from xdf table'); delete xal WHERE site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from xal table'); ELSE update bs set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from bs table'); update bp set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from bp table'); update ccf set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from ccf table'); update xcf set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from xcf table'); update cdf set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from cdf table'); update xdf set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from xdf table'); update xal set site_key = NULL WHERE site_key = lsite_key; deb('updated ' || sql%rowcount || ' rows from xal table'); END IF; -- delete node row deletes entries from site_dfatt, al, site_tfatt, orl etc delete node where site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from node table'); delete fb where db_unique_name = unregisterSite.db_unique_name and dbinc_key in (select dbinc_key from dbinc where db_key = this_db_key); deb('deleted ' || sql%rowcount || ' rows from fb table'); -- remove connect identifier configuration for the site -- The code below assumes that the first character of "value" is a quote -- Use the first quote character to find the second quote character. delete conf where name = 'DB_UNIQUE_NAME' and db_key = this_db_key and upper(unregisterSite.db_unique_name) = upper(substr(value, 2, instr(substr(value, 2, 32), substr(value, 1,1))-1)) and db_unique_name is null; deb('deleted ' || sql%rowcount || ' rows from conf table(2)'); -- remote connect identifier configuration from all control file also on -- next resync. if sql%rowcount <> 0 then update node set force_resync2cf = 'YES' where db_key = this_db_key; end if; -- remove all site specific configuration delete conf where site_key = lsite_key; deb('deleted ' || sql%rowcount || ' rows from conf table (site rows)'); commit; END unregisterSite; ------------------------------- renameSite ------------------------------------ PROCEDURE renameSite(from_db_unique_name IN VARCHAR2, to_db_unique_name IN VARCHAR2) IS rec_count NUMBER; BEGIN deb('renameSite - rename meta-data from '|| from_db_unique_name || ' to ' || to_db_unique_name); IF (this_db_key IS NULL) THEN raise_application_error(-20021, 'Database not set'); END IF; -- disallow renaming connected target database IF this_db_unique_name = upper(from_db_unique_name) THEN raise_application_error(-20244, from_db_unique_name || ' can not rename connected target database'); END IF; -- check if from_db_unique_name is known to the recovery catalog. SELECT count(*) INTO rec_count FROM node WHERE node.db_unique_name = upper(from_db_unique_name) AND node.db_key = this_db_key; IF rec_count = 0 THEN raise_application_error(-20243, from_db_unique_name || ' site unknown to recovery catalog:'); END IF; -- ensure to_db_unique_name is not known to the recovery catalog. SELECT count(*) INTO rec_count FROM node WHERE node.db_unique_name = upper(to_db_unique_name) AND node.db_key = this_db_key; IF rec_count = 1 THEN raise_application_error(-20246, to_db_unique_name || ' site known to recovery catalog:'); END IF; UPDATE NODE SET db_unique_name = upper(to_db_unique_name) WHERE db_unique_name = upper(from_db_unique_name) AND db_key = this_db_key; deb('renamed db_unique_name ' || sql%rowcount || ' row updated'); UPDATE CONF SET db_unique_name = upper(to_db_unique_name) WHERE db_unique_name = upper(from_db_unique_name) AND db_key = this_db_key; deb('updated ' || sql%rowcount || ' rows in conf table'); -- TODO, remove db_unique_name from flashback table UPDATE FB SET db_unique_name = upper(to_db_unique_name) WHERE db_unique_name = upper(from_db_unique_name) AND dbinc_key IN (select dbinc_key from dbinc where db_key = this_db_key); deb('updated ' || sql%rowcount || ' rows in fb table'); COMMIT; END renameSite; -- The following procedure stores a new entry in the node table -- if it doesn't already exist PROCEDURE resyncAddDBUname(cdbunstr IN varchar2) IS dbuname varchar2(30); numentries number; BEGIN -- The cdbunstr contains a string of the form: -- " CONNECT IDENTIFIER ". -- The connect_id may be in either single or double quotes. -- the db_unique_name may be either single or double quotes. -- We have to extract the unquoted db_unique_name and look it up -- in the node table for our db_id. -- If it doesn't exist, then we have to add an entry for this -- db_unique_name to the node table - note that not all columns are -- populated. We assume that this is a STANDBY node, since generic -- configuration changes can only be made at the primary. -- deb('resyncAddDBUname - cdbunstr = '|| cdbunstr); dbuname := substr(cdbunstr, 2, 30); -- strip out the first quote -- strip out the second quote. Assumes that the FIRST character -- in cdbunstr is a quote character. deb('resyncAddDBUname - dbuname before = '|| dbuname); dbuname := substr(dbuname, 1, instr(dbuname, substr(cdbunstr,1,1))-1); deb('resyncAddDBUname - db_unique_name = '|| dbuname); -- Add a new entry as a STANDBY site. If the entry already exists, -- we will get a constraint violation exception which we will ignore. insert into node (db_unique_name, db_key, force_resync2cf, database_role, site_key) values(upper(dbuname), this_db_key, 'YES', 'STANDBY', rman_seq.nextval); deb('resyncAddDBUname - Added entry to node table with value ' || dbuname); EXCEPTION WHEN dup_val_on_index THEN -- we already have an entry. nothing to do RETURN; END resyncAddDBUname; ------------------------------- getThisSiteKey -------------------------------- FUNCTION getThisSiteKey(db_unique_name in VARCHAR2 DEFAULT NULL) return NUMBER IS ret_site_key number; BEGIN deb('getThisSiteKey - This site key is '||this_site_key); if db_unique_name is not null then ret_site_key := dbms_rcvman.getSiteKey(db_unique_name); else ret_site_key := this_site_key; end if; deb('Returning site key is '||ret_site_key); return ret_site_key; END getThisSiteKey; PROCEDURE enableResyncActions IS BEGIN deb('enableResyncActions - resync action tracing enabled'); doResyncReasons := TRUE; END enableResyncActions; PROCEDURE setReason(reason IN number, forceSet IN boolean default FALSE) IS BEGIN IF doResyncReasons THEN -- We only set the reason if we do reason is NONE or asked to forcefully -- set it IF resync_reason = RESYNC_REASON_NONE OR forceSet THEN resync_reason := reason; deb('setReason - resync_reason: '||to_char(resync_reason)); END IF; ELSE resync_reason := RESYNC_REASON_NOACTION; END IF; END setReason; FUNCTION getReason RETURN number IS BEGIN IF doResyncReasons THEN deb('getReason - resync_reason: '||to_char(resync_reason)); RETURN resync_reason; ELSE RETURN RESYNC_REASON_NOACTION; END IF; END getReason; PROCEDURE incResyncActions(action IN number, objno IN number, objname IN varchar2) IS BEGIN IF not doResyncReasons THEN RETURN; END IF; deb('incResynActions - for '||to_char(action)||' objno '|| nvl(to_char(objno), 'IS NULL')||' objname '||nvl(objname, 'IS NULL'), RCVCAT_LEVEL_HI); IF debOK(RCVCAT_LEVEL_HI) THEN dumpResyncActions; END IF; IF not fullResyncAction.active THEN RETURN; END IF; IF objno is NOT NULL THEN IF fullResyncAction.lastobjno = objno THEN IF fullResyncAction.actTaken(action) THEN -- we have already done this object, ignore deb('incResyncActions - '|| RESYNC_ACTION_OBJECTS(fullResyncAction.objtype)||' '|| to_char(objno)||' already '|| RESYNC_ACTION_NAMES(action), RCVCAT_LEVEL_HI); RETURN; ELSE fullResyncAction.actTaken(action) := TRUE; END IF; ELSE -- new objno fullResyncAction.lastobjno := objno; fullResyncAction.actTaken := resyncActionTaken_t(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE); fullResyncAction.actTaken(action) := TRUE; END IF; END IF; fullResyncAction.actCount(action) := fullResyncAction.actCount(action) + 1; fullResyncAction.valid := TRUE; IF objno is NOT NULL THEN IF objname is NOT NULL THEN deb('incResyncActions - '|| RESYNC_ACTION_OBJECTS(fullResyncAction.objtype)||' '|| objname||'('||to_char(objno)||') '|| RESYNC_ACTION_NAMES(action), RCVCAT_LEVEL_HI); ELSE deb('incResyncActions - '|| RESYNC_ACTION_OBJECTS(fullResyncAction.objtype)||' '|| to_char(objno)||' '|| RESYNC_ACTION_NAMES(action), RCVCAT_LEVEL_HI); END IF; ELSE deb('incResyncActions - '|| RESYNC_ACTION_OBJECTS(fullResyncAction.objtype)||' '|| to_char(objname)||' '|| RESYNC_ACTION_NAMES(action), RCVCAT_LEVEL_HI); END IF; deb('incResyncActions - Exiting', RCVCAT_LEVEL_HI); END incResyncActions; PROCEDURE dumpResyncActions IS i number; BEGIN IF not doResyncReasons OR not debOK(RCVCAT_LEVEL_HI) THEN RETURN; END IF; deb('dumpResyncActions - resync_reason: '||to_char(nvl(resync_reason, -1))); IF resync_reason = RESYNC_REASON_NOACTION THEN RETURN; END IF; IF fullResyncAction.active THEN deb('dumpResyncActions - Container is active'); ELSE deb('dumpResyncActions - Container is NOT active'); END IF; IF fullResyncAction.valid THEN deb('dumpResyncActions - Container is valid'); ELSE deb('dumpResyncActions - Container is NOT valid'); END IF; IF fullResyncAction.objtype IS NOT NULL THEN deb('dumpResyncActions - objtype: '|| RESYNC_ACTION_OBJECTS(fullResyncAction.objtype)); ELSE deb('dumpResyncActions - objtype is NULL'); END IF; IF fullResyncAction.lastobjno IS NOT NULL THEN deb('dumpResyncActions - lastobjno: '|| to_char(fullResyncAction.lastobjno)); ELSE deb('dumpResyncActions - lastobjno is NULL'); END IF; FOR i IN 1..6 LOOP IF fullResyncAction.actTaken(i) THEN deb('dumpResyncActions - '||RESYNC_ACTION_NAMES(i)||' TRUE - '|| fullResyncAction.actCount(i)); ELSE deb('dumpResyncActions - '||RESYNC_ACTION_NAMES(i)||' FALSE - '|| fullResyncAction.actCount(i)); END IF; END LOOP; END dumpResyncActions; PROCEDURE getResyncActions(valid OUT boolean ,added OUT number ,dropped OUT number ,changed OUT number ,recreated OUT number ,renamed OUT number ,resized OUT number) IS BEGIN IF doResyncReasons THEN IF debOK(RCVCAT_LEVEL_HI) THEN deb('getResyncActions - called', RCVCAT_LEVEL_HI); dumpResyncActions; END IF; fullResyncAction.active := FALSE; valid := fullResyncAction.valid; fullResyncAction.valid := FALSE; added := fullResyncAction.actCount(RESYNC_ACTION_ADD); dropped := fullResyncAction.actCount(RESYNC_ACTION_DROP); changed := fullResyncAction.actCount(RESYNC_ACTION_CHANGE); recreated := fullResyncAction.actCount(RESYNC_ACTION_RECREATE); renamed := fullResyncAction.actCount(RESYNC_ACTION_RENAME); resized := fullResyncAction.actCount(RESYNC_ACTION_RESIZE); setReason(RESYNC_REASON_NONE, TRUE); ELSE setReason(RESYNC_REASON_NOACTION, TRUE); END IF; END getResyncActions; PROCEDURE clearResyncActions IS BEGIN fullResyncAction.active := FALSE; fullResyncAction.valid := FALSE; fullResyncAction.lastobjno := -1; fullResyncAction.objtype := NULL; fullResyncAction.actTaken := resyncActionTaken_t(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE); fullResyncAction.actCount := resyncActionCounts_t(0, 0, 0, 0, 0, 0); dumpResyncActions; END clearResyncActions; /*-------------------------------------------------* * Private functions for import catalog processing * *-------------------------------------------------*/ -- -- adjustRmanSeq - adjust rman sequence -- -- It reserves the rman_seq value for importing the source recovery -- catalog database into this schema. -- PROCEDURE adjustRmanSeq IS currval number; newval number; incseq number; BEGIN LOOP SELECT rman_seq.nextval INTO currval FROM dual; EXECUTE IMMEDIATE 'SELECT rman_seq.nextval@' || import_dblink || ' FROM dual' INTO incseq; EXECUTE IMMEDIATE 'ALTER SEQUENCE rman_seq INCREMENT BY ' || incseq; SELECT (rman_seq.nextval - incseq) INTO import_offset FROM dual; EXECUTE IMMEDIATE 'ALTER SEQUENCE rman_seq INCREMENT BY 1'; -- If i don't see the import_offset >= currval, then some other process -- concurrently executed this function and changed rman_seq -- increment counter before i did select into import_offset. So, retry -- again. EXIT WHEN (import_offset >= currval); END LOOP; END adjustRmanSeq; -- -- isColumnASeq - is column type genearted by rman sequence -- -- Basically checks if the column name ends with KEY and is one of listed -- key_columns. If so, return TRUE. Otherwise, FALSE. Used to offset -- the key column values when importing catalog schema. -- FUNCTION isColumnASeq( column_name IN varchar2) RETURN BOOLEAN IS BEGIN IF (column_name like '%KEY') THEN FOR i in 1..key_columns.count LOOP IF (key_columns(i) = column_name) THEN RETURN TRUE; END IF; END LOOP; -- You are here because you defined a column that ends with -- KEY but forgot to add it to key_columns table. If your -- column value isn't generated by rman_seq, then you need -- to rename the column name so that it doesn't end with KEY. raise_application_error(-20999, 'Internal error in ' || 'isColumnASeq(): bad column '|| column_name); END IF; RETURN FALSE; END isColumnASeq; -- -- getColumnName - get column name -- -- Given a table name and offset, this function returns the column name -- separated by comma and ordered by name. If a offset is provided, then -- adds the offset to the column name. -- FUNCTION getColumnName( table_name IN varchar2 ,offset IN number DEFAULT NULL) RETURN VARCHAR2 IS v_table user_objects.object_name%TYPE; v_column varchar2(1024) := to_char(null); isaseq boolean; CURSOR column_c(tname varchar2) IS SELECT column_name, data_type FROM user_tab_columns WHERE table_name = tname ORDER BY column_name; FUNCTION add_comma(v_column IN varchar2) RETURN varchar2 IS BEGIN IF (v_column is null) THEN RETURN null; ELSE RETURN ','; END IF; END add_comma; FUNCTION add_offset( offset IN number ,data_type IN varchar2 ,column_name IN varchar2) RETURN varchar2 IS BEGIN IF (offset is null) THEN RETURN null; END IF; IF (data_type = 'NUMBER' AND isColumnASeq(column_name)) THEN RETURN '+' || offset; END IF; RETURN null; END add_offset; BEGIN SELECT object_name INTO v_table FROM user_objects WHERE object_name = upper(table_name) AND object_type = 'TABLE'; -- form the column names in ascending order FOR cRec in column_c(v_table) LOOP v_column := v_column || add_comma(v_column) || table_name || '.' || cRec.column_name || add_offset(offset, cRec.data_type, cRec.column_name); END LOOP; RETURN v_column; END getColumnName; -- -- importTable - import content of table into recovery catalog database. -- -- Given a table name and offset, this function extracts the rows from -- the source recovery catalog database and inserts the rows into -- recovery catalog database incrementing the value of key columns -- by the offset provided. -- -- from_table and where_clause is used to join table_name in order to -- filter interested columns. Usulaly, from_table contains idb or -- idbinc table name and the where_clause should contain how to -- join those tables with from_table. -- -- Example -- If there is a table named foo with columns (db_key, myname), then -- you have to import it by joining idb using db_key with idb.db_key. -- So, the importTable usage will look like. -- -- from_table.delete; -- from_table(1) := idb; -- importTable(table_name => 'foo' -- ,from_table => from_table -- ,uniq_rows => FALSE -- ,where_clause => 'where foo.db_key = ' || idb || '.db_key' -- ); -- PROCEDURE importTable( table_name IN varchar2 ,from_table IN ts_name_list ,uniq_rows IN boolean ,where_clause IN varchar2) IS insert_columns varchar2(2048); from_columns varchar2(2048); source_table varchar2(2048); uniq_keyword varchar2(8); start_time date; BEGIN deb('Entering importTable table=' || table_name); start_time := sysdate; insert_columns := getColumnName(table_name); from_columns := getColumnName(table_name, import_offset); source_table := table_name || '@' || import_dblink; FOR i in 1..from_table.count LOOP source_table := source_table || ', ' || from_table(i) || '@' || import_dblink; END LOOP; IF (uniq_rows) THEN uniq_keyword := 'DISTINCT'; ELSE uniq_keyword := ''; END IF; -- -- Basically doing- -- -- INSERT INTO table_name(column1, column2, column3) -- (SELECT DISTINCT column1+offset, column2+offset, column3+offset -- FROM table_name@dblink, from_table@dblink -- where_clause); -- EXECUTE IMMEDIATE 'INSERT INTO ' || table_name || ' (' || insert_columns || ')' || ' (SELECT ' || uniq_keyword || ' ' || from_columns || ' FROM ' || source_table || ' ' || where_clause || ')'; deb('imported rows = ' || sql%rowcount); deb('importTable took ' || ((sysdate - start_time) * 86400) || ' seconds'); deb('Finished importTable table=' || table_name); END importTable; -- -- registerImportDb - register all the database that has to be -- imported in recovery catalog database. -- -- Create db, dbinc entries for the database that has to be imported. -- This can't be done using importTable because db and dbinc are -- dependent on each other (see constraint db_f1). -- PROCEDURE registerImportDb( idb IN varchar2 ,idbinc IN varchar2) IS TYPE cur_typ IS ref CURSOR; update_c cur_typ; from_table ts_name_list; from_columns varchar2(2048); currkeys numTab_t; dbids numTab_t; dbkeys numTab_t; BEGIN from_columns := getColumnName('db', import_offset); deb('Entering registerImportDb'); -- -- Basically extracing db_key and db_id from source database. -- -- INSERT INTO db(db.db_key, db.db_id) -- (SELECT db.db_key+offset, db.db_id+offset -- FROM db@dblink, idb@dblink -- WHERE db.db_key = idb.db_key); -- EXECUTE IMMEDIATE 'INSERT INTO db (db.db_key, db.db_id)' || ' (SELECT db.db_key + ' || import_offset ||' , db.db_id' || ' FROM db@' || import_dblink || ',' || idb || '@' || import_dblink || ' WHERE db.db_key = ' || idb || '.db_key)'; deb('Total db imported = ' || sql%rowcount); -- Now import all the dbinc keys from_table.delete; from_table(1) := idbinc; importTable(table_name => 'dbinc' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where dbinc.dbinc_key = ' || idbinc || '.dbinc_key'); -- Set the curr_dbinc_key in db table. We do this using -- bulk update. OPEN update_c FOR -- -- Extract curr_dbinc_keys from source database. Basically doing -- -- SELECT (column1, column2, column2) -- FROM db@dblink, idb@dblink -- WHERE db.db_key = idb.db_key; -- 'SELECT ' || from_columns || ' FROM db@' || import_dblink || ',' || idb || '@' || import_dblink || ' WHERE db.db_key = ' || idb || '.db_key'; FETCH update_c BULK COLLECT INTO currkeys, dbids, dbkeys; CLOSE update_c; -- Now update all curr_dbinc_key in one shot. FORALL i in 1..dbids.count UPDATE db SET curr_dbinc_key = currkeys(i) WHERE db.db_key = dbkeys(i); deb('Finished registerImportDb'); EXCEPTION WHEN dup_val_on_index THEN raise_application_error(-20512, 'Database already registered'); END registerImportDb; -- -- dropTempResource - drop the object and also delete the entry -- from tempres table. -- -- Get a rowlock on the tempres table that has this name. If -- lock cannot be obtained, then return. Othewise, drop the object -- and delete the row from tempres. -- PROCEDURE dropTempResource( name IN varchar2 ,data_type IN varchar2) IS dblink_not_found exception; dblink_not_open exception; resource_not_found exception; table_not_found exception; pragma exception_init(dblink_not_found,-2024); pragma exception_init(dblink_not_open,-2081); pragma exception_init(resource_not_found, -20509); pragma exception_init(table_not_found, -942); BEGIN deb('Entering dropTempResource name = ' || name || ' , data_type = '|| data_type); -- commit in order to release any locks that are held. COMMIT; -- lock the resource that is about to be dropped IF (NOT lockTempResource(name, data_type)) THEN deb('Finished dropTempResource - resource busy'); RETURN; END IF; IF data_type = 'TABLE' THEN EXECUTE IMMEDIATE 'DROP TABLE ' || name; ELSIF data_type = 'DBLINK' THEN BEGIN EXECUTE IMMEDIATE 'ALTER SESSION CLOSE DATABASE LINK ' || name; EXCEPTION WHEN dblink_not_open THEN NULL; END; EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || name; END IF; -- remove this temporary resource DELETE FROM tempres WHERE tempres.name = dropTempResource.name; COMMIT; deb('Finished dropTempResource'); EXCEPTION WHEN resource_not_found THEN DELETE FROM tempres WHERE tempres.name = dropTempResource.name; COMMIT; deb('Finished dropTempResource - resource_not_found ' || name); WHEN dblink_not_found THEN deb('Finished dropTempResource - dblink_not_found' || name); WHEN table_not_found THEN deb('Finished dropTempResource - table_not_found' || name); WHEN others THEN dbms_output.put_line('caught exception during dropTempResource ' || substr(sqlerrm, 1, 512)); END dropTempResource; -- -- importGlobalScript - import global script -- -- importing global script is tricky because its db_key is NULL. So, -- there could be a unique constraint violation when such name already -- exists. This function appends COPYOF(copyno) to the name when -- unique constraint is violated. -- PROCEDURE importGlobalScript IS type cur_typ is ref cursor; global_scr_c cur_typ; global_scr_q varchar2(1024); local scr%rowtype; given_name scr.scr_name%type; from_table ts_name_list; from_columns varchar2(2048); copycnt number; unique_violated exception; pragma exception_init(unique_violated, -1); BEGIN -- cursor to get global script information from_columns := getColumnName('scr'); global_scr_q := 'SELECT ' || from_columns || ' FROM scr@' || import_dblink || ' WHERE db_key IS NULL'; OPEN global_scr_c FOR global_scr_q; LOOP FETCH global_scr_c INTO local.db_key, local.scr_comment, local.scr_key, local.scr_name; EXIT WHEN global_scr_c%NOTFOUND; copycnt := 0; given_name := local.scr_name; <> BEGIN -- Basically doing -- -- INSERT INTO scr(column1, column2,...) VALUES -- (db_key, scr_comment, scr_key+offset, scr_key); -- EXECUTE IMMEDIATE 'INSERT INTO scr (' || from_columns || ') VALUES ' || '( null,' || case when local.scr_comment is not null then '''' || local.scr_comment || ''',' else 'null,' end || local.scr_key || '+' || import_offset || ',' || '''' || local.scr_name || ''')'; EXCEPTION WHEN unique_violated THEN -- Now change the name to COPY OF and retry again copycnt := copycnt + 1; IF (copycnt = 1) THEN local.scr_name := 'COPY OF ' || given_name; ELSE local.scr_name := 'COPY(' || copycnt || ') OF ' || given_name; END IF; goto tryagain; END; END LOOP; -- Import global script text from_table.delete; from_table(1) := 'scr'; importTable(table_name => 'scrl' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where scr.db_key is null' || ' and scr.scr_key = scrl.scr_key'); END importGlobalScript; /*------------------------------------------------* * Public functions for import catalog processing * *------------------------------------------------*/ -- -- createTempResource - create temporary resource -- -- Inserts the given name and data_type to tempres table. -- PROCEDURE createTempResource( name IN varchar2 ,data_type IN varchar2) IS unique_violated exception; pragma exception_init(unique_violated ,-1); BEGIN -- add this temporary resource INSERT INTO tempres (name, data_type) VALUES (name, data_type); COMMIT; EXCEPTION WHEN unique_violated THEN raise_application_error(-20508, 'resource already in use ' || name); END createTempResource; -- -- lockTempResource - lock temporary resource -- -- obtain a rowlock on the tempres for the given name. Return TRUE -- if the object is found and able to obtain the lock. Otherwise, FALSE. -- FUNCTION lockTempResource( name IN varchar2 ,data_type IN varchar2) RETURN BOOLEAN IS local tempres%ROWTYPE; found number; resource_busy exception; pragma exception_init(resource_busy, -54); BEGIN deb('Entering lockTempResource ' || name); SELECT name, data_type INTO local.name, local.data_type FROM tempres WHERE name = lockTempResource.name FOR UPDATE NOWAIT; IF (data_type = 'TABLE') THEN SELECT count(*) INTO found FROM user_tab_columns WHERE table_name = lockTempResource.name; ELSIF (data_type = 'DBLINK') THEN SELECT count(*) INTO found FROM user_db_links WHERE db_link = lockTempResource.name; ELSE raise_application_error(-20999, 'Internal error in localTempResource(): bad data_type '|| data_type); END IF; IF (found = 0) THEN deb('Finished lockTempResource with resource not found'); raise_application_error(-20509, 'resource not found ' || name); END IF; deb('Finished lockTempResource'); RETURN TRUE; EXCEPTION WHEN resource_busy THEN deb('Finished lockTempResource with resource_busy'); RETURN FALSE; WHEN no_data_found THEN deb('Finished lockTempResource with no_data_found'); RETURN FALSE; END lockTempResource; -- -- cleanupTempResource - cleanup temporary resource -- -- drop all objects in the tempres table and delete the row as well. -- PROCEDURE cleanupTempResource IS CURSOR temp_c IS SELECT name, data_type FROM tempres; BEGIN FOR tempRec in temp_c LOOP dropTempResource(tempRec.name, tempRec.data_type); END LOOP; END cleanupTempResource; -- -- addDbidToImport - add dbid to idb and idbinc table whose -- information has to be imported. -- -- This is executed on source catalog database. Obtain the lock on -- the temporary resource that was allocated for idb and idbinc, then -- add the dbid to the idb and idbinc table. -- PROCEDURE addDbidToImport( first IN binary_integer ,idb IN varchar2 ,idbinc IN varchar2 ,dbid IN number DEFAULT NULL ,dbname IN varchar2 DEFAULT NULL) IS dummy tempres.name%TYPE; ldbid db.db_id%TYPE := dbid; dbid_exists number; BEGIN -- lock the resource whose content is about to be changed IF (NOT lockTempResource(idb, 'TABLE')) THEN raise_application_error(-20508, 'resource already in use ' || idb); ELSIF (NOT lockTempResource(idbinc, 'TABLE')) THEN raise_application_error(-20508, 'resource already in use ' || idbinc); END IF; IF (dbid IS NULL AND dbname IS NULL) THEN EXECUTE IMMEDIATE 'INSERT INTO ' || idb || '(db_key, db_id) ' || '(SELECT db_key, db_id FROM db)'; IF (sql%rowcount = 0) THEN raise_application_error(-20510, 'import database not found'); END IF; EXECUTE IMMEDIATE 'INSERT INTO ' || idbinc || '(dbinc_key) ' || '(SELECT dbinc_key ' || ' FROM dbinc, ' || idb || ' WHERE dbinc.db_key = ' || idb ||'.db_key)'; COMMIT; RETURN; ELSIF (dbname IS NOT NULL) THEN BEGIN SELECT db.db_id INTO ldbid FROM db, dbinc WHERE db.curr_dbinc_key = dbinc.dbinc_key AND dbinc.db_name = upper(addDbidtoImport.dbname); EXCEPTION WHEN no_data_found THEN raise_application_error(-20510, 'import database not found'); WHEN too_many_rows THEN raise_application_error(-20511, 'import database name is ambiguous'); END; ELSE BEGIN SELECT db.db_id INTO ldbid FROM db WHERE db.db_id = ldbid; EXCEPTION WHEN no_data_found THEN raise_application_error(-20510, 'import database not found'); END; END IF; -- check whether all dbid that was previous added exists in idb -- table. Otherwise, some other process reused our table name. IF (first = 0) THEN FOR i in 1..import_dbid.count LOOP EXECUTE IMMEDIATE 'SELECT count(*) FROM ' || idb || ' WHERE ' || idb || '.db_id =' || import_dbid(i) INTO dbid_exists; IF (dbid_exists != 1) THEN raise_application_error(-20508, 'resource already in use ' || idb); END IF; END LOOP; EXECUTE IMMEDIATE 'SELECT count(*) FROM ' || idb INTO dbid_exists; IF (dbid_exists != import_dbid.count) THEN raise_application_error(-20508, 'resource already in use ' || idb); END IF; import_dbid(import_dbid.count + 1) := ldbid; ELSE import_dbid.delete; import_dbid(1) := ldbid; END IF; EXECUTE IMMEDIATE 'INSERT INTO ' || idb || '(db_key, db_id) ' || '(SELECT db_key, db_id FROM db ' || ' WHERE db_id = ' || ldbid || ')'; EXECUTE IMMEDIATE 'INSERT INTO ' || idbinc || '(dbinc_key) ' || '(SELECT dbinc_key ' || ' FROM dbinc, ' || idb || ' WHERE dbinc.db_key = ' || idb || '.db_key ' || ' AND ' || idb || '.db_id = ' || ldbid || ')'; COMMIT; -- lock the resource table once again IF (NOT lockTempResource(idb, 'TABLE')) THEN raise_application_error(-20508, 'resource already in use ' || idb); ELSIF (NOT lockTempResource(idbinc, 'TABLE')) THEN raise_application_error(-20508, 'resource already in use ' || idbinc); END IF; END addDbidToImport; -- -- lockDbidToImport - lock the db that has to imported on source db -- -- This is executed on source recovery catalog database. Basically, -- hold the rowlock on db table so that no resync happens during -- import. -- PROCEDURE lockDbidToImport( idb IN varchar2) IS TYPE cur_typ IS ref CURSOR; idb_c cur_typ; idb_q varchar2(512); local_db_key db.db_key%TYPE; local_db_id db.db_key%TYPE; BEGIN idb_q := 'SELECT db_key FROM ' || idb; OPEN idb_c FOR idb_q; LOOP FETCH idb_c INTO local_db_key; EXIT WHEN idb_c%NOTFOUND; SELECT db_id INTO local_db_id FROM db WHERE db.db_key = local_db_key FOR UPDATE; END LOOP; END lockDbidToImport; -- -- importSchema - import catalog schema -- -- main routine for IMPORT CATALOG command. Goes through each catalog -- table and imports its information. -- PROCEDURE importSchema( dblink IN varchar2 ,idb IN varchar2 ,idbinc IN varchar2) IS from_table ts_name_list; BEGIN import_dblink := dblink; adjustRmanSeq; registerImportDb(idb, idbinc); -- import rman configuration from_table.delete; from_table(1) := idb; importTable(table_name => 'conf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where conf.db_key = ' || idb || '.db_key'); -- import node from_table.delete; from_table(1) := idb; importTable(table_name => 'node' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where node.db_key = ' || idb || '.db_key'); -- import checkpoint from_table.delete; from_table(1) := idbinc; importTable(table_name => 'ckp' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where ckp.dbinc_key = ' || idbinc || '.dbinc_key'); -- import tablespace from_table.delete; from_table(1) := idbinc; importTable(table_name => 'ts' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where ts.dbinc_key = ' || idbinc || '.dbinc_key'); -- import tablespace attribute from_table.delete; from_table(1) := idbinc; importTable(table_name => 'tsatt' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where tsatt.dbinc_key = ' || idbinc || '.dbinc_key'); -- import datafile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'df' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where df.dbinc_key = ' || idbinc || '.dbinc_key'); -- import datafile attribute from_table.delete; from_table(1) := idbinc; from_table(2) := 'df'; importTable(table_name => 'site_dfatt' ,from_table => from_table ,uniq_rows => TRUE ,where_clause => 'where df.dbinc_key = ' || idbinc || '.dbinc_key' || ' and site_dfatt.df_key = df.df_key'); -- import offline range from_table.delete; from_table(1) := idbinc; importTable(table_name => 'offr' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where offr.dbinc_key = ' || idbinc || '.dbinc_key'); -- import tempfile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'tf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where tf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import tempfile attribute from_table.delete; from_table(1) := idbinc; from_table(2) := 'tf'; importTable(table_name => 'site_tfatt' ,from_table => from_table ,uniq_rows => TRUE ,where_clause => 'where tf.dbinc_key = ' || idbinc || '.dbinc_key' || ' and site_tfatt.tf_key = tf.tf_key'); -- import redo range from_table.delete; from_table(1) := idbinc; importTable(table_name => 'rr' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where rr.dbinc_key = ' || idbinc || '.dbinc_key'); -- import redo thread from_table.delete; from_table(1) := idbinc; importTable(table_name => 'rt' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where rt.dbinc_key = ' || idbinc || '.dbinc_key'); -- import online redo log from_table.delete; from_table(1) := idbinc; importTable(table_name => 'orl' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where orl.dbinc_key = ' || idbinc || '.dbinc_key'); -- import redo log history from_table.delete; from_table(1) := idbinc; importTable(table_name => 'rlh' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where rlh.dbinc_key = ' || idbinc || '.dbinc_key'); -- import archived log from_table.delete; from_table(1) := idbinc; importTable(table_name => 'al' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where al.dbinc_key = ' || idbinc || '.dbinc_key'); -- import backupset from_table.delete; from_table(1) := idb; importTable(table_name => 'bs' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bs.db_key = ' || idb || '.db_key'); -- import backuppiece from_table.delete; from_table(1) := idb; importTable(table_name => 'bp' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bp.db_key = ' || idb || '.db_key'); -- import backup controlfile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'bcf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bcf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import copy controlfile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'ccf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where ccf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import proxy controlfile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'xcf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where xcf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import backup spfile from_table.delete; from_table(1) := idb; importTable(table_name => 'bsf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bsf.db_key = ' || idb || '.db_key'); -- import backup datafile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'bdf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bdf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import backup controlfile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'cdf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where cdf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import proxy datafile from_table.delete; from_table(1) := idbinc; importTable(table_name => 'xdf' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where xdf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import proxy archivelog from_table.delete; from_table(1) := idbinc; importTable(table_name => 'xal' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where xal.dbinc_key = ' || idbinc || '.dbinc_key'); -- import backup redolog from_table.delete; from_table(1) := idbinc; importTable(table_name => 'brl' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where brl.dbinc_key = ' || idbinc || '.dbinc_key'); -- import backup corruption from_table.delete; from_table(1) := idbinc; from_table(2) := 'bdf'; importTable(table_name => 'bcb' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bdf.bdf_key = bcb.bdf_key' || ' and bdf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import copy corruption from_table.delete; from_table(1) := idbinc; from_table(2) := 'cdf'; importTable(table_name => 'ccb' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where cdf.cdf_key = ccb.cdf_key' || ' and cdf.dbinc_key = ' || idbinc || '.dbinc_key'); -- import rman status row from_table.delete; from_table(1) := idbinc; importTable(table_name => 'rsr' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where rsr.dbinc_key= ' ||idbinc|| '.dbinc_key'); -- import stored script from_table.delete; from_table(1) := idb; importTable(table_name => 'scr' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where scr.db_key = ' || idb || '.db_key'); -- import stored script line from_table.delete; from_table(1) := idb; from_table(2) := 'scr'; importTable(table_name => 'scrl' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where scr.db_key = ' || idb || '.db_key' || ' and scr.scr_key = scrl.scr_key'); -- import global script importGlobalScript; -- import rman output from_table.delete; from_table(1) := idb; importTable(table_name => 'rout' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where rout.db_key = ' || idb || '.db_key'); --Ignore importing config table because it is obsolete. The --values aren't interpreted by rman command. Just exists so that --configure compatible command doesn't throw error in 8.1.6 or lower. -- import flashback from_table.delete; from_table(1) := idbinc; importTable(table_name => 'fb' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where fb.dbinc_key = '|| idbinc || '.dbinc_key'); -- import grsp from_table.delete; from_table(1) := idbinc; importTable(table_name => 'grsp' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where grsp.dbinc_key = '|| idbinc || '.dbinc_key'); -- import bcr from_table.delete; from_table(1) := idb; from_table(2) := 'node'; importTable(table_name => 'bcr' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where bcr.site_key = node.site_key' || ' and node.db_key = ' || idb || '.db_key'); -- import nrsp from_table.delete; from_table(1) := idbinc; importTable(table_name => 'nrsp' ,from_table => from_table ,uniq_rows => FALSE ,where_clause => 'where nrsp.dbinc_key = '|| idbinc || '.dbinc_key'); -- -- Add your new catalog table here -- -- Example -- If there is a table named foo with columns (db_key, myname), then -- you have to import it by joining idb using db_key with idb.db_key. -- So, the importTable usage will look like. -- -- from_table.delete; -- from_table(1) := idb; -- importTable(table_name => 'foo' -- ,from_table => from_table -- ,uniq_rows => FALSE -- ,where_clause => 'where foo.db_key = ' || idb || '.db_key' -- ); -- COMMIT; EXCEPTION WHEN others THEN ROLLBACK; RAISE; END importSchema; -- -- unregisterDatabase - another flavor unregister database -- -- Given the idb table, unregister all the database from source recovery -- catalog database. -- PROCEDURE unregisterDatabase( idb IN varchar2) IS TYPE cur_typ IS ref CURSOR; idb_c cur_typ; idb_q varchar2(512); local_db_id db.db_id%TYPE; BEGIN idb_q := 'SELECT db_id FROM ' || idb; OPEN idb_c FOR idb_q; LOOP FETCH idb_c INTO local_db_id; EXIT WHEN idb_c%NOTFOUND; unregisterDatabase(db_id => local_db_id); END LOOP; END unregisterDatabase; /*----------------------------* * Virtual Private Catalog * *----------------------------*/ FUNCTION grant_get_dbid(dbname IN varchar2) RETURN number IS dbid number; cnt number; BEGIN select max(db_id), count(*) into dbid, cnt from db join (select distinct db_key,db_name from dbinc) using(db_key) where db_name = dbname; if cnt = 0 then raise_application_error(-20018, 'database ' || dbname || ' not found in recovery catalog'); end if; if cnt > 1 then raise_application_error(-20019, 'database ' || dbname || ' not unique in recovery catalog'); end if; return dbid; END; PROCEDURE revoke_clean_userid(userid IN varchar2) IS BEGIN delete from vpc_users where filter_user=userid and add_new_db='N' and not exists (select * from vpc_databases where filter_user=userid); END; PROCEDURE grant_catalog(userid IN varchar2, dbname IN varchar2) IS BEGIN grant_catalog(userid, grant_get_dbid(dbname)); END; PROCEDURE grant_catalog(userid IN varchar2, dbid IN number) IS BEGIN insert into vpc_users select userid,'N',null from dual where not exists (select * from vpc_users where filter_user=userid); insert into vpc_databases select userid, dbid from dual where not exists (select * from vpc_databases where filter_user=userid and db_id=dbid); commit; END; PROCEDURE grant_register(userid IN varchar2) IS BEGIN merge into vpc_users using dual on (filter_user=userid) when matched then update set add_new_db='Y' when not matched then insert values(userid,'Y',null); commit; END; PROCEDURE revoke_catalog(userid IN varchar2, dbname IN varchar2) IS BEGIN revoke_catalog(userid, grant_get_dbid(dbname)); END; PROCEDURE revoke_catalog(userid IN varchar2, dbid IN number) IS BEGIN delete from vpc_databases where filter_user=userid and db_id=dbid; revoke_clean_userid(userid); commit; END; PROCEDURE revoke_register(userid IN varchar2) IS BEGIN update vpc_users set add_new_db='N' where filter_user=userid; revoke_clean_userid(userid); commit; END; PROCEDURE revoke_all(userid IN varchar2) IS BEGIN delete from vpc_databases where filter_user=userid; delete from vpc_users where filter_user=userid; commit; END; PROCEDURE vpc_run_sql(cre in boolean) IS type ct is ref cursor; c ct; stmt_type varchar2(1) := 'D'; stmt_sql long; begin if cre then stmt_type := 'C'; end if; open c for 'select stmt_sql from ' || dbms_catowner || '.cfs_v where stmt_type = ''' ||stmt_type|| ''' order by stmt_number'; loop fetch c into stmt_sql; exit when c%notfound; begin execute immediate stmt_sql; exception when others then if sqlcode != -942 or cre then raise; end if; end; end loop; close c; end vpc_run_sql; PROCEDURE create_virtual_catalog IS count1 number; type ct is ref cursor; c ct; begin open c for 'select count(*) from ' || dbms_catowner || '.vpc_users_v'; fetch c into count1; close c; if count1 = 0 then raise_application_error(-20015, 'Not authorized to share this catalog'); end if; begin select count(*) into count1 from user_role_privs where default_role = 'YES' and granted_role = 'RECOVERY_CATALOG_OWNER'; if count1 = 0 then raise_application_error(-20014, 'Must be granted RECOVERY_CATALOG_OWNER role'); end if; exception when others then if sqlcode != -942 then raise; end if; end; execute immediate 'update ' || dbms_catowner || '.vpc_users_v set version = null'; commit; vpc_run_sql(true); update vpc_users set version = (select max(version) from rcver); commit; end create_virtual_catalog; PROCEDURE drop_virtual_catalog IS begin vpc_run_sql(false); end drop_virtual_catalog; /*--------------------------------------------------* * 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.00'; -- 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.00'; -- In 8.1.3 the following changes were made: -- 1. Added 'X' (expired) backup piece status. version_list(3) := '08.01.03.00'; -- In 8.1.6 the following changes were made: -- 1) add stopTime to checkDatafile() version_list(4) := '08.01.06.00'; -- In 8.1.7 the following changes were made: -- 1) Add controlfile_type to bcf -- 2) Add controlfile_included to bs -- 3) Add controlfile_type to ccf -- 4) Add controlfile_type to xcf -- 5) Add is_standby to al -- 6) Add input_file_scan_only to bs version_list(5) := '08.01.07.00'; -- In 9.0.0 the following changes were made: -- 1) add setConfig(), deleteConfig(), resetConfig(), -- beginConfigResync() and endConfigREsync(). -- 2) Added 'X' (expired) status for CC, DC, AL objects. -- 3) Added blocks to bcf version_list(6) := '09.00.00.00'; -- In 9.2.0 the following changes were made: -- 1) add beinBackupSpFileResync, addBackupSpFile, checkBackupSpFile, -- endBackupSpFileResync -- 2) add beginIncarnationResync, endIncarnationResync, checkIncarnation -- 3) Never do a full resync when controlfile is not CURRENT and -- do not update high water marks. version_list(7) := '09.02.00.00'; -- In 10.0 the following changes were made: -- 1) add beingRmanStatusResync, checkRmanStatus, endRmanStatusResync. -- 2) add setConfig2(), beginConfigResync2(), and endConfigREsync2(). version_list(8) := '10.01.00.00'; -- 10gR2 version version_list(9) := '10.02.00.00'; version_list(10) := '10.02.00.01'; -- 11gR1 version version_list(11) := '11.01.00.00'; version_list(12) := '11.01.00.01'; version_list(13) := '11.01.00.02'; version_list(14) := '11.01.00.03'; version_list(15) := '11.01.00.04'; version_list(16) := '11.01.00.05'; version_list(17) := '11.01.00.06'; version_list(18) := '11.01.00.07'; version_list(19) := '11.02.00.00'; version_list(20) := '11.02.00.01'; version_max_index := 20; END dbms_rcvcat; 10207 rows selected. SQL> spool off