3.1.3 Enable Total Recall (optional)
When Total Recall Option is activated, you should decide if one or more of the new tables should be added to a Flashback Data Archive.
Syntax to enable history tracking for a table is:
ALTER TABLE <tablename> FLASHBACK ARCHIVE [<Flashback Data Archive name>];
Note that the FDA name is required only when adding the table to a non-default FDA.
To disable history tracking for a table use:
ALTER TABLE <tablename> NO FLASHBACK ARCHIVE;
For convenience, we provide an example script that can help configuring archived tables:
import groovy.sql.Sql import java.util.logging.* def logger = Logger.getLogger(this.class.getName()); def config = new Config(args, 'setFlashbackArchive.cfg') // Override the default level for the top-level logger Logger.getLogger("").setLevel(config.log.level); config.log() def db = Sql.newInstance( config.db.url , config.db.user , config.db.passwd , 'oracle.jdbc.driver.OracleDriver') this.db = db // Get the set of tables For which FBA needs to be switched off def getTablesToSwitchOff(tables) { def tableList = tables.join("', '") switch_off = db.rows("""select fba.owner_name as owner , fba.table_name from dba_flashback_archive_tables fba where fba.status = 'ENABLED' and fba.owner_name||'.'||fba.table_name not in ('""" + tableList + """') """) } for (table in config.flashback.tables) { switch_on = db.firstRow("""select fba.owner_name as owner , fba.table_name from dba_flashback_archive_tables fba where fba.owner_name||'.'||fba.table_name = $table and ( fba.status is null or fba.status != 'ENABLED' ) """) if (switch_on) { logger.info "Switching Flashback Archiving on for table ${table}" def stmt = "alter table " + table + " flashback archive ${config.flashback.archive}" try { println stmt db.execute(stmt) } catch (java.sql.SQLException e) { logger.warning "Error occurred while executing SQL " + stmt logger.warning "Error was " + e.getMessage() println "Unable to set Flashback Archive to ${config.flashback.archive} for table ${table} " + e.getMessage() println "Press enter to continue" new InputStreamReader(System.in).readLine() } } } // For every table that is not in the config-list but has FBA turned on, a confirmation is asked before actually // turning off FBA getTablesToSwitchOff(config.flashback.tables).each { it -> def tableName = "${it.owner}.${it.table_name}" println "Switching Flashback Archiving OFF for table ${tableName}" println "Are you sure you want to do that? This will purge all history for this table!! (Y/N)" response = new InputStreamReader(System.in).readLine() while (response.toUpperCase() != "Y" && response.toUpperCase() != "N") { println "Please respond with \"Y\" or \"N\"" response = new InputStreamReader(System.in).readLine() } if (response.toUpperCase() == "Y") { def stmt = "alter table " + tableName + " no flashback archive" try { println stmt db.execute(stmt) logger.warning "Flashback Archiving was switched off for table ${tableName} as per users request" } catch (java.sql.SQLException e) { logger.warning "Error occurred while executing SQL " + stmt logger.warning "Error was " + e.getMessage() println "Unable to switch off Flashback Archive for table ${tableName} " + e.getMessage() println "Press enter to continue" new InputStreamReader(System.in).readLine() } } else { println "Flashback Archiving was NOT switched off for table ${tableName}." logger.warning "Flashback Archiving was NOT switched off for table ${tableName} as per users request" } } class Config { private static Logger logger = Logger.getLogger(Config.class.getName()) def configFile def db = [:] def log = [:] def flashback def Config(args, configFile) { this.configFile = configFile def parsedConfig def cl = new CliBuilder(usage: 'setFlashbackArchive.groovy [-c configFile]') cl.h(longOpt:'help', 'Show usage information and quit') cl.c(argName:'configFile', longOpt:'cfg', args:1, required:false, 'Config file, default is ' + configFile) def opt = cl.parse(args) if (!opt) { // the parse failed, the usage will be shown automatically println "\nInvalid command line, exiting..." System.exit(-1) } else if (opt.h) { cl.usage() System.exit(0) } if (opt.c) { this.configFile = opt.c } try { parsedConfig = new ConfigSlurper().parse(new File(this.configFile).toURL()) } catch (FileNotFoundException e) { logger.severe "Config file ${this.configFile} not found" System.exit(-1) } db = parsedConfig.db db.url = db.protocol + "//" + db.hostname + ":" + db.port + "/" + db.sid log = parsedConfig.log flashback = parsedConfig.flashback if (!flashback) { println("\nflashback is not set in Config file") System.exit(-1) } } def log() { logger.config "db=" + db.toString() } }
This script works in conjunction with a configuration file that can be created by copying the following to a file named setFlashbackArchive.cfg and changing its settings to appropriate values:
import java.util.logging.Level // Allowable values: SEVERE | WARNING | INFO | CONFIG | FINE | FINER | FINEST log.level = Level.INFO // user has to be a database user with the FLASHBACK ARCHIVE object privilege granted // on the Archive used. db { protocol = 'jdbc:oracle:thin:@' hostname = '<your hostname>' port = '<your port>' sid = '<your sid>' user = '<your dbuser>' passwd = '<your dbpassword>' } // This Flashback Archive has to exist. // It can be created by a SYSDBA or a user with // The tables argument contains a list of tables that should be archived. // If you remove a table from this list, archiving will be disabled. flashback { archive = '<name of your FlashBack Archive>' tables = ['<owner.table1>', '<owner.table2>'] }
The script can be run using Groovy (groovy <scriptname>)