Files
server/opt/psa/admin/sbin/repair_innodb
cutemeli 0bfc6c8425 Initial
2025-12-22 10:32:59 +00:00

770 lines
22 KiB
Bash
Executable File

#!/bin/bash
### Copyright 1999-2025. WebPros International GmbH. All rights reserved.
shopt -s nullglob
export LC_ALL="C" PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
unset GREP_OPTIONS
umask 022
export PLESK_MYSQL_AUTH_SKIP_CONNECTION_CHECK=1
PRODUCT_ROOT_D="/opt/psa"
PRODUCT_LOGS_D="/var/log/plesk"
prog="`basename $0`"
action="$1"
ts()
{
[ "$PLESK_REPAIR_INNODB_TIMESTAMPS" = "none" ] ||
echo "[`date --rfc-3339=seconds`] "
}
# Communication protocol with repaird is:
# * emit output to stdout as plaintext;
# * stderr is not shown to user, but may be logged by 'repaird -v 3';
# * last line of output is JSON with any remaining output (see result() below);
# * exit code is 0.
info()
{
echo "`ts`INFO: $*"
}
warn()
{
echo "`ts`WARNING: $*"
}
err()
{
echo "`ts`ERROR: $*"
}
die()
{
err "$@" | result "failed"
exit 0
}
success()
{
info "$@" | result "ok"
exit 0
}
usage()
{
echo "Usage: $prog { --check | --repair }" >&2
exit 1
}
result()
{
local status="$1"
python3 -c 'import sys, json
message = sys.stdin.read()
status = sys.argv[1] if message.strip() else "ok"
print(json.dumps({"status": status, "message": message}))
' "$status"
}
log_if_failed()
{
# Emits command output to stdout if it failed, otherwise to stderr
local out=
local rc=
if out="`"$@" 2>&1`"; then
echo "$out" >&2
return 0
else
rc="$?"
echo "$out"
return "$rc"
fi
}
is_remote_db_feature_enabled()
{
[ -s "/etc/psa/private/dsn.ini" ]
}
get_psa_conf_option_value()
{
local name="$1"
local default="$2"
local prod_conf_t="/etc/psa/psa.conf"
local value="`sed -n "s/^\s*$name\s*//p" "$prod_conf_t" | tail -n 1`"
echo "${value:-$default}"
}
get_plesk_dump_d()
{
get_psa_conf_option_value "DUMP_D" "/var/lib/psa/dumps"
}
create_backup_dir()
{
local dump_d="`get_plesk_dump_d`"
local timestamp="`date +%Y%m%d-%H%M%0S`"
local backup_d="$dump_d/mysql.repair-innodb.$timestamp.d"
mkdir "$backup_d" || return 1
echo "$backup_d"
}
log_backup_dir_location()
{
local backup_d="$1"
local plesk_cron_daily="/etc/cron.daily/50plesk-daily"
local lifetime= expire_by=
lifetime="`grep -F "mysql.repair-innodb.*.d" "$plesk_cron_daily" | grep -E -o '\<[0-9]+\s+days\>'`"
[ -z "$lifetime" ] || expire_by="`date -d "$(date -r "$backup_d") +$lifetime"`"
info "Backups will be stored under '$backup_d'." \
${lifetime:+"Directory lifetime: $lifetime (will be automatically removed after $expire_by)."}
}
find_latest_preexisting_dump()
{
local dump_d="`get_plesk_dump_d`"
find "$dump_d" \
-type f \( \
-name 'mysql.preupgrade.*' -a ! -name 'mysql.preupgrade.apsc.*' -o \
-name 'mysql.daily.*' -o \
-name 'mysql.pre-rdbms-upgrade.*' \
\) \
-printf "%T@\t%p\n" | LC_ALL=C sort -nr -k1 | head -n1 | cut -f2
}
get_mysql_option_value()
{
local section="$1"
local name="$2"
local default="$3"
local value="`/usr/bin/my_print_defaults "$section" | sed -n "s/^--$name=//p" | tail -n 1`"
echo "${value:-$default}"
}
get_mysql_datadir()
{
get_mysql_option_value mysqld datadir "/var/lib/mysql"
}
mysql_action()
{
local action="$1"
if [ "$action" = "start" -o "$action" = "restart" ]; then
# The script may restart MySQL often, which may lead to it failing with 'start-limit-hit'.
[ -n "$MYSQL_SERVICE_NAME" ] || MYSQL_SERVICE_NAME="`"$PRODUCT_ROOT_D/admin/sbin/pleskrc" mysql name`"
[ -z "$MYSQL_SERVICE_NAME" ] || /bin/systemctl reset-failed "$MYSQL_SERVICE_NAME"
fi
"$PRODUCT_ROOT_D/admin/sbin/pleskrc" mysql "$1" 1>&2 || {
# 'pleskrc' output is usually not very pretty or informative, so don't show it to user directly.
local log_path="$PRODUCT_LOGS_D/rc_actions.log"
info "Failed to $action the database service. See $log_path and the service log for details."
return 1
}
}
mysql_disable_respawn()
{
[ -n "$MYSQL_SERVICE_NAME" ] || MYSQL_SERVICE_NAME="`"$PRODUCT_ROOT_D/admin/sbin/pleskrc" mysql name`"
local unit_drop_in="/lib/systemd/system/$MYSQL_SERVICE_NAME.d/respawn.conf"
[ -f "$unit_drop_in" ] || return 0
# This action is later reverted by 'mysqlmng --post-re-init', if needed. However, in practice
# it may be reverted only on CentOS 7 like systems, since other versions already include own
# Restart unit configuration (and don't need ours). This means that effective CentOS 7
# configuration may change as a result of repair, but I'm willing to make this small sacrifice.
rm -f "$unit_drop_in"
/bin/systemctl daemon-reload
info "Plesk-configured automatic restart of $MYSQL_SERVICE_NAME was temporarily disabled."
}
report_disk_space_for_repair()
{
local datadir="$1"
local dump_d="`get_plesk_dump_d`"
# This allows a very rough estimate of space that would be consumed by created dump files.
local latest_dump="`find_latest_preexisting_dump`"
local du_estimate="`du -cshD "$datadir" $latest_dump | tail -n1 | cut -f1`"
local du_mount_point="`df -h --output=target "$dump_d" | tail -n1`"
local du_available="`df -h --output=avail "$dump_d" | tail -n1 | xargs`"
info "Repair of InnoDB corruption will require at least $du_estimate of free disk space" \
"on $du_mount_point mount point. Currently $du_available of disk space is available on it."
}
check()
{
! is_remote_db_feature_enabled || die "Cannot check for InnoDB corruption on a remote database."
mysql_action "stop" || die "Could not stop the database service."
{
local rc=0
local datadir="`get_mysql_datadir`"
# Default system tablespace data file is 'ibdata1', assume others (if any) are named similarly.
# Alternatively we should parse innodb_data_file_path value, which may include many paths.
# On some versions (e.g. Percona and MySQL 8.0) `mysql` DB tables are created in the `mysql`
# tablespace, which is just ./mysql.ibd.
#
# Note that technically tablespace file locations could be quite arbitrary (see CREATE TABLESPACE and
# CREATE TABLE ... TABLESPACE syntax), but fetching that information (e.g. from INFORMATION_SCHEMA.FILES)
# generally requires the DB server to be up, which is not feasible. However, any tablespace file
# locations are expected to be under datadir, innodb_data_home_dir, innodb_directories system variables.
for file in "$datadir"/ibdata* "$datadir"/*/*.ibd "$datadir"/*.ibd; do
/usr/bin/innochecksum "$file" || {
rc="$?"
err "InnoDB tablespace file '$file' is corrupted."
}
done
[ "$rc" -eq 0 ] || report_disk_space_for_repair "$datadir"
[ "$rc" -ne 0 ] || mysql_action "start" || err "Could not start the database service."
} | result "corrupted"
exit 0
}
change_recovery_mode()
{
# This assumes that create_my_cnf_d() was called during installation, if needed.
local mode="$1"
local no_restart="$2"
local my_cnf_d="/etc/mysql/conf.d"
local config="$my_cnf_d/plesk-innodb-recovery.cnf"
if [ -n "$mode" ] && [ "$mode" -gt 0 ]; then
cat > "$config" <<-EOT
# Temporarily added by Plesk Repair Kit
[mysqld]
innodb_force_recovery = $mode
EOT
else
rm -f "$config"
fi
[ -n "$no_restart" ] || mysql_action "restart"
}
reinit_mysql_datadir()
{
local datadir="$1"
info "Re-initializing the database service data directory."
mysql_action "stop" || die "Could not stop the database service."
find "$datadir" -mindepth 1 -maxdepth 1 \
! \( -name debian-\*.flag -o -name 'lost+found' -o -name 'lost@002bfound' \) -print0 |
xargs -0 -r rm -rf
# remove innodb_force_recovery, as required by the mysql_install_db script
change_recovery_mode 0 --no-restart || die "Failed to de-configure the database recovery mode."
TMPDIR= log_if_failed bash /usr/bin/mysql_install_db \
--force --datadir="$datadir" --user=mysql --disable-log-bin ||
die "Failed to re-initialize '$datadir'."
# restart w/o innodb_force_recovery
info "Exiting InnoDB recovery mode."
change_recovery_mode || die "Failed to start the database server in normal mode."
}
database_client_invoke()
{
local use_root="$1"
shift
local MYSQL_BIN_D="/usr/bin"
local mysql_client="$MYSQL_BIN_D/mysql"
if [ -f "$MYSQL_BIN_D/mariadb" ]; then
mysql_client="$MYSQL_BIN_D/mariadb"
fi
if [ -n "$use_root" ]; then
if [ -s "/var/log/mysqld.log" ]; then
# MySQL generates and logs a temporary password (the last word on the line), forces to set a new one.
local marker='A temporary password is generated for root@localhost:'
local temp_passwd="`grep -F "$marker" "/var/log/mysqld.log" | tail -n1 | xargs -n1 | tail -n1`"
MYSQL_PWD="$temp_passwd" "$mysql_client" --connect-expired-password -uroot "$@" \
-e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$temp_passwd';"
MYSQL_PWD="$temp_passwd" "$mysql_client" -uroot "$@"
else
"$mysql_client" -uroot "$@"
fi
else
MYSQL_PWD="`cat /etc/psa/.psa.shadow`" "$mysql_client" -uadmin "$@"
fi
}
db_convert_str()
{
local str="$1"
local src="$2"
local dst="$3"
# Note: this doesn't really account for 'lower_case_table_names', which may affect character case.
# https://mariadb.com/kb/en/identifier-case-sensitivity/
if [ -x "/usr/bin/mariadb-conv" ]; then
# Since MariaDB 10.5.1: https://mariadb.com/kb/en/mariadb-conv/
echo "$str" | /usr/bin/mariadb-conv -f "$src" -t "$dst" --delimiter="\r\n"
else
# A very dumb and incomplete fallback for other forks and versions.
if [ "$dst" = "filename" ]; then
[ -z "`echo "$str" | tr -d '[:alnum:]_'`" ]
elif [ "$src" = "filename" ]; then
[ "`echo "$str" | tr -d '@'`" = "$str" ]
fi || echo "Cannot correctly convert '$str' from '$src' to '$dst' on the current database service version." >&2
echo "$str"
fi
}
db_fname_to_utf8()
{
# On MariaDB same as: SELECT CONVERT(_filename $1 USING utf8);
# Implemented by 'my_charset_filename' and 'my_wc_mb_filename()' in MySQL sources.
# See also https://dev.mysql.com/doc/refman/8.4/en/identifier-mapping.html
# and https://mariadb.com/kb/en/identifier-to-file-name-mapping/
db_convert_str "$1" filename utf8
}
db_utf8_to_fname()
{
# Implemented by 'my_charset_filename' and 'my_mb_wc_filename()' in MySQL sources.
db_convert_str "$1" utf8 filename
}
db_fmt()
{
# Formats each argument DB filename for use in SQL
for db in "$@"; do
echo "\``db_fname_to_utf8 "$db" | sed -e 's|\`|\`\`|g'`\`"
done | xargs
}
file_age()
{
# Prints file age in human-readable form, such as '1d 3m 4s'
local path="$1"
[ -f "$path" ] || return 0
local age="$(($(date +%s) - $(date +%s -r "$path")))"
declare -A steps=(
[d]=$((24*3600))
[h]=3600
[m]=60
[s]=1
)
local result=() val=
for unit in d h m s; do
val=$((age / steps[$unit]))
age=$((age % steps[$unit]))
! [ "$val" -gt 0 ] || result+=("$val$unit")
done
echo "${result[*]}"
}
escape_for_regex()
{
# Escapes a string for use in sed or awk regex
echo -n "$1" | sed -e 's/[][\/$*.^]/\\&/g'
}
filter_db_from_sql_dump()
{
local db="`db_fmt "$1"`"
true escape_for_regex
awk "
BEGIN { p=1 }
/^$(escape_for_regex '-- Current Database:')/ { p=0 }
/^$(escape_for_regex "-- Current Database: $db")$/ { p=1 }
/^$(escape_for_regex '/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;')$/ { p=1 }
/^$(escape_for_regex '/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;')$/ { p=1 }
/^$(escape_for_regex '/*!50112 SET @disable_bulk_load = ')/ { p=1 }
(p) { print }
"
}
is_db_in_sql_dump()
{
local db="`db_fmt "$1"`"
grep -Fxq -- "-- Current Database: $db"
}
is_plesk_db()
{
local db="$1"
# Some DBs may technically have different names (e.g. apsc), but we don't really expect this
# on modern Plesk versions and there's no reliable way to get them with unavailable MySQL.
local plesk_dbs=(
"psa"
"apsc"
"horde"
"mysql"
"phpmyadmin"
"roundcubemail"
"sitebuilder5"
)
printf "%s\n" "${plesk_dbs[@]}" | grep -Fxq "$db"
}
list_critical_dbs()
{
local critical_dbs=(
"psa"
"apsc"
"mysql"
)
printf "%s\n" "${critical_dbs[@]}"
}
is_critical_db()
{
local db="$1"
list_critical_dbs | grep -Fxq "$db"
}
list_databases_on_fs()
{
local datadir="$1"
# If this is not precise enough, we could check for directories with db.opt files inside
find "$datadir" -mindepth 1 -maxdepth 1 \
-type d ! \( -name 'lost+found' -o -name 'lost@002bfound' -o -name '*#*' \) \
-exec basename {} \;
}
should_restore_db()
{
local db="$1"
local backup="$2"
# Restore Plesk DBs unconditionally
! is_plesk_db "$db" || return 0
# Restore other DBs as long as their total size of non-compressed dumps doesn't exceed 625 Mb.
# This is an estimate of 0.75 percentile across all production servers (assuming 5x compression).
[ -n "$LIMIT_OF_RESTORED_USER_DB_SIZE" ] || LIMIT_OF_RESTORED_USER_DB_SIZE=$((625 * 1024 * 1024))
gunzip < "$backup" 2>/dev/null | is_db_in_sql_dump "$db" || return 1
local dump_size="`gunzip < "$backup" | filter_db_from_sql_dump "$db" | wc -c`"
if [ "$(( LIMIT_OF_RESTORED_USER_DB_SIZE - dump_size ))" -ge 0 ]; then
LIMIT_OF_RESTORED_USER_DB_SIZE="$(( LIMIT_OF_RESTORED_USER_DB_SIZE - dump_size ))"
return 0
fi
return 1
}
repair()
{
local recovery_modes=(${PLESK_REPAIR_INNODB_RECOVERY_MODES:-1 2 3 4 5 6})
local datadir="`get_mysql_datadir`"
local corrupted_ibdata=()
local corrupted_dbs=()
local all_dbs=()
local failed_critical_dbs=()
local missing_dbs=()
local backup_d=
local need_datadir_reinit=
declare -A backups=()
# Special $backups key for all databases - some invalid name for a database - more than 64 chars
local -r ALL="__ALL_DATABASES__________________________________________________"
! is_remote_db_feature_enabled || die "Cannot repair InnoDB corruption on a remote database."
# 0. check which databases are affected
info "(1/6) Checking what should be repaired."
do_check_for_corrupted_files()
{
info "Checking for corrupted InnoDB tablespace files."
mysql_action "stop" || die "Could not stop the database service."
corrupted_ibdata=()
for file in "$datadir"/ibdata*; do
/usr/bin/innochecksum "$file" >/dev/null 2>&1 ||
corrupted_ibdata+=("`basename "$file"`")
done
corrupted_dbs=()
for file in "$datadir"/*/*.ibd; do
/usr/bin/innochecksum "$file" >/dev/null 2>&1 ||
corrupted_dbs+=("$(basename "`dirname "$file"`")")
done
# Heuristic, mostly for ./mysql.ibd (separate tablespace for `mysql` DB).
for file in "$datadir"/*.ibd; do
/usr/bin/innochecksum "$file" >/dev/null 2>&1 ||
corrupted_dbs+=("$(basename "${file%.*}")")
done
corrupted_dbs=(`printf "%s\n" "${corrupted_dbs[@]}" | sort -u`)
# This check is mostly to foolproof the verification stage.
failed_critical_dbs=()
for db in `list_critical_dbs`; do
[ -d "$datadir/$db" ] || failed_critical_dbs+=("$db")
done
[ -z "${corrupted_ibdata[*]}" ] ||
warn "The following system tablespace files are corrupted: ${corrupted_ibdata[*]}"
[ -z "${corrupted_dbs[*]}" ] ||
warn "The following databases contain corrupted tablespace files: ${corrupted_dbs[*]}"
[ -z "${failed_critical_dbs[*]}" ] ||
warn "The following essential databases are missing: ${failed_critical_dbs[*]}"
[ -z "${corrupted_ibdata[*]}" -a -z "${corrupted_dbs[*]}" -a -z "${failed_critical_dbs[*]}" ]
}
mysql_disable_respawn
! do_check_for_corrupted_files || {
mysql_action "start" || die "Failed to start the database service after check for corruption."
success "There are no corrupted InnoDB tablespace files - nothing to repair."
}
# Check if data dir will need to be re-initialized. 'mysql' cannot be reliably repaired alone.
if [ -n "${corrupted_ibdata[*]}" ] || printf "%s\n" "${corrupted_dbs[@]}" | grep -Fxq "mysql"; then
need_datadir_reinit="yes"
fi
all_dbs=(`list_databases_on_fs "$datadir"`)
# 1. back up MySQL datadir
info "(2/6) Backing up the database service data directory."
backup_d="`create_backup_dir`" ||
die "Cannot create directory to store backups."
log_backup_dir_location "$backup_d"
cp -raT --reflink=auto "$datadir" "$backup_d/mysql" ||
die "Cannot backup '$datadir' to '$backup_d/mysql'. Ensure sufficient disk space is available."
info "(3/6) Backing up the affected databases."
do_backup_database()
{
local db="$1"
local mysqldump="$PRODUCT_ROOT_D/admin/sbin/mysqldump.sh"
if [ "$db" = "$ALL" ]; then
"$mysqldump" --dump-dir "$backup_d" --title "$ALL" --all-databases
else
"$mysqldump" --dump-dir "$backup_d" --title "$db" --databases "`db_fname_to_utf8 "$db"`"
fi
}
do_backup_databases()
{
local rc=0
local log_path="$PRODUCT_LOGS_D/mysqldump.log"
for db in "$@"; do
[ -z "${backups["$db"]}" ] || continue
backups["$db"]="`do_backup_database "$db"`" || {
backups["$db"]=
rc=1
[ "$db" = "$ALL" ] || warn "Failed to dump '$db' database. See $log_path for details."
}
done
return "$rc"
}
for mode in "${recovery_modes[@]}"; do
# 2. try innodb_force_recovery
info "Trying to backup databases in InnoDB recovery mode $mode."
change_recovery_mode "$mode" || {
warn "Failed to start the database server in InnoDB recovery mode $mode."
continue
}
# 3. dump databases (not tables)
if [ -n "$need_datadir_reinit" ]; then
do_backup_databases "$ALL" || {
warn "Failed to dump all databases. Will try to dump databases one by one."
do_backup_databases "${all_dbs[@]}" || continue
}
else
do_backup_databases "${corrupted_dbs[@]}" || continue
fi
break
done
[ -n "${backups["$ALL"]}" ] || backups["$ALL"]="`find_latest_preexisting_dump`"
[ -n "${backups["$ALL"]}" ] || warn "No pre-existing Plesk databases dump found."
info "Finished backup into '$backup_d'. Directory size: `du -shD "$backup_d" | cut -f1`."
info "(4/6) Removing the corrupted data."
if [ -n "$need_datadir_reinit" ]; then
# 4-5. re-initialize MySQL data dir, all DBs will effectively be dropped
reinit_mysql_datadir "$datadir"
else
# 4. drop corrupted databases
info "Dropping the corrupted databases."
for db in "${corrupted_dbs[@]}"; do
# In case db_fmt() wasn't able to correctly convert the DB name, it's better to skip the drop
# (which happens due to 'IF EXISTS') than to remove the files manually, since this will prevent
# the respective tables from being re-created (w/o re-initialization of the MySQL data dir).
log_if_failed database_client_invoke "$need_datadir_reinit" -e "DROP DATABASE IF EXISTS `db_fmt "$db"`;" || {
warn "Failed to drop the corrupted database '$db'. Will remove it from the filesystem."
rm -rf "$datadir/$db" || warn "Failed to remove '$datadir/$db'."
}
done
# 5. restart w/o innodb_force_recovery
info "Exiting InnoDB recovery mode."
change_recovery_mode || die "Failed to start the database server in normal mode."
fi
# 6. restore databases from available backups
info "(5/6) Restoring the removed databases from backups."
do_restore_backup()
{
local db="$1"
local backup="${2:-${backups["$db"]}}"
local use_root="$need_datadir_reinit"
[ -f "$backup" ] || return 1
if [ "$db" = "$ALL" ]; then
gunzip < "$backup" |
log_if_failed database_client_invoke "$use_root" || return 1
info "Databases were restored from '$backup' (backup age: `file_age "$backup"`)."
else
gunzip < "$backup" | filter_db_from_sql_dump "$db" |
log_if_failed database_client_invoke "$use_root" || return 1
info "Database '$db' (`db_fmt "$db"`) was restored from '$backup' (backup age: `file_age "$backup"`)."
fi
}
do_restore_databases()
{
local fallback_to_all="$1"
shift
failed_critical_dbs=()
for db in "$@"; do
if [ -f "${backups["$db"]}" ]; then
should_restore_db "$db" "${backups["$db"]}" || {
warn "Database '$db' (`db_fmt "$db"`) will not be restored from '${backups["$db"]}'" \
"to limit repair time."
continue
}
elif [ -n "$fallback_to_all" -a -f "${backups["$ALL"]}" ]; then
should_restore_db "$db" "${backups["$ALL"]}" || {
warn "Database '$db' (`db_fmt "$db"`) will not be restored from '${backups["$ALL"]}'" \
"(either not present or too big)."
continue
}
else
warn "Database '$db' (`db_fmt "$db"`) will not be restored as there is no backup available."
continue
fi
! do_restore_backup "$db" || continue
[ -z "$fallback_to_all" ] ||
! do_restore_backup "$db" "${backups["$ALL"]}" || continue
warn "Failed to restore the '$db' (`db_fmt "$db"`) database from" \
"'${backups["$db"]}'${fallback_to_all:+ or '${backups["$ALL"]}'}."
! is_critical_db "$db" || failed_critical_dbs+=("$db")
done
}
do_check_for_missing_databases()
{
missing_dbs=()
for db in "${all_dbs[@]}"; do
[ -d "$datadir/$db" ] || missing_dbs+=("$db")
done
}
do_post_reinit_mysql()
{
# Mostly to avoid errors in Docker. Don't ask me why.
mysql_action "stop"
# Mainly to remove test DB and extra service users, in case they were re-created.
"$PRODUCT_ROOT_D/admin/sbin/mysqlmng" --post-re-init ||
warn "Failed to re-configure the database server after re-initializing its data directory."
}
if [ -n "$need_datadir_reinit" ]; then
do_restore_backup "$ALL" ||
warn "Failed to restore databases from '${backups["$ALL"]}'." \
"Will try to restore from individual backups, if any."
do_check_for_missing_databases
do_restore_databases "" "${missing_dbs[@]}"
do_post_reinit_mysql
else
do_restore_databases --fallback-to-all "${corrupted_dbs[@]}"
fi
# 7. verify
info "(6/6) Checking if the repair was successful."
[ -z "${failed_critical_dbs[*]}" ] ||
die "Failed to restore some essential databases: ${failed_critical_dbs[*]}"
do_check_for_corrupted_files ||
die "Failed to fully repair InnoDB tablespace files."
mysql_action "start" || die "Failed to start the database service after repair."
do_check_for_missing_databases
[ -z "${missing_dbs[*]}" ] || {
warn "The following databases were not restored, you will need to restore them manually," \
"for example from daily or subscription backups: ${missing_dbs[*]}"
local missing_dbs_sql_str="`db_fmt "${missing_dbs[@]}"`"
if [ "$(echo "$missing_dbs_sql_str" | tr -d '`')" != "${missing_dbs[*]}" ]; then
warn "Here are the same databases, but formatted for SQL rather than filesystem:" \
"$missing_dbs_sql_str"
elif [ -n "$(echo "$missing_dbs_sql_str" | tr -d '[:alnum:]_ `')" ]; then
warn "Most likely this happened because it was impossible to automatically convert" \
"database names found on the filesystem (as listed above) into names formatted for SQL."
fi
}
success "InnoDB tablespace files corruption has been repaired." \
"Make sure the restored database data is up-to-date and complete."
unset do_check_for_corrupted_files
unset do_backup_database
unset do_backup_databases
unset do_restore_backup
unset do_restore_databases
unset do_check_for_missing_databases
unset do_post_reinit_mysql
}
case "$action" in
--check)
check
;;
--repair)
repair
;;
*)
usage
;;
esac
# vim:ft=sh