Sunday, May 4, 2025

refresh_tool/ ├── config │ ├── variables.env # User-provided configurations │ └── db_credentials.env # Source/Target DB details ├── scripts │ ├── refresh_schemas.sh # Main script │ └── schema_functions.sh # Comparison/Validation functions └── logs/ # Auto-created log directory SCHEMAS="HR,OE,SH" NAS_DIR="/dba_nas/refresh" DIRECTORY_NAME="DATA_PUMP_DIR" #!/bin/bash compare_schema_objects() { local schema=$1 local source_conn=$2 local target_conn=$3 sqlplus -s /nolog << EOF | tee -a $LOG_FILE connect $source_conn set pagesize 0 feedback off select object_type || ':' || count(*) from dba_objects where owner = upper('$schema') group by object_type; exit; EOF sqlplus -s /nolog << EOF | tee -a $LOG_FILE connect $target_conn set pagesize 0 feedback off select object_type || ':' || count(*) from dba_objects where owner = upper('$schema') group by object_type; exit; EOF } check_invalid_objects() { local schema=$1 local conn=$2 invalid_count=$(sqlplus -s /nolog << EOF connect $conn set heading off feedback off select count(*) from dba_objects where owner = upper('$schema') and status != 'VALID'; exit; EOF ) [ $invalid_count -gt 0 ] && return 1 || return 0 } compare_row_counts() { local schema=$1 local source_conn=$2 local target_conn=$3 sqlplus -s /nolog << EOF | tee -a $LOG_FILE connect $source_conn set pagesize 0 feedback off execute dbms_output.put_line('=== Source DB Row Counts ==='); select table_name || ':' || num_rows from dba_tables where owner = upper('$schema'); exit; EOF sqlplus -s /nolog << EOF | tee -a $LOG_FILE connect $target_conn set pagesize 0 feedback off execute dbms_output.put_line('=== Target DB Row Counts ==='); select table_name || ':' || num_rows from dba_tables where owner = upper('$schema'); exit; EOF } #!/bin/bash CURRENT_DIR=$(dirname "$0") LOG_DIR="${CURRENT_DIR}/../logs" LOG_FILE="${LOG_DIR}/refresh_$(date +%Y%m%d%H%M).log" mkdir -p $LOG_DIR # Load configurations source "${CURRENT_DIR}/../config/variables.env" source "${CURRENT_DIR}/../config/db_credentials.env" source "${CURRENT_DIR}/schema_functions.sh" # Database Connections SOURCE_CONN="sys/${SYS_PWD}@${SOURCE_DB} as sysdba" TARGET_CONN="sys/${SYS_PWD}@${TARGET_DB} as sysdba" # Export parameters EXPORT_FILE="export_$(date +%Y%m%d).dmp" EXPORT_LOG="export_$(date +%Y%m%d).log" # Import parameters IMPORT_LOG="import_$(date +%Y%m%d).log" check_schema_existence() { local schema=$1 exists=$(sqlplus -s /nolog << EOF connect $TARGET_CONN set heading off feedback off select count(*) from dba_users where username = upper('$schema'); exit; EOF ) [ $exists -gt 0 ] && return 0 || return 1 } drop_schema_objects() { local schema=$1 sqlplus -s /nolog << EOF | tee -a $LOG_FILE connect $TARGET_CONN set serveroutput on begin for obj in (select object_name, object_type from dba_objects where owner = upper('$schema')) loop execute immediate 'drop ' || obj.object_type || ' ' || obj.object_name; end loop; for syn in (select synonym_name from dba_synonyms where table_owner = upper('$schema') and owner = 'PUBLIC') loop execute immediate 'drop public synonym ' || syn.synonym_name; end loop; end; / exit; EOF } perform_export() { echo "Starting Export..." | tee -a $LOG_FILE expdp $SOURCE_CONN directory=$DIRECTORY_NAME \ schemas=$SCHEMAS \ dumpfile=$EXPORT_FILE \ logfile=$EXPORT_LOG \ parallel=4 \ compression=ALL \ reuse_dumpfiles=YES } perform_import() { echo "Starting Import..." | tee -a $LOG_FILE impdp $TARGET_CONN directory=$DIRECTORY_NAME \ schemas=$SCHEMAS \ dumpfile=$EXPORT_FILE \ logfile=$IMPORT_LOG \ remap_schema=$SCHEMAS \ transform=OID:N \ parallel=4 } main() { # Perform export from source perform_export | tee -a $LOG_FILE # Process each schema IFS=',' read -ra SCHEMA_ARRAY <<< "$SCHEMAS" for schema in "${SCHEMA_ARRAY[@]}"; do schema=$(echo $schema | xargs) # Trim whitespace if check_schema_existence $schema; then echo "Dropping objects in $schema..." | tee -a $LOG_FILE drop_schema_objects $schema fi perform_import | tee -a $LOG_FILE # Post-import validation compare_schema_objects $schema $SOURCE_CONN $TARGET_CONN check_invalid_objects $schema $TARGET_CONN || echo "Invalid objects found!" compare_row_counts $schema $SOURCE_CONN $TARGET_CONN done echo "Refresh completed. Log file: $LOG_FILE" } main #!/bin/bash # Email notification function send_alert() { local subject=$1 local body=$2 local attachment=$3 if [ "$EMAIL_ENABLED" = "true" ]; then if [ "$USE_SSL" = "true" ]; then ssl_option="-S smtp-use-starttls -S ssl-verify=ignore" fi echo "$body" | s-nail -s "$subject" \ -S smtp="$SMTP_SERVER" \ -S smtp-auth=login \ -S smtp-auth-user="$EMAIL_USER" \ -S smtp-auth-password="$EMAIL_PASSWORD" \ $ssl_option \ -a "$attachment" \ "$EMAIL_TO" fi } # Checksum verification generate_checksum() { local dump_file=$1 sha256sum $dump_file > ${dump_file}.sha256 } verify_checksum() { local dump_file=$1 sha256sum -c ${dump_file}.sha256 || return 1 } # Tablespace verification check_tablespace() { local schema=$1 local conn=$2 local target_conn=$3 # Get schema's tablespace usage from source source_usage=$(sqlplus -s /nolog << EOF connect $conn set pagesize 0 feedback off select sum(bytes) from dba_segments where owner = upper('$schema'); exit; EOF ) # Get target tablespace free space target_free=$(sqlplus -s /nolog << EOF connect $target_conn set pagesize 0 feedback off select sum(bytes) from dba_free_space where tablespace_name = ( select default_tablespace from dba_users where username = upper('$schema') ); exit; EOF ) if [ $source_usage -gt $target_free ]; then echo "ERROR: Insufficient tablespace for $schema" return 1 fi } # Retry with backoff retry_operation() { local cmd=$@ local max_retries=3 local delay=60 for ((i=1; i<=$max_retries; i++)); do $cmd && return 0 echo "Attempt $i failed. Retrying in $delay seconds..." sleep $delay done return 1 } # Rollback functions create_rollback() { local schema=$1 local timestamp=$(date +%Y%m%d%H%M) local dump_file="rollback_${schema}_${timestamp}.dmp" echo "Creating rollback for $schema..." | tee -a $LOG_FILE expdp $TARGET_CONN directory=$DIRECTORY_NAME \ schemas=$schema \ dumpfile=$dump_file \ logfile=rollback_${schema}.log echo $dump_file } restore_rollback() { local schema=$1 local dump_file=$2 echo "Restoring from rollback $dump_file..." | tee -a $LOG_FILE impdp $TARGET_CONN directory=$DIRECTORY_NAME \ schemas=$schema \ dumpfile=$dump_file \ logfile=restore_${schema}.log \ table_exists_action=replace } #!/bin/bash # ... [Keep previous header and config loading] # Additional config loading source "${CURRENT_DIR}/../config/email_settings.env" # Enhanced connection handling if [ "$USE_WALLET" = "true" ]; then SOURCE_CONN="/@${SOURCE_DB} as sysdba" TARGET_CONN="/@${TARGET_DB} as sysdba" else SOURCE_CONN="sys/${SYS_PWD}@${SOURCE_DB} as sysdba" TARGET_CONN="sys/${SYS_PWD}@${TARGET_DB} as sysdba" fi # Enhanced main function with error handling main() { trap "handle_error" ERR start_time=$(date +%s) send_alert "Schema Refresh Started" "Refresh process initiated for schemas: $SCHEMAS" # Pre-flight checks verify_checksum $NAS_DIR/$EXPORT_FILE 2>/dev/null || { echo "Performing fresh export..." retry_operation perform_export | tee -a $LOG_FILE } generate_checksum $NAS_DIR/$EXPORT_FILE IFS=',' read -ra SCHEMA_ARRAY <<< "$SCHEMAS" for schema in "${SCHEMA_ARRAY[@]}"; do schema=$(echo $schema | xargs) local rollback_file="" # Tablespace check check_tablespace $schema $SOURCE_CONN $TARGET_CONN || { send_alert "Refresh Failed" "Tablespace check failed for $schema" $LOG_FILE exit 1 } if check_schema_existence $schema; then rollback_file=$(create_rollback $schema) drop_schema_objects $schema fi if ! retry_operation perform_import; then if [ -n "$rollback_file" ]; then restore_rollback $schema $rollback_file || { send_alert "Critical Failure" "Both import and rollback failed for $schema" $LOG_FILE exit 1 } fi send_alert "Refresh Failed" "Import failed for $schema" $LOG_FILE exit 1 fi # Post-import validation validate_refresh $schema done send_alert "Refresh Completed" "Successfully refreshed schemas: $SCHEMAS" $LOG_FILE } handle_error() { error_time=$(date +%s) duration=$((error_time - start_time)) echo "Error occurred after $duration seconds" | tee -a $LOG_FILE send_alert "Refresh Failed" "Error during schema refresh process" $LOG_FILE exit 1 } validate_refresh() { local schema=$1 # Object comparison compare_schema_objects $schema $SOURCE_CONN $TARGET_CONN # Invalid objects check check_invalid_objects $schema $TARGET_CONN || { send_alert "Validation Warning" "Invalid objects found in $schema" $LOG_FILE } # Row count comparison row_diff=$(compare_row_counts $schema $SOURCE_CONN $TARGET_CONN | grep -c 'DIFFERENCE') [ $row_diff -gt 0 ] && { send_alert "Validation Warning" "Row count mismatches in $schema" $LOG_FILE } } # ... [Keep other functions but add retry_operation calls] perform_export() { echo "Starting Export..." | tee -a $LOG_FILE expdp $SOURCE_CONN directory=$DIRECTORY_NAME \ schemas=$SCHEMAS \ dumpfile=$EXPORT_FILE \ logfile=$EXPORT_LOG \ parallel=4 \ compression=ALL \ reuse_dumpfiles=YES \ encryption=ALL \ encryption_password=$SYS_PWD } perform_import() { echo "Starting Import..." | tee -a $LOG_FILE verify_checksum $NAS_DIR/$EXPORT_FILE || return 1 impdp $TARGET_CONN directory=$DIRECTORY_NAME \ schemas=$SCHEMAS \ dumpfile=$EXPORT_FILE \ logfile=$IMPORT_LOG \ remap_schema=$SCHEMAS \ transform=OID:N \ parallel=4 \ encryption_password=$SYS_PWD } # ... [Keep remaining functions] #!/bin/bash generate_html_report() { local schema=$1 local source_conn=$2 local target_conn=$3 local report_file="${NAS_DIR}/reports/${schema}_comparison_$(date +%Y%m%d%H%M).html" mkdir -p "${NAS_DIR}/reports" # Get object counts from source source_objects=$(sqlplus -s /nolog << EOF connect $source_conn set pagesize 0 feedback off select object_type || ':' || count(*) from dba_objects where owner = upper('$schema') group by object_type order by object_type; exit; EOF ) # Get object counts from target target_objects=$(sqlplus -s /nolog << EOF connect $target_conn set pagesize 0 feedback off select object_type || ':' || count(*) from dba_objects where owner = upper('$schema') group by object_type order by object_type; exit; EOF ) # Get invalid objects source_invalid=$(get_invalid_count "$schema" "$source_conn") target_invalid=$(get_invalid_count "$schema" "$target_conn") # Get index status source_indexes=$(get_index_status "$schema" "$source_conn") target_indexes=$(get_index_status "$schema" "$target_conn") # Generate HTML report cat << HTML > "$report_file" Schema Comparison Report: $schema

