do/commands/db
2025-07-05 12:06:08 -04:00

209 lines
10 KiB
Bash

# ----------------------------------------------------
# Performs a WordPress database-only backup to a secure, private directory.
# ----------------------------------------------------
function db_backup() {
echo "Starting database-only backup..."
local home_directory; home_directory=$(pwd);
local private_directory
if ! private_directory=$(_get_private_dir); then
return 1
fi
if ! setup_wp_cli; then echo "Error: wp-cli is not installed." >&2; return 1; fi
local database_name; database_name=$("$WP_CLI_CMD" config get DB_NAME --skip-plugins --skip-themes --quiet); local database_username; database_username=$("$WP_CLI_CMD" config get DB_USER --skip-plugins --skip-themes --quiet); local database_password; database_password=$("$WP_CLI_CMD" config get DB_PASSWORD --skip-plugins --skip-themes --quiet);
local dump_command; if command -v mariadb-dump &> /dev/null; then dump_command="mariadb-dump"; elif command -v mysqldump &> /dev/null; then dump_command="mysqldump"; else echo "Error: Neither mariadb-dump nor mysqldump could be found." >&2; return 1; fi
echo "Using ${dump_command} for the backup."
local backup_file="${private_directory}/database-backup-$(date +"%Y-%m-%d").sql"
if ! "${dump_command}" -u"${database_username}" -p"${database_password}" --max_allowed_packet=512M --default-character-set=utf8mb4 --add-drop-table --single-transaction --quick --lock-tables=false "${database_name}" > "${backup_file}"; then echo "Error: Database dump failed." >&2; rm -f "${backup_file}"; return 1; fi
chmod 600 "${backup_file}"; echo "✅ Database backup complete!"; echo " Backup file located at: ${backup_file}"
}
# ----------------------------------------------------
# Checks the size and contents of autoloaded options in the WordPress database.
# ----------------------------------------------------
function db_check_autoload() {
echo "Checking autoloaded options in the database..."
# Ensure the 'gum' utility is available for formatting
if ! setup_gum; then
echo "Aborting check: gum setup failed." >&2
return 1
fi
if ! setup_wp_cli; then echo "Error: wp-cli is not installed." >&2; return 1; fi
if ! "$WP_CLI_CMD" core is-installed --quiet; then echo "Error: This does not appear to be a WordPress installation." >&2; return 1; fi
echo
echo "--- Total Autoloaded Size ---"
"$WP_CLI_CMD" db query "SELECT ROUND(SUM(LENGTH(option_value))/1024/1024, 2) as 'Autoload MB', COUNT(*) as 'Count' FROM $($WP_CLI_CMD db prefix)options WHERE autoload IN ('yes', 'on');" | "$GUM_CMD" table --print --separator $'\t'
echo
echo "--- Top 25 Autoloaded Options & Totals ---"
"$WP_CLI_CMD" db query "SELECT option_name, round(length(option_value) / 1024 / 1024, 2) as 'Size (MB)' FROM $($WP_CLI_CMD db prefix)options WHERE autoload IN ('yes', 'on') ORDER BY length(option_value) DESC LIMIT 25" | "$GUM_CMD" table --print --separator $'\t'
echo
echo "✅ Autoload check complete."
}
# ----------------------------------------------------
# Optimizes the database by converting tables to InnoDB, reporting large tables, and cleaning transients.
# ----------------------------------------------------
function db_optimize() {
# --- Pre-flight checks ---
if ! setup_gum; then
echo "Aborting optimization: gum setup failed." >&2
return 1
fi
if ! setup_wp_cli; then echo "Error: WP-CLI not found." >&2; return 1; fi
if ! "$WP_CLI_CMD" core is-installed --quiet; then echo "Error: This does not appear to be a WordPress installation." >&2; return 1; fi
echo "🚀 Starting database optimization..."
echo ""
# --- Step 1: Convert MyISAM to InnoDB ---
echo "--- Step 1: Checking for MyISAM tables to convert to InnoDB ---"
local myisam_tables
myisam_tables=$("$WP_CLI_CMD" db query "SELECT TABLE_NAME FROM information_schema.TABLES WHERE ENGINE = 'MyISAM' AND TABLE_SCHEMA = DATABASE()" --skip-column-names)
if [[ -z "$myisam_tables" ]]; then
echo "✅ All tables are already using the InnoDB engine. No conversion needed."
else
echo "Found the following MyISAM tables to convert:"
# Use gum to format the list of tables
"$WP_CLI_CMD" db query "SELECT TABLE_NAME AS 'MyISAM Tables' FROM information_schema.TABLES WHERE ENGINE = 'MyISAM' AND TABLE_SCHEMA = DATABASE()" | "$GUM_CMD" table --print --separator $'\t'
echo "Converting tables to InnoDB..."
"$WP_CLI_CMD" db query "SELECT CONCAT('ALTER TABLE \`', TABLE_NAME, '\` ENGINE=InnoDB;') FROM information_schema.TABLES WHERE ENGINE = 'MyISAM' AND TABLE_SCHEMA = DATABASE()" --skip-column-names | "$WP_CLI_CMD" db query
if [ $? -eq 0 ]; then
echo "✅ Successfully converted tables to InnoDB."
else
echo "❌ An error occurred during the conversion."
return 1
fi
fi
# --- Step 2: List Top 10 Largest Tables ---
echo ""
echo "--- Step 2: Top 10 Tables Larger Than 1MB ---"
# Use gum to format the table of large tables
"$WP_CLI_CMD" db query "
SELECT
TABLE_NAME,
CASE
WHEN (data_length + index_length) >= 1073741824 THEN CONCAT(ROUND((data_length + index_length) / 1073741824, 2), ' GB')
WHEN (data_length + index_length) >= 1048576 THEN CONCAT(ROUND((data_length + index_length) / 1048576, 2), ' MB')
WHEN (data_length + index_length) >= 1024 THEN CONCAT(ROUND((data_length + index_length) / 1024, 2), ' KB')
ELSE CONCAT((data_length + index_length), ' B')
END AS Size
FROM
information_schema.TABLES
WHERE
TABLE_SCHEMA = DATABASE() AND (data_length + index_length) > 1048576
ORDER BY
(data_length + index_length) DESC
LIMIT 10;
" | "$GUM_CMD" table --print --separator $'\t'
# --- Step 3: Delete Expired Transients ---
echo ""
echo "--- Step 3: Deleting Expired Transients ---"
"$WP_CLI_CMD" transient delete --expired
echo ""
echo "✅ Database optimization complete."
}
# ----------------------------------------------------
# Changes the database table prefix for a WordPress installation.
# ----------------------------------------------------
function db_change_prefix() {
echo "🚀 Starting Database Prefix Change 🚀"
# --- Pre-flight Checks ---
if ! setup_wp_cli; then echo "❌ Error: WP-CLI not found." >&2; return 1; fi
if ! "$WP_CLI_CMD" core is-installed --quiet; then echo "❌ Error: This does not appear to be a WordPress installation." >&2; return 1; fi
if ! setup_gum; then echo "❌ Error: gum is required for interactive prompts." >&2; return 1; fi
if ! command -v sed &>/dev/null; then echo "❌ Error: sed command not found." >&2; return 1; fi
# --- Get Current and New Prefix ---
local db_prefix
db_prefix=$("$WP_CLI_CMD" db prefix)
echo "Current database prefix is: $db_prefix"
local random_string
# Use a more portable random string generator to avoid locale issues.
if command -v openssl &>/dev/null; then
random_string=$(openssl rand -hex 3) # 6 hex characters
elif command -v md5sum &>/dev/null; then
random_string=$(date +%s | md5sum | head -c 5)
elif command -v sha1sum &>/dev/null; then
random_string=$(date +%s | sha1sum | head -c 5)
else
# A simpler fallback if no hashing tool is found
random_string=$(date +%s | cut -c 6-10) # last 5 digits of timestamp
fi
local db_prefix_new
db_prefix_new=$("$GUM_CMD" input --value="wp_${random_string}_" --prompt="Enter the new database prefix: ")
if [ -z "$db_prefix_new" ]; then
echo "❌ No new prefix entered. Aborting."
return 1
fi
if [ "$db_prefix" == "$db_prefix_new" ]; then
echo "❌ The new prefix is the same as the current prefix. Aborting."
return 1
fi
echo "You are about to change the database prefix from '${db_prefix}' to '${db_prefix_new}'."
"$GUM_CMD" confirm "This is a potentially destructive operation. Are you sure you want to continue?" || { echo "Operation cancelled."; return 0; }
# --- Backup Database ---
local private_dir
if ! private_dir=$(_get_private_dir); then
return 1
fi
local db_file="${private_dir}/prefix-change-backup-$(date +"%Y-%m-%d-%H%M%S").sql"
echo "Step 1/5: Backing up database to a temporary file..."
if ! "$WP_CLI_CMD" db export "$db_file" --add-drop-table; then
echo "❌ Error: Failed to create database backup. Aborting."
return 1
fi
echo "✅ Database backup created at: $db_file"
# --- Modify SQL File ---
echo "Step 2/5: Modifying the database backup with the new prefix..."
# The -i flag for sed behaves differently on macOS/BSD vs Linux.
# Providing a backup extension (e.g., -i.bak) makes it work on both.
sed -i.bak "s#\`${db_prefix}#\`${db_prefix_new}#g" "$db_file"
sed -i.bak "s#'${db_prefix}user_roles#'${db_prefix_new}user_roles#g" "$db_file"
sed -i.bak "s#'${db_prefix}capabilities#'${db_prefix_new}capabilities#g" "$db_file"
sed -i.bak "s#'${db_prefix}user_level#'${db_prefix_new}user_level#g" "$db_file"
echo "✅ SQL file modified."
# --- Reset and Import ---
echo "Step 3/5: Resetting the database (dropping all tables)..."
"$WP_CLI_CMD" db reset --yes
echo "Step 4/5: Importing the modified database..."
if ! "$WP_CLI_CMD" db import "$db_file"; then
echo "❌ Error: Failed to import the modified database."
echo " Your original database is backed up at: $db_file"
echo " You may need to manually restore it."
# Clean up the .bak file
rm -f "${db_file}.bak"
return 1
fi
echo "✅ Database imported successfully."
# Clean up the .bak file created by sed
rm -f "${db_file}.bak"
# --- Update wp-config.php ---
echo "Step 5/5: Updating wp-config.php with the new prefix..."
if ! "$WP_CLI_CMD" config set table_prefix "$db_prefix_new" --skip-plugins --skip-themes; then
echo "❌ Error: Failed to update the table_prefix in your wp-config.php file."
echo " Please update it manually to: \$table_prefix = '$db_prefix_new';"
return 1
fi
echo "✅ wp-config.php updated."
echo ""
echo "✨ Database prefix change complete!"
echo " The backup file from before the change is still available at: $db_file"
}