#!/bin/bash # # Restores a MySQL/MariaDB database from a native or dockerized RDBMS instance # accessible from this box. Also creates an owner user for this database # (if it doesn't exist) and grants the appropriate privileges. # # Needs MySQL v5.7.6 or MariaDB 10.1.3 (or later). # To restore a database with the necessary user management and grants, # needs the superuser privileges on RDBMS. # * If the RDBMS runs dockerized you need call this script as a Docker manager # user (member of the docker Linux group). # * If we're using a native MySQL/MariaDB, you need call this script as a # Linux user whom the superuser role has been already granted within RDBMS # (via unix_socket authentication) or you need provide the superuser's # credentials as well. # Lack of this the script will skip the user management and grant steps. # # Usage: # $0 [-U dbuser] [-P dbpass] [-h dbhost] [-p dbport] # [-A dbadminuser] [-a dbadminpass] [-c characterset] # [-C container] [-d database] [-f dumpfile ] # [database (if not in -d)] [dumpfile (if not in -f)] # # Author: Kovács Zoltán # Kovács Zoltán # License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html) # 2023-06-18 v1.0 # new: forked from the "SMARTERP_skeleton" repository. # 2022-04-07 v0.4 # new: An option and a guess mechanism has added to set the default character set # of the restored database. # 2021-08-30 v0.3 # fix: Uses the defaults when MYDBA* variables aren't set. # 2021-03-22 v0.2 # fix: A duplicated SQLVERB has blocked setting password for a newly created user. # The unnecessary PASSWORD() call has been removed as well. # fix: Typos. # 2021-02-18 v0.1 Initial release # Accepted environment variables and their defaults. # MYCONTAINER=${MYCONTAINER-""} # Docker container's name MYCHARSET=${MYCHARSET-""} # Default character set for DB MYDATABASE=${MYDATABASE-""} # Database name to restore MYDBAUSER=${MYDBAUSER:-""} # Database admin superuser MYDBAPASSWORD=${MYDBAPASSWORD:-""} # Credential for the DBA user MYDUMP=${MYDUMP-""} # Dump file pathname MYHOST=${MYHOST:-"localhost"} # Connection parameter MYOPTIONS=${MYOPTIONS-""} # Options to pass to pg_dump MYPASSWORD=${MYPASSWORD-""} # Credential for the DB owner MYPORT=${MYPORT:-"3306"} # Connection parameter MYUSER=${MYUSER:-"root"} # Owner of the restored DB ### Temporailly ignored! Need to sanitize. MYOPTIONS="" # Basic environment settings. # LANG=C LC_ALL=C # We need also the sbin directories. if ! [[ "$PATH" =~ '/sbin:' ]]; then PATH="$PATH:/usr/local/sbin:/usr/sbin:/sbin"; fi # Other initialisations. # LOGSTAMP="\"\$DATE\" +%Y-%m-%d\ %H:%M:%S" # Timestamp format for logs MARIADBMIN="010001003" # MariaDB minimum version MYSQLMIN="005007006" # MySQL minimum version vetodatabases="information_schema mysql performance_schema sys" # Messages. # MSG_AMBIGOUS="The character set used within the dump is ambigous." MSG_BADDBTYPE="Unknown database type" MSG_BADDUMP="Doesn't exist or doesn't a dumpfile:" MSG_BADOPT="Invalid option" MSG_BADPARAM="Doubtful parameter:" MSG_BLOCKING="This is a fatal error - restore has been aborted." MSG_CONNTERM="DB connection(s) have forced to terminate" MSG_DOCKERGRPNEED="You must be a member of the docker group." MSG_DOESNOTRUN="Doesn't run the database container" MSG_EXISTING="did not create exisiting object" MSG_FAILCONN="Failed to connect the RDBMS." MSG_FAILGRANT="Failet to grant privileges to user" MSG_FAILKILL="Failed to kill active connection" MSG_FAILPASS="Failed to set password to user" MSG_FAILTOKILL="Failed to retrieve the active connections." MSG_FAILVER="Failed to get the RDBMS version." MSG_FAILUSER="Failed to create RDBMS user" MSG_MISSINGDEP="Fatal: missing dependency" MSG_NONBLOCKING="Recoverable error - restore is continuing." MSG_NONSUPER="DB user hasn't DBA (database superuser) privileges." MSG_NONZERO="The result code is non zero" MSG_OLDRDBMS="RDBMS version is too old" MSG_PERCENT="Hint: you may use percent-encoding (e.g %40 instead of @)" MSG_SUPERNEED="user must have DBA (database superuser) privileges." MSG_USAGE="Usage: $0 [options] [database [dump_pathname]]\n" MSG_USAGE+="Option:\tENVVAR:\n" MSG_USAGE+=" -A\tMYDBAUSER \tMySQL/MariaDB DB admin superuser\n" MSG_USAGE+=" -a\tMYDBAPASSWORD \tMySQL/MariaDB DB admin password\n" MSG_USAGE+=" -c\tMYCHARSET \tMySQL/MariaDB DB character set\n" MSG_USAGE+=" -C\tMYCONTAINER \tMySQL/MariaDB Docker container's name\n" MSG_USAGE+=" -d\tMYDATABASE \tMySQL/MariaDB database to restore\n" MSG_USAGE+=" -f\tMYDUMPFILE \tDumpfile pathname\n" MSG_USAGE+=" -h\tMYHOST \tHostname or IP to connect (localhost)\n" MSG_USAGE+=" -p\tMYPORT \tTCP port to connect (3306)\n" MSG_USAGE+=" -P\tMYPASSWORD \tMySQL/MariaDB password\n" MSG_USAGE+=" -U\tMYUSER \tMySQL/MariaDB username ($MYUSER)\n" # Getting options. # while getopts ":-:a:A:c:C:d:D:f:h:H:p:P:u:U:" option do case ${option} in "-" ) if [ "$OPTARG" = "help" ]; then echo -e "$MSG_USAGE" >&2; exit else echo "$MSG_BADOPT --$OPTARG" >&2; exit 1 fi ;; "A" ) MYDBAUSER="$OPTARG" ;; "a" ) MYDBAPASSWORD="$OPTARG" ;; "c" ) MYCHARSET="$OPTARG" ;; "C" ) MYCONTAINER="$OPTARG" ;; "d" | "D" ) MYDATABASE="$OPTARG" ;; "f" ) MYDUMPFILE="$OPTARG" ;; "h" | "H" ) MYHOST="$OPTARG" ;; "P" ) MYPASSWORD="$OPTARG" ;; "p" ) MYPORT="$OPTARG" ;; "u" | "U" ) MYUSER="$OPTARG" ;; \? ) echo "$MSG_BADOPT -$OPTARG" >&2; exit 1 ;; esac done; shift $((OPTIND -1)) # All options have been processed. # Checks the dependencies. # # Conditional dependencies (according to native or dockerized environment). [[ -z "$MYCONTAINER" ]] \ && additem="mysql" \ || additem="docker" # Common dependencies. TR=$(which tr 2>/dev/null) if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi for item in basename cat cut date dirname file grep gunzip head id locale \ readlink printf sed sort tail tee wc $additem do if [ -n "$(which $item)" ] then export $(echo $item | "$TR" '[:lower:]' '[:upper:]')=$(which $item) else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi done # All dependencies are available via "$THECOMMAND" (upper case) call. # Sanitizing the parameters. # Most of them are only arbitrary restrictions (reliable source: TODO!) # [[ -n "$MYDBAUSER" ]] && [[ ! "$MYDBAUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \ && echo -e "$MSG_BADPARAM $MYDBAUSER\n$MSG_USAGE" >&2 && exit 1 # [[ -n "$MYDBAPASSWORD" ]] && [[ ! "$MYDBAPASSWORD" =~ ^([[:alnum:]]|[ !~&#$<>()%+-_.])*$ ]] \ && echo -e "$MSG_BADPARAM $MYDBAPASSWORD\n$MSG_PERCENT\n$MSG_USAGE" >&2 && exit 1 # [[ -n "$MYCONTAINER" ]] && [[ ! "$MYCONTAINER" =~ ^([[:alnum:]]|[-_])*$ ]] \ && echo -e "$MSG_BADPARAM $MYCONTAINER\n$MSG_USAGE" >&2 && exit 1 # [[ -n "$MYDATABASE" ]] && [[ ! "$MYDATABASE" =~ ^([[:alnum:]]|[_])*$ ]] \ && echo -e "$MSG_BADPARAM $MYDATABASE\n$MSG_USAGE" >&2 && exit 1 # [[ -n "$MYDUMPFILE" ]] && [[ ! "$MYDUMPFILE" =~ ^([[:alnum:]]|[ .-_/])*$ ]] \ && echo -e "$MSG_BADPARAM $MYDUMPFILE\n$MSG_USAGE" >&2 && exit 1 # https://tools.ietf.org/html/rfc1123#page-13 (relaxed) [[ -z "$MYHOST" ]] && MYHOST="localhost" [[ -n "$MYHOST" ]] && [[ ! "$MYHOST" =~ ^([[:alnum:]]|[.-])*$ ]] \ && echo -e "$MSG_BADPARAM $MYHOST\n$MSG_USAGE" >&2 && exit 1 # https://tools.ietf.org/html/rfc6056 (relaxed) [[ -z "$MYPORT" ]] && MYPORT=3306 [[ -n "$MYPORT" ]] && [[ ! "$MYPORT" =~ ^[1-9]([[:digit:]]){0,4}$ ]] \ && echo -e "$MSG_BADPARAM $MYPORT\n$MSG_USAGE" >&2 && exit 1 # [[ -n "$MYPASSWORD" ]] && [[ ! "$MYPASSWORD" =~ ^([[:alnum:]]|[ !~&#$<>()%+-_.])*$ ]] \ && echo -e "$MSG_BADPARAM $MYPASSWORD\n$MSG_PERCENT\n$MSG_USAGE" >&2 && exit 1 # [[ -n "$MYUSER" ]] && [[ ! "$MYUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \ && echo -e "$MSG_BADPARAM $MYUSER\n$MSG_USAGE" >&2 && exit 1 # We've at least a minimally checked parameters. # Need to be root or a Docker manager user if the DB runs in a container. # [[ -n "$MYCONTAINER" ]] && [[ "$USER" != 'root' ]] \ && [[ -z "$(echo "$("$ID" -Gn "$USER") " | "$GREP" ' docker ')" ]] \ && echo "$MSG_DOCKERGRPNEED" >&2 && exit 1 #" # If the RDBMS is dockerized the container must be running. # [[ -n "$MYCONTAINER" ]] \ && [[ -z "$("$DOCKER" ps -q -f name=$MYCONTAINER)" ]] \ && echo "$MSG_DOESNOTRUN $MYCONTAINER" >&2 && exit 1 # Determines the database to restore. # # Lack of -d the 1st non-option parameter is the database's name. if [ -z "$MYDATABASE" -a -n "$1" ]; then MYDATABASE="$1"; shift; fi # The database's name is mandatory. if [ -z "$MYDATABASE" ] then echo -e "$MSG_USAGE" >&2; exit 1; fi # A humble sanitization. if [[ ! "$MYDATABASE" =~ ^([[:alnum:]]|[_])*$ ]] then echo -e "$MSG_USAGE" >&2; exit 1; fi # Silently refuses the MySQL/MariaDB internal databases. for veto in $vetodatabases "" do [[ "$MYDATABASE" = "$veto" ]] && exit 0 done # We've a database name to restore. # Determines the dumpfile. # # Lack of -f the 2nd non-option parameter is the database's name. if [ -z "$MYDUMPFILE" -a -n "$1" ]; then MYDUMPFILE="$1"; shift; fi # The dumpfile is mandatory. if [ -z "$MYDUMPFILE" ] then echo -e "$MSG_USAGE" >&2; exit 1; fi # The MYDUMPFILE must point to a readable file. # If it is an existing symlink dereferences it to ensure, it points to a file. if [ -h "$MYDUMPFILE" ]; then if [[ "$("$READLINK" "$MYDUMPFILE")" != /* ]] # relative path in symlink then MYDUMPFILE="$("$DIRNAME" "$MYDUMPFILE")/$("$READLINK" "$MYDUMPFILE")" # absolute path in symlink else MYDUMPFILE="$("$READLINK" "$MYDUMPFILE")"; fi fi # Let's check it! if [ ! -r "$MYDUMPFILE" -o ! -f "$MYDUMPFILE" ] then echo -e "$MSG_BADDUMP $MYDUMPFILE"; exit 1; fi # We've an existing dumpfile. # Tries to get the locale settings (actually CHARACTER SET) of this dump. # if [ -z "$MYCHARSET" ]; then # Let's identify the file is gzipped or not. UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE") UNPACKER=${UNPACKER##* } # The last word is the MIME-type. # We'll use gunzip or cat (a dummy unzipper), according to the MIME type. [[ "$UNPACKER" = 'application/gzip' ]] \ && UNPACKER="$GUNZIP" \ || UNPACKER="$CAT" # Collects all character set adjustments from the dumpfile. MYCHARSET=$("$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$GREP" -B2 -i 'CREATE TABLE' | \ "$GREP" -i 'character_set_client =' | "$SORT" -u) # Trims the character set's name itself (the first word after the equal sign). [[ -n "$MYCHARSET" ]] && MYCHARSET=$(echo -e "$MYCHARSET" | "$SED" 's/^.*= \(.*\) .*$/\1/') #' fi # We've a raw guess about the character sets used. # Finds the LOGFILE to use. # # If the folder containing the MYDUMPFILE is writable, we will use a # logfile with the same name as the dumpfile but with .log extension. [[ -w "$("$DIRNAME" "$MYDUMPFILE")" ]] \ && LOGFILE="${MYDUMPFILE%.gz}" \ && LOGFILE="${LOGFILE%.*}.log" \ || LOGFILE="/dev/null" # We've a suitable logfile. # Opens the log and takes care to close it when finish. # echo "$(eval $LOGSTAMP) Starting job #$$ $("$TR" '\0' ' ' < /proc/$$/cmdline)" | \ "$TEE" -a "$LOGFILE" # Sets a trap to make always a corresponding exit log entry as well. function close_log() { echo -e "$(eval $LOGSTAMP) Finished job #$$ $("$TR" '\0' ' ' < /proc/$$/cmdline)\n" | \ "$TEE" -a "$LOGFILE" } trap -- 'close_log' EXIT # We started logging. # Prepopulates the SQL command skeleton (macro). # # This skeleton makes the SQL calls independent to the environment # (native or dockerized) and credentials. We need only actualize the # CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLVERB. # Warning: the parameters must had been sanitized! DO_SQLVERB="" DO_SQLVERB+="export MYSQL_PWD; " DO_SQLVERB+="\"\$MYSQL\" \$CONNECT -N \$DATABASE " DO_SQLVERB+="-e \"\$SQLVERB\"" # We've a suitable SQL macro. # Do we connect the database as a DBA? # SQLVERB="SELECT 1;" # Sets the default DBA username for dockerized and native RDBMS as well. if [ -z "$MYDBAUSER" ]; then [[ -n "$MYCONTAINER" ]] \ && MYDBAUSER="root" \ || MYDBAUSER="$USER" fi # # We'll try the local connection (Unix-domain socket) first. CONNECT="" DATABASE="" #result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$? result="${result//[[:space:]]/}" if [ "$result" != "1" ]; then # # On failure we'll try the TCP connection. MYSQL_PWD="$MYDBAPASSWORD" CONNECT="-u $MYDBAUSER -h $MYHOST -P $MYPORT" result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$? result="${result//[[:space:]]/}" if [ "$result" != "1" ]; then # # On failure we'll try the TCP connection with non-DBA credentials. MYSQL_PWD="$MYPASSWORD" CONNECT="-u $MYUSER -h $MYHOST -P $MYPORT" result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$? result="${result//[[:space:]]/}" if [ "$result" != "1" ]; then # # On failure we'll give up here. [[ "$result" != "1" ]] \ && echo -e "$MSG_FAILCONN" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 fi fi fi # We've valid MYSQL_PWD and CONNECT clauses. # Checks the superuser privilege. # Better check: TODO! ISDBA=false DATABASE="" SQLVERB="SHOW GRANTS;" result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" if [[ $excode -eq 0 && \ "$result" =~ ^GRANTALLPRIVILEGESON\*\.\*.*WITHGRANTOPTION$ ]]; then ISDBA=true else echo -e "$MSG_NONSUPER" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 fi # We know we're a DB superuser or not. # Following steps need the superuser privileges. # Lack of this we'll skip them. if $ISDBA; then DATABASE="mysql" # Checks the minimal MySQL/MariaDB version. # SQLVERB="SELECT version();" result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" [[ $excode -ne 0 ]] \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 [[ -z "$result" ]] \ && echo -e "$MSG_FAILVER" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 # Let's extract a comparable RDBMS version from the result. dbversion=${result%%-*} # strips anyone after 1st dash (including) dbversion=(${dbversion//./ }) # converts to an array dbversion=$("$PRINTF" '%03d%03d%03d' ${dbversion[@]}) # 3 times 3 digits 0-padded if [ -n "$(echo "$result" | "$GREP" -i "mariadb")" ]; then # MariaDB version check. (( dbversion < MARIADBMIN )) \ && echo -e "$MSG_OLDRDBMS: $result" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 else # MySQL version check. (( dbversion < MYSQLMIN )) \ && echo -e "$MSG_OLDRDBMS: $result" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 fi # RDBMS version is proper. # Creates the database user (owner) if it doesn't exist. # echo -e "CREATE USER" | "$TEE" -a "$LOGFILE" SQLVERB=" CREATE USER '$MYUSER'@'$MYHOST'; " result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" if [[ $excode -ne 0 ]]; then # Already exists (or something went wrong). echo -e "$MSG_FAILUSER $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 else # Sets the password only if the user has just created. echo -e "SET PASSWORD" | "$TEE" -a "$LOGFILE" SQLVERB="SET PASSWORD FOR '$MYUSER'@'$MYHOST' = '$MYPASSWORD'; " result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" [[ $excode -ne 0 ]] \ && echo -e "$MSG_FAILPASS $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 fi # # Grants all privileges on the database to the user. # echo -e "GRANT" | "$TEE" -a "$LOGFILE" SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; " result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" [[ $excode -ne 0 ]] \ && echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 # We've the database user with the proper password. # Drops all existing connections to the database being restored. # echo -e "KILL CONNECTIONS" | "$TEE" -a "$LOGFILE" # List of the active connections. SQLVERB="SELECT id FROM information_schema.processlist " SQLVERB+="WHERE db = '$MYDATABASE';" result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? if [[ $excode -ne 0 ]]; then echo -e "$MSG_FAILTOKILL" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 else # Enumerates and tries to kill these connections. for connection in $result "" do if [ -n "$connection" ]; then SQLVERB="KILL $connection;" eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2); excode=$? [[ $excode -ne 0 ]] \ && echo -e "$MSG_FAILKILL $connection" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 fi done fi # Connections have eliminated (we hope). fi # Done with the superuser part. # Drops the database. # echo -e "DROP DATABASE" | "$TEE" -a "$LOGFILE" DATABASE="" SQLVERB="DROP DATABASE IF EXISTS $MYDATABASE;" result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" [[ $excode -ne 0 ]] \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 # Recreates the database. # echo -e "CREATE DATABASE" | "$TEE" -a "$LOGFILE" DATABASE="" SQLVERB="CREATE DATABASE $MYDATABASE;" result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" [[ $excode -ne 0 ]] \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 # We've an empty database. # Sets the default character set. # if [ -n "$MYCHARSET" ]; then echo -e "ALTER CHARACTER SET" | "$TEE" -a "$LOGFILE" # If it is ambigous, we'll ignore it. if [ "$(echo -e "$MYCHARSET" | "$WC" -l)" -ne 1 ]; then echo -e "$MSG_AMBIGOUS" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 MYCHARSET="" else # Let's set it. DATABASE="$MYDATABASE" SQLVERB="ALTER DATABASE $MYDATABASE CHARACTER SET $MYCHARSET;" result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result="${result//[[:space:]]/}" [[ $excode -ne 0 ]] \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && exit 1 fi fi # We've the character set adjusted. # Restores the database from the dump. # # This isn't so straightforward as in PostgreSQL. # We'll use the database user's credentials, not the superuser's # to mitigate the effect of an unsanitized dump. echo -e "RESTORE" | "$TEE" -a "$LOGFILE" # Let's identify the file is gzipped or not. UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE") UNPACKER=${UNPACKER##* } # The last word is the MIME-type. # We'll use gunzip or cat (a dummy unzipper), according to the MIME type. [[ "$UNPACKER" = 'application/gzip' ]] \ && UNPACKER="$GUNZIP" \ || UNPACKER="$CAT" # This is a sed expression to modify the security definers within the dump. MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=\`$MYUSER\`@\`$MYHOST\`/" # Considers the RDBMS environment. if [ -n "$MYCONTAINER" ]; then # Dockerized RDBMS. echo "MySQL dockerized - TODO!" | "$TEE" -a "$LOGFILE" >&2 else # Native RDBMS. # Reads the dump, on the fly unpacks it and modifies the scurity definer, # then passes the data stream to the MySQL client. "$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$SED" "$MOD_DEFINER" | \ "$MYSQL" -u "$MYUSER" -p$MYPASSWORD -h "$MYHOST" -P "$MYPORT" \ -f -D "$MYDATABASE" \ >/dev/null 2> >("$TEE" -a "$LOGFILE" >&2); excode=$? # Unfortunately the result code doesn't differentiate the # blocking and non-blocking states. [[ $excode -ne 0 ]] \ && echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2 fi # We had a try to restore the database - the result isn't properly defined. # Closing log entry will be handled via EXIT trap. # # That's all, Folks! :)