Schema Comparison Report

Summary

Schema Name: $schema

Report Date: $(date +"%Y-%m-%d %H:%M:%S")

Object Comparison

$(join -t: -a1 -a2 -o 0,1.2,2.2 -e "0" \ <(echo "$source_objects" | sort) \ <(echo "$target_objects" | sort) | \ awk -F: '{printf "%s\n", \ $1, $2, $3, \ ($2 == $3 ? "" : "")}')
Object Type Source DB Count Target DB Count Status
%s%s%s
MatchMismatch

Invalid Objects

$source_invalid $target_invalid
Database Invalid Count
Source DB
Target DB

Index Status

Database Invalid Indexes
Source DB $(echo "$source_indexes" | wc -l)
Target DB $(echo "$target_indexes" | wc -l)

Table Row Count Comparison

$(compare_row_counts $schema $source_conn $target_conn | \ awk -F: '/:/ {printf "%s\n", \ $1, $2, $3, \ ($2 == $3 ? "" : "")}')
Table Name Source Rows Target Rows Status
%s%s%s
MatchMismatch
HTML echo "Generated report: $report_file" echo "View Comparison Report" | tee -a $LOG_FILE } get_invalid_count() { local schema=$1 local conn=$2 sqlplus -s /nolog << EOF connect $conn set heading off feedback off select count(*) from dba_objects where owner = upper('$schema') and status != 'VALID'; exit; EOF } get_index_status() { local schema=$1 local conn=$2 sqlplus -s /nolog << EOF connect $conn set heading off feedback off select index_name from dba_indexes where owner = upper('$schema') and status not in ('VALID', 'USABLE'); exit; EOF } get_index_status() { local schema=$1 local conn=$2 sqlplus -s /nolog << EOF connect $conn set heading off feedback off -- Check both non-partitioned and partitioned indexes with index_status as ( /* Non-partitioned indexes */ select i.index_name, i.status as index_status, null as partition_status from dba_indexes i where i.owner = upper('$schema') and i.partitioned = 'NO' and i.status not in ('VALID', 'USABLE') union all /* Partitioned indexes with bad partitions */ select distinct i.index_name, i.status as index_status, p.status as partition_status from dba_indexes i join dba_ind_partitions p on i.owner = p.index_owner and i.index_name = p.index_name where i.owner = upper('$schema') and i.partitioned = 'YES' and (i.status not in ('VALID', 'USABLE') or p.status != 'USABLE') ) select index_name || ':' || nvl2(partition_status, 'PARTITION=' || partition_status, 'INDEX=' || index_status) from index_status order by 1; exit; EOF } generate_html_report() { # ... [keep previous variable declarations] # New: Get index status for both databases source_indexes=$(get_index_status "$schema" "$source_conn") target_indexes=$(get_index_status "$schema" "$target_conn") # Generate HTML report with side-by-side comparison cat << HTML > "$report_file" Schema Comparison: $schema

Schema Comparison Report: $schema

Object Type Counts

$(join -t: -a1 -a2 -o 0,1.2,2.2 -e "0" \ <(echo "$source_objects" | sort) \ <(echo "$target_objects" | sort) | \ awk -F: '{printf "%s\n", $1, $2, $3, ($2 == $3 ? "" : "")}')
Object Type Source DB Target DB Status
%s%s%s
✅ Match❌ Mismatch

Invalid Objects

Database Invalid Count
Source DB $source_invalid
Target DB $target_invalid

Index Status

$(awk -F: ' BEGIN { # Load source indexes while(getline < "source_idx.tmp") { split($0, parts, ":"); src_idx[parts[1]] = parts[2]; } # Load target indexes while(getline < "target_idx.tmp") { split($0, parts, ":"); tgt_idx[parts[1]] = parts[2]; } # Print combined results for (idx in src_idx) { print ""; delete tgt_idx[idx]; } for (idx in tgt_idx) { print ""; } }' <<< "")
Index Name Source DB Status Target DB Status
" idx "" src_idx[idx] "" (tgt_idx[idx] ? tgt_idx[idx] : "Valid") "
" idx "Valid" tgt_idx[idx] "

Table Row Counts

$(compare_row_counts $schema $source_conn $target_conn | \ awk -F: '/:/ {printf "%s\n", $1, $2, $3, ($2 == $3 ? "" : "")}')
Table Name Source DB Target DB Status
%s%s%s
✅ Match❌ Mismatch
HTML # Cleanup temporary files rm -f source_idx.tmp target_idx.tmp echo "Generated report: $report_file" } compile_invalid_objects() { local schema=$1 local conn=$2 local log_file=$3 echo "Compiling invalid objects for $schema..." | tee -a $log_file # Generate compilation script sqlplus -s /nolog << EOF > compile_$$.sql connect $conn set feedback off verify off pagesize 0 linesize 200 spool compile_temp_$$.sql select case when object_type = 'PACKAGE BODY' then 'alter package "' || owner || '"."' || object_name || '" compile body;' else 'alter ' || object_type || ' "' || owner || '"."' || object_name || '" compile;' end as ddl from dba_objects where owner = upper('$schema') and status = 'INVALID' and object_type in ( 'FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY', 'TRIGGER','VIEW','MATERIALIZED VIEW','TYPE','TYPE BODY' ) order by object_type, object_name; spool off exit; EOF # Execute compilation sqlplus -s /nolog << EOF | tee -a $log_file connect $conn set feedback on echo on @compile_$$.sql @compile_temp_$$.sql exit; EOF # Verify remaining invalid objects invalid_count=$(sqlplus -s /nolog << EOF connect $conn set heading off feedback off select count(*) from dba_objects where owner = upper('$schema') and status = 'INVALID' and object_type in ( 'FUNCTION','PROCEDURE','PACKAGE','PACKAGE BODY', 'TRIGGER','VIEW','MATERIALIZED VIEW','TYPE','TYPE BODY' ); exit; EOF ) # Cleanup temporary files rm -f compile_$$.sql compile_temp_$$.sql if [ $invalid_count -gt 0 ]; then echo "Warning: $invalid_count invalid objects remaining after compilation" | tee -a $log_file return 1 else echo "All objects compiled successfully" | tee -a $log_file return 0 fi } generate_invalid_object_report() { local schema=$1 local conn=$2 local report_file="${NAS_DIR}/reports/${schema}_invalid_objects_$(date +%Y%m%d%H%M).html" sqlplus -s /nolog << EOF > $report_file connect $conn set markup html on set pagesize 50000 set feedback off select object_name as "Object Name", object_type as "Object Type", status as "Status", created as "Created", last_ddl_time as "Last DDL Time" from dba_objects where owner = upper('$schema') and status = 'INVALID' order by object_type, object_name; exit; EOF echo "Generated invalid object report: $report_file" | tee -a $LOG_FILE }