2
0
docker-skeleton/.templates/bin/mysql_restoredb

527 lines
20 KiB
Bash
Executable File

#!/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 <kovacs.zoltan@smartfront.hu>
# Kovács Zoltán <kovacsz@marcusconsulting.hu>
# 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! :)