#!/bin/bash # # Script to backup (export), restore (import), or restore-and-rename a SAP HANA schema. # It automatically detects the number of available CPU threads to optimize operations. # Designed for both interactive use and cronjob automation. # # ETomi 2025 # --- Configuration --- hdbsql_path="/usr/sap/hdbclient/hdbsql" # -- HANA Secure User Store Key -- user_key="CRONKEY" # --- Validation --- for cmd in hdbsql hdbuserstore tar gzip nproc pv mkdir chmod date; do tool_path="$(dirname "$hdbsql_path")/$cmd" if ! command -v $cmd &> /dev/null && [ ! -f "$tool_path" ]; then echo "❌ Error: Required command '$cmd' is not found in your PATH or at '$tool_path'." exit 1 fi done if [ "$#" -lt 2 ]; then echo "❌ Error: Not enough arguments." echo "" echo "Usage: $0 " echo "" echo "Actions:" echo " setup-key - Interactively create a secure user store key" echo " backup [--compress] [--timestamp] - Export a schema, optionally compress and/or add a timestamp" echo " restore - Import a schema from a path" echo " restore-rename - Import and rename a schema" exit 1 fi action=$1 threads=$(nproc --all) # --- Functions --- confirm_action() { if [ "$TERM" != "dumb" ] && [ -t 0 ]; then read -p "❓ Are you sure you want to proceed with the '$1' operation? (y/n): " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then echo "🛑 Operation cancelled by user." exit 1 fi fi } execute_sql() { local command_to_run=$1 local action_name=$2 echo "âš™ī¸ Executing SQL: $command_to_run" $hdbsql_path -U $user_key "$command_to_run" if [ $? -ne 0 ]; then echo "❌ Failure! The '$action_name' command failed. Please check the logs." exit 1 fi } # --- Main Logic --- # The setup-key action is handled first as it doesn't need all global variables. if [ "$action" == "setup-key" ]; then if [ "$#" -ne 2 ]; then echo "❌ Error: Invalid arguments for 'setup-key'." echo "Usage: $0 setup-key " exit 1 fi key_name=$2 hdbuserstore_path="$(dirname "$hdbsql_path")/hdbuserstore" echo "--- Interactive Secure User Store Key Setup ---" read -p "Enter HANA host [localhost]: " hdb_host hdb_host=${hdb_host:-localhost} read -p "Enter instance number [00]: " hdb_instance hdb_instance=${hdb_instance:-00} hdb_port="3${hdb_instance}15" read -p "Enter tenant database name [NDB]: " hdb_tenant hdb_tenant=${hdb_tenant:-NDB} read -p "Enter database user [SYSTEM]: " hdb_user hdb_user=${hdb_user:-SYSTEM} read -sp "Enter password for '$hdb_user': " hdb_pass echo "" if [ -z "$hdb_pass" ]; then echo "❌ Error: Password cannot be empty." exit 1 fi connection_string="${hdb_host}:${hdb_port}@${hdb_tenant}" echo "-------------------------------------" echo "Key Name: $key_name" echo "Connection String: $connection_string" echo "Database User: $hdb_user" echo "-------------------------------------" confirm_action "create key '$key_name'" echo "â–ļī¸ Creating secure store key..." $hdbuserstore_path SET "$key_name" "$connection_string" "$hdb_user" "$hdb_pass" if [ $? -eq 0 ]; then echo "✅ Success! Key '$key_name' created. You can now set 'user_key=\"$key_name\"' at the top of this script." else echo "❌ Failure! Could not create key '$key_name'. Please check the details and try again." exit 1 fi exit 0 fi # --- Actions (Backup/Restore) --- echo "â„šī¸ Detected $threads available CPU threads to use for the operation." case "$action" in backup) if [ "$#" -lt 3 ] || [ "$#" -gt 5 ]; then echo "❌ Error: Invalid arguments for 'backup'." echo "Usage: $0 backup [--compress] [--timestamp]" exit 1 fi schema=$2 path=$3 compress_flag=false timestamp_flag=false # Loop through optional arguments to handle any order for arg in "${@:4}"; do case "$arg" in --compress) compress_flag=true ;; --timestamp) timestamp_flag=true ;; *) echo "❌ Error: Invalid option '$arg'. Valid options are '--compress' or '--timestamp'." exit 1 ;; esac done # Add timestamp to path if flag is set if [ "$timestamp_flag" = true ]; then timestamp=$(date +'%Y%m%d_%H%M%S') path="${path}_${timestamp}" fi echo "â„šī¸ Creating backup directory: $path" mkdir -p "$path" && chmod 777 "$path" if [ $? -ne 0 ]; then echo "❌ Failure! Could not create or set permissions for directory '$path'." exit 1 fi echo "â–ļī¸ Starting BACKUP (EXPORT) for schema '$schema' to '$path'..." sql_command="EXPORT \"$schema\".\"*\" AS BINARY INTO '$path' WITH REPLACE THREADS $threads;" execute_sql "$sql_command" "$action" echo "✅ SQL Export completed successfully." if [ "$compress_flag" = true ]; then echo "â–ļī¸ Compressing backup directory: $path" archive_file="${path}.tar.gz" total_size=$(du -sb "$path" | awk '{print $1}') tar cf - -C "$(dirname "$path")" "$(basename "$path")" | pv -s "$total_size" | gzip > "$archive_file" if [ $? -eq 0 ]; then echo "✅ Compression successful: $archive_file" echo "đŸ—‘ī¸ Removing original backup directory..." rm -rf "$path" else echo "❌ Failure! Compression failed." exit 1 fi fi ;; restore) if [ "$#" -ne 3 ]; then echo "❌ Error: Invalid arguments for 'restore'." echo "Usage: $0 restore " exit 1 fi schema=$2 path=$3 confirm_action "restore" echo "â–ļī¸ Starting RESTORE (IMPORT) for schema '$schema' from '$path'..." sql_command="IMPORT \"$schema\".\"*\" AS BINARY FROM '$path' WITH IGNORE EXISTING THREADS $threads;" execute_sql "$sql_command" "$action" echo "✅ Success! The '$action' operation completed." ;; restore-rename) if [ "$#" -ne 4 ]; then echo "❌ Error: Invalid arguments for 'restore-rename'." echo "Usage: $0 restore-rename " exit 1 fi schema=$2 new_schema=$3 path=$4 confirm_action "restore-rename" echo "â–ļī¸ Starting RESTORE & RENAME for schema '$schema' from '$path' to '$new_schema'..." sql_command="IMPORT \"$schema\".\"*\" AS BINARY FROM '$path' WITH IGNORE EXISTING THREADS $threads RENAME SCHEMA \"$schema\" TO \"$new_schema\";" execute_sql "$sql_command" "$action" echo "✅ Success! The '$action' operation completed." ;; *) echo "❌ Error: Invalid action '$action'." echo "Valid actions are 'backup', 'restore', 'restore-rename', or 'setup-key'." exit 1 ;; esac exit 0