mysql/repair_mysql_tables.sh

613 lines
15 KiB
Bash
Executable File

#!/usr/bin/env bash
working_dir="$(dirname $(realpath $0))"
conf_file="${working_dir}/conf/mysql_credetials.conf"
tmp_log_file="$(mktemp)"
# -------------
# - Variable settings
# -------------
MYSQL_CREDENTIALS_GIVEN=false
ALL_DATABASES=false
DATABASE_NAME_NEEDED=true
# -------------
# --- Some functions
# -------------
usage() {
[[ -n "$1" ]] && error "$1"
[[ $terminal ]] && echo -e "
\033[1mUsage:\033[m
$(basename $0) [DB-Name]
\033[1mDescription\033[m
Script repairs (and reorganizes) all tables of the given databases by executing MySQL
command 'REPAIR TABLE'. If this was successfully, also 'OPTIMIZE TABLE' command will
be triggered.
If no database was given at command line all tables of ALL databases are checked.
\033[1mOptions\033[m
No Options available
\033[1mFiles\033[m
$conf_file: Configuration file
"
clean_up 1
}
clean_up() {
# Perform program exit housekeeping
rm -f $tmp_log_file
exit $1
}
echononl(){
if $terminal ; then
echo X\\c > /tmp/shprompt$$
if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then
echo -e -n "$*\\c" 1>&2
else
echo -e -n "$*" 1>&2
fi
rm /tmp/shprompt$$
fi
}
fatal(){
echo ""
if $terminal ; then
if [[ -n "$*" ]] ; then
echo -e " [ \033[31m\033[1mFatal\033[m ]: $*"
echo ""
echo -e " \033[31m\033[1mScript will be interrupted.\033[m\033[m"
else
echo -e " \033[31m\033[1mFatal error\033[m: \033[1mScript will be interrupted.\033[m"
fi
else
if [[ -n "$*" ]] ; then
echo " [ Fatal ]: $*"
echo ""
echo " Script was terminated.."
else
echo " Fatal error: Script was terminated.."
fi
fi
echo ""
clean_up 1
}
error(){
echo ""
if $terminal ; then
echo -e " [ \033[31m\033[1mError\033[m ]: $*"
else
echo " [ Error ]: $*"
fi
echo ""
}
warn (){
if $terminal ; then
echo ""
echo -e " [ \033[33m\033[1mWarning\033[m ]: $*"
echo ""
else
echo " [ Warning ]: $*"
fi
}
info (){
if $terminal ; then
echo ""
echo -e " [ \033[32m\033[1mInfo\033[m ]: $*"
echo ""
else
echo " [ Info ]: $*"
fi
}
echo_ok() {
if $terminal ; then
echo -e "\033[80G[ \033[32mok\033[m ]"
fi
}
echo_failed(){
if $terminal ; then
echo -e "\033[80G[ \033[1;31mfailed\033[m ]"
fi
}
echo_skipped() {
if $terminal ; then
echo -e "\033[80G[ \033[37mskipped\033[m ]"
fi
}
is_number() {
return $(test ! -z "${1##*[!0-9]*}" > /dev/null 2>&1);
# - also possible
# -
#[[ ! -z "${1##*[!0-9]*}" ]] && return 0 || return 1
#return $([[ ! -z "${1##*[!0-9]*}" ]])
}
trim() {
local var="$*"
var="${var#"${var%%[![:space:]]*}"}" # remove leading whitespace characters
var="${var%"${var##*[![:space:]]}"}" # remove trailing whitespace characters
echo -n "$var"
}
detect_mysql_version () {
_MYSQLD_VERSION="$(mysqld -V 2>/dev/null)"
if [[ -z "$_MYSQLD_VERSION" ]]; then
fatal "No installed MySQL server or distribution found!"
elif [[ "$_MYSQLD_VERSION" =~ MariaDB ]]; then
MYSQL_CUR_DISTRIBUTION="MariaDB"
elif [[ "$(basename "$(realpath "/usr/local/mysql")")" =~ percona- ]]; then
MYSQL_CUR_DISTRIBUTION="Percona"
elif [[ "$(basename "$(realpath "/usr/local/mysql")")" =~ mysql- ]]; then
MYSQL_CUR_DISTRIBUTION="MySQL"
fi
MYSQL_VERSION="$(echo $_MYSQLD_VERSION | grep -o -E "[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?" | head -n 1)"
MYSQL_MAJOR_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f1)"
MYSQL_MINOR_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f2)"
MYSQL_PATCH_LEVEL="$(echo $MYSQL_VERSION | cut -d '.' -f3)"
MYSQL_MAIN_VERSION="$(echo $MYSQL_VERSION | cut -d '.' -f1,2)"
}
trap clean_up SIGHUP SIGINT SIGTERM
# - Is this script running on terminal ?
# -
if [[ -t 1 ]] ; then
terminal=true
else
terminal=false
fi
echononl " Get MySQL command.."
mysql_command="$(which mysql)"
if [[ $? -eq 0 ]]; then
echo_ok
else
if [[ -x "/usr/local/mysql/bin/mysql" ]]; then
mysql_command="/usr/local/mysql/bin/mysql"
echo_ok
else
echo_failed
fatal "No binary 'mysql' found!"
fi
fi
# - Print help?
# -
if [[ "$(trim $*)" = "-h" ]] || [[ "$(trim $*)" = "--help" ]] ; then
usage
fi
# -------------
# - Get Database from commandline
# -------------
if [[ -n "$1" ]] ; then
DATABASE_NAME_NEEDED=false
DATABASE_NAME="$1"
else
DATABASE_NAME=""
fi
# -------------
# - Load Settings from configuration file
# -------------
if $terminal ; then
echo ""
fi
echononl " Loading configuration settings from $(basename ${conf_file}).."
if [[ -f "$conf_file" ]]; then
source "$conf_file" > $tmp_log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
fatal "$(cat $tmp_log_file)"
fi
else
echo_skipped
if $terminal ;then
warn "No Configuration File found. Loading defaults.."
fi
fi
declare -i index_arr=0
if [[ -n "$mysql_credential_args" ]]; then
MYSQL_CREDENTIAL_ARGS="$mysql_credential_args"
MYSQL_CREDENTIALS_GIVEN=true
fi
# - Get MySQL Version if paralle installations
# -
if [[ ${#mysql_credential_args_arr[@]} -gt 0 ]] ; then
echo ""
echo -e "\033[32m--\033[m"
echo ""
echo "Which installation contains the database to be repaired?"
echo ""
echo ""
declare -a _tmp_arr=()
while [[ $index_arr -lt ${#mysql_credential_args_arr[@]} ]] ; do
IFS=':' read -a _val_arr <<< "${mysql_credential_args_arr[$index_arr]}"
mysql_version="${_val_arr[0]}"
mysql_credential_args="${_val_arr[1]}"
mysql_dist_string="$(${mysql_command} $mysql_credential_args -N -s -e "SELECT VERSION()" 2> /dev/null)"
if [[ $? -ne 0 ]] ; then
if [[ "$(cat $tmp_log_file)" =~ "unknown variable 'login-path" ]] ; then
if [[ -x "/usr/local/mysql/bin/mysql" ]] ; then
mysql_dist_string="$(/usr/local/mysql/bin/mysql $mysql_credential_args -N -s -e "SELECT VERSION()" 2> /dev/null)"
fi
fi
fi
if [[ "$mysql_dist_string" =~ MariaDB ]]; then
mysql_dist="MariaDB $mysql_version"
else
mysql_dist="MySQL/Percona $mysql_version"
fi
echo -e " [\033[33m$index_arr\033[m] \033[33m$mysql_dist\033[m"
_temp_arr[${index_arr}]="$mysql_credential_args"
#_temp_arr+=("$mysql_credential_args")
(( index_arr++ ))
done
_OK=false
echo ""
echononl "Eingabe: "
while ! $_OK ; do
read _IN
if is_number "$_IN" && [[ -n ${_temp_arr[$_IN]} ]]; then
MYSQL_CREDENTIAL_ARGS="${_temp_arr[$_IN]}"
_OK=true
else
echo ""
echo -e "\tFalsche Eingabe !"
echo ""
echononl "Eingabe: "
fi
done
MYSQL_CREDENTIALS_GIVEN=true
fi
if $DATABASE_NAME_NEEDED ; then
echo ""
echo -e "\033[32m--\033[m"
echo ""
echo "Insert Database name which should be repaired.."
echo ""
echo -e " \033[33mLeave empty to repair tables of all databases\033[m"
echo ""
echononl "Database name: "
read DATABASE_NAME
#while [ "X$DATABASE_NAME" = "X" ] ; do
# echo -e "\n\t\033[33m\033[1mEingabe erforderlich.\033[m\n"
# echononl "Database name: "
# read DATABASE_NAME
#done
fi
if $MYSQL_CREDENTIALS_GIVEN ; then
# - Get MySQL Version
# -
echo ""
echo -e "\033[32m--\033[m"
echo ""
echononl " Get MySQL Version"
_version="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "SELECT VERSION()" 2> $tmp_log_file)"
if [[ $? -ne 0 ]] ; then
if [[ "$(cat $tmp_log_file)" =~ "unknown variable 'login-path" ]] ; then
if [[ -x "/usr/local/mysql/bin/mysql" ]] ; then
mysql_command="/usr/local/mysql/bin/mysql"
else
echo_failed
fatal "$(cat $tmp_log_file)"
fi
_version="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "SELECT VERSION()" 2> $tmp_log_file)"
if [[ $? -ne 0 ]] ; then
echo_failed
fatal "$(cat $tmp_log_file)"
else
echo_ok
fi
else
echo_failed
fatal "$(cat $tmp_log_file)"
fi
else
echo_ok
fi
IFS='.' read -r -a version_arr <<< "$_version"
declare -i MAJOR_VERSION="${version_arr[0]}"
declare -i MINOR_VERSION="${version_arr[1]}"
_path_level="${version_arr[2]}"
declare -i PATCH_LEVEL="${_path_level%%-*}"
## - Get current MySQL Distribution
## -
echononl " Get MySQL distribution .."
if [[ -z "$_version" ]]; then
echo_failed
fatal "No installed MySQL server or distribution found!"
elif [[ "$_version" =~ MariaDB ]]; then
MYSQL_CUR_DISTRIBUTION="MariaDB"
else
MYSQL_CUR_DISTRIBUTION="MySQL"
fi
echo_ok
echo ""
echo -e "\033[32m--\033[m"
echo ""
else
detect_mysql_version
MAJOR_VERSION="$MYSQL_MAJOR_VERSION"
MINOR_VERSION="$MYSQL_MINOR_VERSION"
PATCH_LEVEL="$MYSQL_PATCH_LEVEL"
if [[ "$MYSQL_CUR_DISTRIBUTION" = "MariaDB" ]] && ([[ $MAJOR_VERSION -gt 10 ]] \
|| ( [[ $MAJOR_VERSION -eq 10 ]] && [[ $MINOR_VERSION -gt 3 ]] )) ; then
if [[ -S "/tmp/mysql.sock" ]]; then
MYSQL_CREDENTIAL_ARGS="-u root -S /tmp/mysql.sock"
elif [[ -S "/run/mysqld/mysqld.sock" ]]; then
mysql_credential_args="-u root -S /run/mysqld/mysqld.sock"
elif [[ -S "/var/run/mysqld/mysqld.sock" ]]; then
MYSQL_CREDENTIAL_ARGS="-u root -S /var/run/mysqld/mysqld.sock"
else
fatal "Parameter 'MYSQL_CREDENTIAL_ARGS' cannot be determined automated.
Use configuration file "$conf_file" to set
parameter manually."
fi
else
if $(${mysql_command} --login-path=local -e ";" > /dev/null 2>&1) ; then
MYSQL_CREDENTIAL_ARGS="--login-path=local"
elif [[ -f "/usr/local/mysql/sys-maint.cnf" ]] ; then
MYSQL_CREDENTIAL_ARGS="--defaults-file=/usr/local/mysql/sys-maint.cnf"
elif [[ -f "/etc/mysql/debian.cnf" ]] ; then
MYSQL_CREDENTIAL_ARGS="--defaults-file=/etc/mysql/debian.cnf"
else
fatal "Parameter 'MYSQL_CREDENTIAL_ARGS' cannot be determined automated.
Use configuration file "$conf_file" to set
parameter manually."
fi
fi
fi
echo ""
echo ""
echo -e "\033[32m\033[1m====================\033[m"
echo "Settings: Repair tables of MySQL Database '$DATABASE_NAME'"
echo -e "\033[32m\033[1m====================\033[m"
echo ""
echo " MySQL Distribution...........: $MYSQL_CUR_DISTRIBUTION"
echo " MySQL Version................: ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}"
echo " MySQL Credentials............: $MYSQL_CREDENTIAL_ARGS"
echo ""
echo " MySQL commnd.................: ${mysql_command}"
echo ""
if [[ -n "$DATABASE_NAME" ]]; then
echo " Database name................: $DATABASE_NAME"
else
echo -e " Database name................: -- \033[033mall databases\033[m --"
fi
echo ""
echo ""
echo -e -n "\033[1mContinue repairing tables with above settings? [y/n]:\033[m "
read OK
while [[ "X${OK}X" = "XX" ]] ; do
echo ""
echo -e -n "\033[1mContinue with above settings? [y/n]:\033[m "
read OK
done
if [[ "${OK,,}" != 'yes' ]] && [[ "${OK,,}" != 'y' ]]; then
fatal "Abort by user request."
fi
declare -i length_table_name
declare -i number_blank_signd
declare -i index_i
_all_success=true
DATABASES="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "show databases")"
if [[ -z "$DATABASE_NAME" ]] ; then
if $terminal ; then
echo ""
echo -e " [ \033[37m\033[1m$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}\033[m ]: repair (and optimize) tables of ALL databases at host '$(hostname -f)'."
fi
ALL_DATABASES=true
else
found=false
for db in $DATABASES ; do
if [[ "$db" = "$DATABASE_NAME" ]]; then
DATABASES="$DATABASE_NAME"
found=true
break
fi
done
if ! $found ; then
warn "No Database '$DATABASE_NAME' found at $MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}"
clean_up 0
fi
fi
length_table_name=0
for db in $DATABASES ; do
[ "$db" = "information_schema" ] && continue
[ "$db" = "performance_schema" ] && continue
[ "$db" = "mysql" ] && continue
if $terminal ;then
echo ""
if [[ -n "$DATABASE_NAME" ]] ; then
echo -e " [$(date)] Repair (and optimize) tables in database '${db}'.."
else
echo -e " [ \033[37m\033[1m$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}\033[m $(date) ] Repair (and optimize) tables in database '${db}'.."
fi
fi
TABLES="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS $db -N -s -e "show tables" 2> $tmp_log_file )"
if [[ $? -ne 0 ]]; then
_all_success=false
error "Getting tables of database '${db}' failed.\n $(cat "$tmp_log_file")"
error_messages_arr+=("Getting tables of database '${db}' failed.")
continue
fi
for table in $TABLES ; do
# - Ommit InnoDB tables
# -
_engine="$(${mysql_command} $MYSQL_CREDENTIAL_ARGS -N -s -e "SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = '$db' AND TABLE_NAME = '$table'")"
if [[ "${_engine,,}" = 'innodb' ]] ; then
echo -e " [$(date)] Ommit table '$table' - The storage engine (InnoDB) doesn't support repair"
continue
fi
if $terminal ; then
blank_signs=""
if [[ $length_table_name -gt ${#table} ]]; then
number_blank_sign=$(expr $length_table_name - ${#table})
index_i=0
while [[ $index_i -lt $number_blank_sign ]] ; do
blank_signs="$blank_signs "
(( index_i++ ))
done
echo -en "\033[1G"
fi
echo -en "\033[1G \033[32mRepair table \033[1m$table\033[m$blank_signs\033[1G"
fi
length_table_name=${#table}
${mysql_command} $MYSQL_CREDENTIAL_ARGS $db -N -s -e "REPAIR TABLE \`$table\`" > $tmp_log_file 2>&1
if [[ $? -ne 0 ]]; then
_all_success=false
error "Repairing table '$table' failed.\n$(cat "$tmp_log_file")"
error_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Error while repairing table '${table}' of database '$db'.")
else
${mysql_command} $MYSQL_CREDENTIAL_ARGS $db -N -s -e "OPTIMIZE TABLE \`$table\`" > $tmp_log_file 2>&1
if [[ $? -ne 0 ]]; then
error "Reoptimizing table \"${table}\" of database \"$db\" failed.\n$(cat "$tmp_log_file")"
error_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Error while (re-)optimizing table '${table}' of database '$db'.")
fi
fi
done
if $terminal ;then
echo -en "\033[1G [$(date)] End repairing tables of database '${db}'.."
echo
fi
done
if $_all_success ; then
if $ALL_DATABASES ; then
info_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Repairing MySQL tables of all databases was successful.")
else
info_messages_arr+=("$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}: Repairing MySQL tables of database '${db}' was successful.")
fi
fi
if $terminal && $ALL_DATABASES ; then
echo ""
echo -e " [ \033[37m\033[1m$MYSQL_CUR_DISTRIBUTION ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_LEVEL}\033[m ]: Finished repairing MySQL databases at host $(hostname -f)."
echo ""
fi
if [[ ${#info_messages_arr[@]} -gt 0 ]]; then
for msg in "${info_messages_arr[@]}" ; do
if $terminal ; then
info "$msg"
fi
done
fi
if [[ ${#error_messages_arr[@]} -gt 0 ]]; then
for msg in "${error_messages_arr[@]}" ; do
if $terminal ; then
error "$msg"
fi
done
fi
clean_up 0