Files
AzerothCore-RealmMaster/scripts/bash/lib/mysql-utils.sh
2025-12-02 21:43:05 -05:00

376 lines
11 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# MySQL utility library for AzerothCore RealmMaster scripts
# This library provides standardized MySQL operations, connection management,
# and database interaction functions.
#
# Usage: source /path/to/scripts/bash/lib/mysql-utils.sh
#
# Prevent multiple sourcing
if [ -n "${_MYSQL_UTILS_LIB_LOADED:-}" ]; then
return 0
fi
_MYSQL_UTILS_LIB_LOADED=1
# Source common library for logging functions
MYSQL_UTILS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "$MYSQL_UTILS_DIR/common.sh" ]; then
source "$MYSQL_UTILS_DIR/common.sh"
elif command -v info >/dev/null 2>&1; then
# Common functions already available
:
else
# Fallback logging functions
info() { printf '\033[0;34m %s\033[0m\n' "$*"; }
warn() { printf '\033[1;33m⚠ %s\033[0m\n' "$*" >&2; }
err() { printf '\033[0;31m❌ %s\033[0m\n' "$*" >&2; }
fatal() { err "$*"; exit 1; }
fi
# =============================================================================
# MYSQL CONNECTION CONFIGURATION
# =============================================================================
# Default MySQL configuration - can be overridden by environment
MYSQL_HOST="${MYSQL_HOST:-${CONTAINER_MYSQL:-ac-mysql}}"
MYSQL_PORT="${MYSQL_PORT:-3306}"
MYSQL_USER="${MYSQL_USER:-root}"
MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-${MYSQL_PW:-azerothcore}}"
MYSQL_CONTAINER="${MYSQL_CONTAINER:-ac-mysql}"
# =============================================================================
# MYSQL CONNECTION FUNCTIONS
# =============================================================================
# Test MySQL connection with current configuration
# Returns 0 if connection successful, 1 if failed
#
# Usage:
# if mysql_test_connection; then
# echo "MySQL is available"
# fi
#
mysql_test_connection() {
local host="${1:-$MYSQL_HOST}"
local port="${2:-$MYSQL_PORT}"
local user="${3:-$MYSQL_USER}"
local password="${4:-$MYSQL_ROOT_PASSWORD}"
MYSQL_PWD="$password" mysql -h "$host" -P "$port" -u "$user" -e "SELECT 1" >/dev/null 2>&1
}
# Wait for MySQL to be ready with timeout
# Returns 0 if MySQL becomes available within timeout, 1 if timeout reached
#
# Usage:
# mysql_wait_for_connection 60 # Wait up to 60 seconds
# mysql_wait_for_connection # Use default 30 second timeout
#
mysql_wait_for_connection() {
local timeout="${1:-30}"
local retry_interval="${2:-2}"
local elapsed=0
info "Waiting for MySQL connection (${MYSQL_HOST}:${MYSQL_PORT}) with ${timeout}s timeout..."
while [ $elapsed -lt $timeout ]; do
if mysql_test_connection; then
info "MySQL connection established"
return 0
fi
sleep "$retry_interval"
elapsed=$((elapsed + retry_interval))
done
err "MySQL connection failed after ${timeout}s timeout"
return 1
}
# Execute MySQL command with retry logic
# Handles both direct queries and piped input
#
# Usage:
# mysql_exec_with_retry "database_name" "SELECT COUNT(*) FROM table;"
# echo "SELECT 1;" | mysql_exec_with_retry "database_name"
# mysql_exec_with_retry "database_name" < script.sql
#
mysql_exec_with_retry() {
local database="$1"
local query="${2:-}"
local max_attempts="${3:-3}"
local retry_delay="${4:-2}"
local attempt=1
while [ $attempt -le $max_attempts ]; do
if [ -n "$query" ]; then
# Direct query execution
if MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" "$database" -e "$query"; then
return 0
fi
else
# Input from pipe/stdin
if MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" "$database"; then
return 0
fi
fi
if [ $attempt -lt $max_attempts ]; then
warn "MySQL query failed (attempt $attempt/$max_attempts), retrying in ${retry_delay}s..."
sleep "$retry_delay"
fi
attempt=$((attempt + 1))
done
err "MySQL query failed after $max_attempts attempts"
return 1
}
# Execute MySQL query and return result (no table headers)
# Optimized for single values and parsing
#
# Usage:
# count=$(mysql_query "acore_characters" "SELECT COUNT(*) FROM characters")
# tables=$(mysql_query "information_schema" "SHOW TABLES")
#
mysql_query() {
local database="$1"
local query="$2"
local host="${3:-$MYSQL_HOST}"
local port="${4:-$MYSQL_PORT}"
local user="${5:-$MYSQL_USER}"
local password="${6:-$MYSQL_ROOT_PASSWORD}"
MYSQL_PWD="$password" mysql -h "$host" -P "$port" -u "$user" -N -B "$database" -e "$query" 2>/dev/null
}
# =============================================================================
# DOCKER MYSQL FUNCTIONS
# =============================================================================
# Execute MySQL command inside Docker container
# Wrapper around docker exec with standardized MySQL connection
#
# Usage:
# docker_mysql_exec "acore_auth" "SELECT COUNT(*) FROM account;"
# echo "SELECT 1;" | docker_mysql_exec "acore_auth"
#
docker_mysql_exec() {
local database="$1"
local query="${2:-}"
local container="${3:-$MYSQL_CONTAINER}"
local password="${4:-$MYSQL_ROOT_PASSWORD}"
if [ -n "$query" ]; then
docker exec "$container" mysql -uroot -p"$password" "$database" -e "$query"
else
docker exec -i "$container" mysql -uroot -p"$password" "$database"
fi
}
# Execute MySQL query in Docker container (no table headers)
# Optimized for single values and parsing
#
# Usage:
# count=$(docker_mysql_query "acore_characters" "SELECT COUNT(*) FROM characters")
#
docker_mysql_query() {
local database="$1"
local query="$2"
local container="${3:-$MYSQL_CONTAINER}"
local password="${4:-$MYSQL_ROOT_PASSWORD}"
docker exec "$container" mysql -uroot -p"$password" -N -B "$database" -e "$query" 2>/dev/null
}
# Check if MySQL container is healthy and accepting connections
#
# Usage:
# if docker_mysql_is_ready; then
# echo "MySQL container is ready"
# fi
#
docker_mysql_is_ready() {
local container="${1:-$MYSQL_CONTAINER}"
local password="${2:-$MYSQL_ROOT_PASSWORD}"
docker exec "$container" mysqladmin ping -uroot -p"$password" >/dev/null 2>&1
}
# =============================================================================
# DATABASE UTILITY FUNCTIONS
# =============================================================================
# Check if database exists
# Returns 0 if database exists, 1 if not found
#
# Usage:
# if mysql_database_exists "acore_world"; then
# echo "World database found"
# fi
#
mysql_database_exists() {
local database_name="$1"
local result
result=$(mysql_query "information_schema" "SELECT COUNT(*) FROM SCHEMATA WHERE SCHEMA_NAME='$database_name'" 2>/dev/null || echo "0")
[ "$result" -gt 0 ] 2>/dev/null
}
# Get table count for database(s)
# Supports both single database and multiple database patterns
#
# Usage:
# count=$(mysql_get_table_count "acore_world")
# count=$(mysql_get_table_count "acore_auth,acore_characters")
#
mysql_get_table_count() {
local databases="$1"
local schema_list
# Convert comma-separated list to SQL IN clause format
schema_list=$(echo "$databases" | sed "s/,/','/g" | sed "s/^/'/" | sed "s/$/'/")
mysql_query "information_schema" "SELECT COUNT(*) FROM tables WHERE table_schema IN ($schema_list)"
}
# Get database connection string for applications
# Returns connection string in format: host;port;user;password;database
#
# Usage:
# conn_str=$(mysql_get_connection_string "acore_auth")
#
mysql_get_connection_string() {
local database="$1"
local host="${2:-$MYSQL_HOST}"
local port="${3:-$MYSQL_PORT}"
local user="${4:-$MYSQL_USER}"
local password="${5:-$MYSQL_ROOT_PASSWORD}"
printf '%s;%s;%s;%s;%s\n' "$host" "$port" "$user" "$password" "$database"
}
# =============================================================================
# BACKUP AND RESTORE UTILITIES
# =============================================================================
# Create database backup using mysqldump
# Supports both compressed and uncompressed output
#
# Usage:
# mysql_backup_database "acore_characters" "/path/to/backup.sql"
# mysql_backup_database "acore_world" "/path/to/backup.sql.gz" "gzip"
#
mysql_backup_database() {
local database="$1"
local output_file="$2"
local compression="${3:-none}"
local container="${4:-$MYSQL_CONTAINER}"
local password="${5:-$MYSQL_ROOT_PASSWORD}"
info "Creating backup of $database -> $output_file"
case "$compression" in
gzip|gz)
docker exec "$container" mysqldump -uroot -p"$password" "$database" | gzip > "$output_file"
;;
none|*)
docker exec "$container" mysqldump -uroot -p"$password" "$database" > "$output_file"
;;
esac
}
# Restore database from backup file
# Handles both compressed and uncompressed files automatically
#
# Usage:
# mysql_restore_database "acore_characters" "/path/to/backup.sql"
# mysql_restore_database "acore_world" "/path/to/backup.sql.gz"
#
mysql_restore_database() {
local database="$1"
local backup_file="$2"
local container="${3:-$MYSQL_CONTAINER}"
local password="${4:-$MYSQL_ROOT_PASSWORD}"
if [ ! -f "$backup_file" ]; then
err "Backup file not found: $backup_file"
return 1
fi
info "Restoring $database from $backup_file"
case "$backup_file" in
*.gz)
gzip -dc "$backup_file" | docker exec -i "$container" mysql -uroot -p"$password" "$database"
;;
*.sql)
docker exec -i "$container" mysql -uroot -p"$password" "$database" < "$backup_file"
;;
*)
warn "Unknown backup file format, treating as uncompressed SQL"
docker exec -i "$container" mysql -uroot -p"$password" "$database" < "$backup_file"
;;
esac
}
# =============================================================================
# VALIDATION AND DIAGNOSTICS
# =============================================================================
# Validate MySQL configuration and connectivity
# Comprehensive health check for MySQL setup
#
# Usage:
# mysql_validate_configuration
#
mysql_validate_configuration() {
info "Validating MySQL configuration..."
# Check required environment variables
if [ -z "$MYSQL_ROOT_PASSWORD" ]; then
err "MYSQL_ROOT_PASSWORD is not set"
return 1
fi
# Test basic connectivity
if ! mysql_test_connection; then
err "Cannot connect to MySQL at ${MYSQL_HOST}:${MYSQL_PORT}"
return 1
fi
# Check Docker container if using container setup
if docker ps --format "table {{.Names}}" | grep -q "$MYSQL_CONTAINER"; then
if ! docker_mysql_is_ready; then
err "MySQL container $MYSQL_CONTAINER is not ready"
return 1
fi
info "MySQL container $MYSQL_CONTAINER is healthy"
fi
info "MySQL configuration validation successful"
return 0
}
# Print MySQL configuration summary
# Useful for debugging and verification
#
# Usage:
# mysql_print_configuration
#
mysql_print_configuration() {
info "MySQL Configuration Summary:"
info " Host: $MYSQL_HOST"
info " Port: $MYSQL_PORT"
info " User: $MYSQL_USER"
info " Container: $MYSQL_CONTAINER"
info " Password: $([ -n "$MYSQL_ROOT_PASSWORD" ] && echo "***SET***" || echo "***NOT SET***")"
}
# =============================================================================
# INITIALIZATION
# =============================================================================
# Library loaded successfully
# Scripts can check for $_MYSQL_UTILS_LIB_LOADED to verify library is loaded