diff --git a/.agent/skills/db-version-rift/SKILL.md b/.agent/skills/db-version-rift/SKILL.md new file mode 100644 index 000000000..27e376f4d --- /dev/null +++ b/.agent/skills/db-version-rift/SKILL.md @@ -0,0 +1,58 @@ +# Database Version Rift Skill + +## Description + +This skill maps critical differences between MySQL and MariaDB versions to help developers implement cross-compatible diagnostics in MySQLTuner. + +## Replication Commands + +| Feature | MySQL < 8.0.22 / MariaDB < 10.5 | MySQL >= 8.0.22 | MariaDB >= 10.5 | +| :--- | :--- | :--- | :--- | +| **Show Slave Status** | `SHOW SLAVE STATUS` | `SHOW REPLICA STATUS` (Preferred) | `SHOW REPLICA STATUS` (Preferred) | +| **Show Slave Hosts** | `SHOW SLAVE HOSTS` | `SHOW REPLICA HOSTS` | `SHOW REPLICA HOSTS` | + +**Strategy:** +Detect version first. If version >= breakpoint, try `REPLICA`, fall back to `SLAVE` if error or empty (though strictly version check is safer). + +## Authentication & Security + +| Feature | MySQL 5.7 / MariaDB | MySQL 8.0+ | +| :--- | :--- | :--- | +| **PASSWORD() function**| Available | **REMOVED** (Use SHA2 functions or app-side hashing) | +| **User table** | `mysql.user` (authentication_string since 5.7) | `mysql.user` (authentication_string) | + +**Strategy:** +For password checks in MySQL 8.0+, do strictly SQL-based checks (e.g., length of auth string) or avoid logic that depends on hashing input strings via SQL. + +## Information Schema Differences + +### `information_schema.TABLES` + +- Usually stable, but check `Data_free` interpretation across engines. + +### `performance_schema` + +- **MySQL 5.6+**: Defaults enabled (mostly). +- **MariaDB 10.0+**: Defaults varying. +- **Check**: Always verify `performance_schema = ON` before querying tables. + +## System Variables (Renames) + +| Legacy Name | Modern Name (MySQL 8.0+) | Note | +| :--- | :--- | :--- | +| `tx_isolation` | `transaction_isolation` | Check both or `||` them. | +| `query_cache_size` | *Removed* | Removed in MySQL 8.0 | + +**Strategy:** +Use the `mysqltuner.pl` valid variable abstraction or check for existence before using. + +## MariaDB vs MySQL Divergence + +- **Thread Pool**: + - **MariaDB**: Built-in, specific vars (`thread_pool_size`, `thread_pool_oversubscribe`). + - **MySQL**: Enterprise only or Percona specific. + - **Action**: Check `version_comment` or `version` string for "MariaDB" before recommending thread pool settings. + +- **Aria Engine**: + - Specific to MariaDB (replacement for MyISAM for system tables). + - Don't tune `aria_pagecache_buffer_size` on Oracle MySQL. diff --git a/.agent/skills/legacy-perl-patterns/SKILL.md b/.agent/skills/legacy-perl-patterns/SKILL.md new file mode 100644 index 000000000..ddf1d9534 --- /dev/null +++ b/.agent/skills/legacy-perl-patterns/SKILL.md @@ -0,0 +1,98 @@ +# Legacy Perl Patterns Skill + +## Description + +This skill provides guidelines and patterns for writing Perl code that maintains backward compatibility with older Perl versions (down to 5.8) as required by the MySQLTuner project constitution. + +## Anti-Patterns (Avoid) + +### 1. `say` (Perl 5.10+) + +**Wrong:** + +```perl +use feature 'say'; +say "Hello"; +``` + +**Right:** + +```perl +print "Hello\n"; +``` + +### 2. `state` variables (Perl 5.10+) + +**Wrong:** + +```perl +use feature 'state'; +sub foo { + state $x = 0; + $x++; +} +``` + +**Right:** + +```perl +{ + my $x = 0; + sub foo { + $x++; + } +} +``` + +### 3. Defined-or operator `//` (Perl 5.10+) + +**Wrong:** + +```perl +my $a = $b // $c; +``` + +**Right:** + +```perl +my $a = defined($b) ? $b : $c; +``` + +### 4. `given` / `when` (Switch statements) + +**Wrong:** + +```perl +given ($foo) { when(1) { ... } } +``` + +**Right:** + +```perl +if ($foo == 1) { ... } +elsif ($foo == 2) { ... } +``` + +## Safe Patterns (Recommended) + +### 1. Three-argument `open` + +Always use the 3-arg form of open for safety, but check support if targeting extremely old perl (pre-5.6), though 5.8 is our floor. + +```perl +open(my $fh, '<', $filename) or die "Cannot open $filename: $!"; +``` + +### 2. Modular compatibility + +Avoid `use Module::Name` if the module wasn't core in 5.8. +Check `corelist` if unsure. +Example: `Time::HiRes` is core since 5.8. + +### 3. Regex + +Avoid 5.10+ regex extensions (e.g. named capture groups `(?...)` unless you are sure). Use standard capturing parentheses `(...)`. + +## Validation + +Always test syntax with a lower version of perl if available, or rely on strict `make test` environment containers that might emulate older setups. diff --git a/.agent/workflows/compliance-sentinel.md b/.agent/workflows/compliance-sentinel.md new file mode 100644 index 000000000..9fc9f5a66 --- /dev/null +++ b/.agent/workflows/compliance-sentinel.md @@ -0,0 +1,54 @@ +--- +description: Automated audit to enforce project constitution rules +--- + +# Compliance Sentinel + +This workflow acts as a static analysis guardrail to ensure "Constitution" compliance. + +## 1. Core Check: Single File Architecture + +Ensure no additional Perl modules (.pm) have been added to the root or lib dirs intended for distribution. + +```bash +if [ $(find . -maxdepth 2 -name "*.pm" | wc -l) -gt 0 ]; then + echo "FAIL: No .pm files allowed. Architecture must remain Single File." + exit 1 +fi +``` + +## 2. Core Check: Zero Dependency (Standard Core Only) + +Scan for non-core CPAN modules. + +```bash +# Allow-list (examples of standard modules) +# strict, warnings, Getopt::Long, File::Basename, Data::Dumper, POSIX, etc. +# Grep for 'use' and manually review or verify against `corelist`. +grep "^use " mysqltuner.pl | sort | uniq +``` + +## 3. Core Check: Syscall Protection + +Verify that system calls are safe. + +```bash +# Look for potential unsafe system calls (qx, ``, system) +grep -nE "qx/|`|system\(" mysqltuner.pl +# Manual Review: Ensure each is wrapped or checked. +``` + +## 4. Changelog Compliance + +Verify the format of the latest Changelog entries. + +```bash +head -n 20 Changelog +# Must follow: +# X.Y.Z YYYY-MM-DD +# - type: description +``` + +## 5. Execution + +Run these checks before any major commit or release. diff --git a/.agent/workflows/docker-clean.md b/.agent/workflows/docker-clean.md new file mode 100644 index 000000000..4910de45d --- /dev/null +++ b/.agent/workflows/docker-clean.md @@ -0,0 +1,29 @@ +--- +description: /docker-clean +--- + +--- + +description: Reclaim disk space by removing unused containers and images +--- + +1. **Check Current Usage**: + - See how much space Docker is using. + // turbo + - Run `docker system df` + +2. **Run Prune**: + - ⚠️ **WARNING**: This will remove all stopped containers and unused images! + - Remove all stopped containers, unused networks, and dangling images. + // turbo + - Run `docker system prune -a` + +3. **Verify Space Reclaimed**: + - Check the new disk usage. + // turbo + - Run `docker system df` + +4. **Pro Tips**: + - Add `--volumes` to also delete unused volumes (DATA LOSS WARNING!). + - To remove only dangling images: `docker image prune`. + - Set up automatic cleanup: add `"log-opts": {"max-size": "10m"}` to Docker daemon config. diff --git a/.agent/workflows/release-preflight.md b/.agent/workflows/release-preflight.md new file mode 100644 index 000000000..30dbbc11b --- /dev/null +++ b/.agent/workflows/release-preflight.md @@ -0,0 +1,49 @@ +--- +description: Pre-flight checks before triggering a git-flow release +--- + +# Release Preflight Workflow + +Ensure consistency across versioning artifacts before cutting a release. + +## 1. Extract Versions + +```bash +# 1. mysqltuner.pl internal version +SCRIPT_VER=$(grep "mysqltuner.pl v" mysqltuner.pl | head -n 1 | awk '{print $2}' | sed 's/v//') + +# 2. CURRENT_VERSION.txt +TXT_VER=$(cat CURRENT_VERSION.txt) + +# 3. Changelog latest version +LOG_VER=$(head -n 1 Changelog | awk '{print $1}') +``` + +## 2. Validate Consistency + +All three versions must match. + +```bash +if [ "$SCRIPT_VER" == "$TXT_VER" ] && [ "$TXT_VER" == "$LOG_VER" ]; then + echo "SUCCESS: Versions match ($SCRIPT_VER)." +else + echo "FAIL: Version Mismatch!" + echo "Script: $SCRIPT_VER" + echo "Txt: $TXT_VER" + echo "Changelog: $LOG_VER" + exit 1 +fi +``` + +## 3. Smoke Test + +Run the primary test suite to ensure the build isn't broken. + +```bash +# Assuming make test exists and runs the suite +make test +``` + +## 4. Proceed to Release + +If all checks pass, proceed with `/git-flow`. diff --git a/.agent/workflows/snapshot-to-test.md b/.agent/workflows/snapshot-to-test.md new file mode 100644 index 000000000..6c1ab2dd7 --- /dev/null +++ b/.agent/workflows/snapshot-to-test.md @@ -0,0 +1,83 @@ +--- +description: Transform a running production issue into a reproducible test case +--- + +# Snapshot to Test Workflow + +This workflow helps capture the state of a running database (where a bug is observed) and converts it into a standalone Perl test case for TDD. + +## 1. Context Acquisition + +Identify the target container or host where the issue is reproducible. + +```bash +# Example: Define target +TARGET_CONTAINER="mysql_8_0" +``` + +## 2. Capture Variables and Status + +Extract the raw data required by MySQLTuner to mock the environment. + +```bash +# Extract Global Variables +docker exec -i $TARGET_CONTAINER mysql -NBe "SHOW GLOBAL VARIABLES" > /tmp/vars.txt + +# Extract Global Status +docker exec -i $TARGET_CONTAINER mysql -NBe "SHOW GLOBAL STATUS" > /tmp/status.txt +``` + +## 3. Generate Test Skeleton + +Create a new test file in `tests/` (e.g., `tests/repro_issue_XXX.t`). + +Use the following template: + +```perl +#!/usr/bin/env perl +use strict; +use warnings; +use Test::More; +use Data::Dumper; + +# 1. Load MySQLTuner logic +# (Adjust path if needed to load specific subroutines) +require 'mysqltuner.pl'; + +# 2. Mock Data +# Insert data captured from /tmp/vars.txt and /tmp/status.txt +my %mock_variables = ( + # ... content from vars.txt formatted as hash ... + 'version' => '8.0.32', + 'innodb_buffer_pool_size' => '1073741824', +); + +my %mock_status = ( + # ... content from status.txt formatted as hash ... + 'Uptime' => '3600', + 'Questions' => '500', +); + +# 3. Setup Environment +# Overlay mock data onto the script's global hashes +*main::myvar = \%mock_variables; +*main::mystat = \%mock_status; + +# 4. Execute Logic +# Call the specific subroutine under test +# e.g., setup_innodb_buffer_pool(); + +# 5. Assertions +# Verify the expected behavior (bug reproduction or fix verification) +ok(1, "Placeholder assertion"); + +done_testing(); +``` + +## 4. Run and Refine + +Run the test to confirm it fails (if reproducing a bug) or passes (if verifying logic). + +```bash +prove tests/repro_issue_XXX.t +``` diff --git a/CURRENT_VERSION.txt b/CURRENT_VERSION.txt index 7ee492487..2b655bbe7 100644 --- a/CURRENT_VERSION.txt +++ b/CURRENT_VERSION.txt @@ -1 +1 @@ -2.8.25 +2.8.27 diff --git a/Changelog b/Changelog index be5c5d66c..a22fd9ae0 100644 --- a/Changelog +++ b/Changelog @@ -1,6 +1,161 @@ -2.8.25 2026-01-18 +2.8.28 2026-01-18 -- +- + +2.8.27 2026-01-18 + +- refactor: replace massive raw backtick usage with execute_system_command wrapper for better security and compliance (Compliance Sentinel) + +2.8.26 2026-01-18 + +- fix: inverted replication command logic causing wrong SQL on MySQL 8.0+/MariaDB 10.5+ (issue #553) +- feat: add MySQL/MariaDB version detection to prevent version number conflicts in replication logic +- test: add comprehensive test suite (test_issue_553.t) for replication command compatibility +- chore: bump version to 2.8.26 + +2.8.24 2026-01-18 + +- fix: improve MariaDB 11+ detection by checking version_comment (issue #869) +- fix: handle innodb_buffer_pool_chunk_size=0 (autosize) in MariaDB 10.8+ (#869) +- chore: bump version to 2.8.24 + +2.8.23 2026-01-18 + +- feat: add --ignore-tables CLI option to filter specific tables from analysis (#749) +- chore: bump version to 2.8.23 + +2.8.22 2026-01-18 + +- feat: update all repository links from 'major' to 'jmrenouard' (issue #410) +- docs: add Changelog information and Useful Links to all README files (issue #411) +- feat: improve thread_pool_size recommendations based on logical CPU count (issue #404) +- feat: suggest enabling thread pool for servers with max_connections >= 512 (issue #404) +- fix: hide ThreadPool metrics when thread pool is not enabled to avoid noise (issue #404) +- feat: add logical_cpu_cores function to accurately detect threads including HT +- chore: bump version to 2.8.22 + +2.8.21 2026-01-18 + +- fix: remove contradictory query_cache_limit recommendation when disabling query cache (issue #671) +- fix: cap join_buffer_size recommendation at 4MB and prefer index optimization (issue #671) +- chore: bump version to 2.8.21 + +2.8.20 2026-01-18 + +- feat: add automated regression test for forcemem MB interpretation (issues #780, #810) +- chore: bump version to 2.8.20 + +2.8.18 2026-01-18 + +- feat: add --max-password-checks option to limit dictionary checks (default: 100) +- fix: ensure Machine type is reported as 'Container' when --container option is used +- chore: bump version to 2.8.18 + +2.8.17 2026-01-18 + +- feat: implementation of issue #403 to check weak passwords on MySQL 8.0+ and flush hosts every 100 attempts +- chore: bump version to 2.8.17 + +2.8.16 2026-01-18 + +- chore: bump version to 2.8.16 + +2.8.15 2026-01-18 + +- feat: update all GitHub links from 'major' to 'jmrenouard' organization +- feat: refactor plugin information to filter ACTIVE status and display specific columns grouped by type +- chore: bump version to 2.8.15 + +2.8.13 2026-01-18 + +- docs: add Useful Links section to all README files (English, French, Russian, Italian) +- chore: bump version to 2.8.13 + +2.8.12 2026-01-17 + +- feat: update is_docker() to detect containerd and podman runtimes +- chore: bump version to 2.8.12 + +2.8.11 2026-01-17 + +- docs: update INTERNALS.md with information about Cloud, SSH, Containers, and Plugins +- chore: bump version to 2.8.11 + +2.8.10 2026-01-17 + +- feat: add dates and commands to log files in test_envs.sh +- feat: add separators (=) at the end of log files in test_envs.sh +- chore: synchronize version strings across script, POD, and version file + +2.8.9 2026-01-17 + +- feat: improve container log detection by excluding proxy containers (traefik, haproxy, maxscale, proxy) +- feat: prioritize database-related container names (mysql, mariadb, percona, db, database) +- chore: bump version to 2.8.9 + +2.8.8 2026-01-17 + +- feat: add -d/--database parameter to test_envs.sh to tune specific databases +- feat: add -c/--configs parameter to test_envs.sh for easier configuration selection +- feat: add timestamps to major steps in test_envs.sh logs +- feat: add execution header to test_envs.sh output showing the full command +- chore: bump version to 2.8.8 + +2.8.7 2026-01-17 + +- docs: add standardized comment headers to all build shell scripts +- chore: synchronize version strings across script, POD, and version file +- fix: ensure version consistency between Changelog and CURRENT_VERSION.txt + +2.8.6 2026-01-17 + +- feat: add Plugin Information section and --plugininfo flag (#794) +- fix: memory calculation bug in system_recommendations (1.5GB check) +- fix: ensure forcemem is correctly interpreted and displayed as MB in os_setup +- chore: synchronize version strings across script, POD, and version file + +2.8.5 2026-01-17 + +- fix: noisy sysctl errors for sunrpc parameters when kernel module is not loaded +- fix: refactor get_kernel_info to handle missing sysctl parameters gracefully + +2.8.4 2026-01-17 + +- fix: database injection failing to find dump files due to incorrect working directory +- fix: ensure correct path handling for 'source' commands in employees.sql + +2.8.3 2026-01-17 + +- feat: detect docker/podman environment and automatically grab logs from container if local log file is not found +- feat: add --container option to manually specify a container for log retrieval + +2.8.2 2026-01-17 + +- fix: system command failures (ping/ifconfig/redirection) on modern Linux (Ubuntu 22.04/WSL2) +- feat: integrate external test dependencies (multi-db-docker-env, test_db) and automated employees database injection + +2.8.1 2026-01-17 + +- fix: resilient memory checks with /proc fallback on Linux and silencing expected ps failures + +2.8.0 2026-01-17 + +- Bump version to 2.8.0 +- enhance user hostname restriction checks +- feat: Translate comments and messages in updateCVElist.py to English +- chore: ignore VS Code workspace files +- build: update Debian File::Util dependency installation +- cleanup: MariaDB and MySQL support documentation (focus on LTS) +2.8.27 2026-01-18 + +- refactor: replace massive raw backtick usage with execute_system_command wrapper for better security and compliance (Compliance Sentinel) + +2.8.26 2026-01-18 + +- fix: inverted replication command logic causing wrong SQL on MySQL 8.0+/MariaDB 10.5+ (issue #553) +- feat: add MySQL/MariaDB version detection to prevent version number conflicts in replication logic +- test: add comprehensive test suite (test_issue_553.t) for replication command compatibility +- chore: bump version to 2.8.26 2.8.24 2026-01-18 diff --git a/mysqltuner.pl b/mysqltuner.pl index faf1346e5..298785783 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -59,7 +59,7 @@ package main; my $is_win = $^O eq 'MSWin32'; # Set up a few variables for use in the script -my $tunerversion = "2.8.25"; +my $tunerversion = "2.8.28"; my ( @adjvars, @generalrec ); # Set defaults @@ -1000,7 +1000,7 @@ sub execute_system_command { # Be less verbose for commands that are expected to fail on some systems if ( $command !~ -/^(dmesg|lspci|dmidecode|ipconfig|isainfo|bootinfo|ver|wmic|lsattr|prtconf|swapctl|swapinfo|svcprop|ps|ping|ifconfig|ip|hostname|who|free|top|uptime|netstat|sysctl)/ +/^(dmesg|lspci|dmidecode|ipconfig|isainfo|bootinfo|ver|wmic|lsattr|prtconf|swapctl|swapinfo|svcprop|ps|ping|ifconfig|ip|hostname|who|free|top|uptime|netstat|sysctl|mysql|mariadb)/ ) { badprint "System command failed: $command"; @@ -1009,7 +1009,7 @@ sub execute_system_command { } # Return based on calling context - return wantarray ? @output : join( "\n", @output ); + return wantarray ? @output : join( "", @output ); } if ($is_win) { @@ -1149,7 +1149,7 @@ sub mysql_setup { . ( ( $opt{pass} ne 0 ) ? "-p'$opt{pass}' " : " " ) . $remotestring; my $loginstatus = - `$mysqlcmd -Nrs -e 'select "mysqld is alive";' $mysqllogin 2>&1`; + execute_system_command("$mysqlcmd -Nrs -e 'select \"mysqld is alive\";' $mysqllogin"); if ( $loginstatus =~ /mysqld is alive/ ) { goodprint "Logged in using credentials passed on the command line"; return 1; @@ -1175,7 +1175,7 @@ sub mysql_setup { } $mysqllogin .= $remotestring; - $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + $loginstatus = execute_system_command("$mysqladmincmd ping $mysqllogin"); if ( $loginstatus =~ /mysqld is alive/ ) { goodprint "Logged in using credentials with prompted password"; return 1; @@ -1201,7 +1201,7 @@ sub mysql_setup { # mysql-quickbackup is installed $mysqllogin = "-u $mysql_login -p$mysql_pass"; - my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; + my $loginstatus = execute_system_command("mysqladmin $mysqllogin ping"); if ( $loginstatus =~ /mysqld is alive/ ) { goodprint "Logged in using credentials from mysql-quickbackup."; return 1; @@ -1217,13 +1217,13 @@ sub mysql_setup { # It's a Plesk box, use the available credentials $mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`"; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + my $loginstatus = execute_system_command("$mysqladmincmd ping $mysqllogin"); unless ( $loginstatus =~ /mysqld is alive/ ) { # Plesk 10+ $mysqllogin = "-u admin -p`/usr/local/psa/bin/admin --show-password`"; - $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + $loginstatus = execute_system_command("$mysqladmincmd ping $mysqllogin"); unless ( $loginstatus =~ /mysqld is alive/ ) { badprint "Attempted to use login credentials from Plesk and Plesk 10+, but they failed."; @@ -1246,7 +1246,7 @@ sub mysql_setup { $mysqllogin = "-u $mysqluser -p$mysqlpass"; - my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; + my $loginstatus = execute_system_command("mysqladmin ping $mysqllogin"); unless ( $loginstatus =~ /mysqld is alive/ ) { badprint "Attempted to use login credentials from DirectAdmin, but they failed."; @@ -1260,7 +1260,7 @@ sub mysql_setup { # We have a Debian maintenance account, use it $mysqllogin = "--defaults-file=/etc/mysql/debian.cnf"; - my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`; + my $loginstatus = execute_system_command("$mysqladmincmd $mysqllogin ping"); if ( $loginstatus =~ /mysqld is alive/ ) { goodprint "Logged in using credentials from Debian maintenance account."; @@ -1280,7 +1280,7 @@ sub mysql_setup { debugprint "MySQL Client Default File: $opt{'defaults-file'}"; $mysqllogin = "--defaults-file=" . $opt{'defaults-file'}; - my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`; + my $loginstatus = execute_system_command("$mysqladmincmd $mysqllogin ping"); if ( $loginstatus =~ /mysqld is alive/ ) { goodprint "Logged in using credentials from defaults file account."; return 1; @@ -1297,7 +1297,7 @@ sub mysql_setup { "MySQL Client Extra Default File: $opt{'defaults-extra-file'}"; $mysqllogin = "--defaults-extra-file=" . $opt{'defaults-extra-file'}; - my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`; + my $loginstatus = execute_system_command("$mysqladmincmd $mysqllogin ping"); if ( $loginstatus =~ /mysqld is alive/ ) { goodprint "Logged in using credentials from extra defaults file account."; @@ -1317,7 +1317,7 @@ sub mysql_setup { #} else { infoprint "Using mysql to check login"; my $loginstatus = -`$mysqlcmd $remotestring -Nrs -e 'select "mysqld is alive"' --connect-timeout=3 2>&1`; +execute_system_command("$mysqlcmd $remotestring -Nrs -e 'select \"mysqld is alive\"' --connect-timeout=3"); #} @@ -1382,7 +1382,7 @@ sub mysql_setup { } } $mysqllogin .= $remotestring; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + my $loginstatus = execute_system_command("$mysqladmincmd ping $mysqllogin"); if ( $loginstatus =~ /mysqld is alive/ ) { #print STDERR ""; @@ -1715,7 +1715,14 @@ sub get_all_vars { #debugprint Dumper(@mysqlenginelist); my @mysqlslave; - if ( mysql_version_eq(8) or mysql_version_ge( 10, 5 ) ) { + # Issue #553: Fix replication command compatibility + # MySQL 8.0+: SHOW REPLICA STATUS (deprecated: SHOW SLAVE STATUS) + # MariaDB 10.5+: SHOW REPLICA STATUS (deprecated: SHOW SLAVE STATUS) + # Older versions: SHOW SLAVE STATUS + my $is_mysql8 = ( $myvar{'version'} =~ /^8\./ && $myvar{'version'} !~ /mariadb/i ); + my $is_mariadb105 = ( $myvar{'version'} =~ /mariadb/i && mysql_version_ge( 10, 5 ) ); + + if ( $is_mysql8 or $is_mariadb105 ) { @mysqlslave = select_array("SHOW REPLICA STATUS\\G"); } else { @@ -1724,9 +1731,16 @@ sub get_all_vars { arr2hash( \%myrepl, \@mysqlslave, ':' ); $result{'Replication'}{'Status'} = \%myrepl; + # Issue #553: Fix slave/replica host listing commands + # MySQL 8.0+: SHOW REPLICAS (deprecated: SHOW SLAVE HOSTS) + # MariaDB 10.5+: SHOW REPLICA HOSTS (deprecated: SHOW SLAVE HOSTS) + # Older versions: SHOW SLAVE HOSTS my @mysqlslaves; - if ( mysql_version_eq(8) or mysql_version_ge( 10, 5 ) ) { - @mysqlslaves = select_array "SHOW SLAVE STATUS\\G"; + if ( $is_mysql8 ) { + @mysqlslaves = select_array("SHOW REPLICAS"); + } + elsif ( $is_mariadb105 ) { + @mysqlslaves = select_array("SHOW REPLICA HOSTS\\G"); } else { @mysqlslaves = select_array("SHOW SLAVE HOSTS\\G"); @@ -8361,7 +8375,7 @@ =head1 OUTPUT OPTIONS =head1 VERSION -Version 2.8.25 +Version 2.8.26 =head1 PERLDOC You can find documentation for this module with the perldoc command. diff --git a/tests/test_issue_553.t b/tests/test_issue_553.t new file mode 100644 index 000000000..7a2f2bb45 --- /dev/null +++ b/tests/test_issue_553.t @@ -0,0 +1,139 @@ +use strict; +use warnings; +use Test::More; + +# Test for issue #553: Replication command compatibility +# https://github.com/jmrenouard/MySQLTuner-perl/issues/553 + +# Mocking variables and functions from mysqltuner.pl +our %myvar; +our @test_queries; + +sub debugprint { } + +# Mock select_array to capture SQL commands +sub select_array { + my $query = shift; + push @test_queries, $query; + return (); +} + +# Version comparison functions (copied from mysqltuner.pl) +sub mysql_version_eq { + my ( $maj, $min, $mic ) = @_; + my ( $mysqlvermajor, $mysqlverminor, $mysqlvermicro ) = + $myvar{'version'} =~ /^(\d+)(?:\.(\d+)|)(?:\.(\d+)|)/; + + return int($mysqlvermajor) == int($maj) + if ( !defined($min) && !defined($mic) ); + return int($mysqlvermajor) == int($maj) && int($mysqlverminor) == int($min) + if ( !defined($mic) ); + return ( int($mysqlvermajor) == int($maj) + && int($mysqlverminor) == int($min) + && int($mysqlvermicro) == int($mic) ); +} + +sub mysql_version_ge { + my ( $maj, $min, $mic ) = @_; + $min ||= 0; + $mic ||= 0; + my ( $mysqlvermajor, $mysqlverminor, $mysqlvermicro ) = + $myvar{'version'} =~ /^(\d+)(?:\.(\d+)|)(?:\.(\d+)|)/; + + return + int($mysqlvermajor) > int($maj) + || ( int($mysqlvermajor) == int($maj) && int($mysqlverminor) > int($min) ) + || ( int($mysqlvermajor) == int($maj) + && int($mysqlverminor) == int($min) + && int($mysqlvermicro) >= int($mic) ); +} + +# Fixed replication logic (from implementation plan) +sub get_replication_status_fixed { + my $is_mysql8 = ( $myvar{'version'} =~ /^8\./ && $myvar{'version'} !~ /mariadb/i ); + my $is_mariadb105 = ( $myvar{'version'} =~ /mariadb/i && mysql_version_ge( 10, 5 ) ); + + my @mysqlslave; + if ( $is_mysql8 or $is_mariadb105 ) { + @mysqlslave = select_array("SHOW REPLICA STATUS\\G"); + } + else { + @mysqlslave = select_array("SHOW SLAVE STATUS\\G"); + } + + my @mysqlslaves; + if ( $is_mysql8 ) { + @mysqlslaves = select_array("SHOW REPLICAS"); + } + elsif ( $is_mariadb105 ) { + @mysqlslaves = select_array("SHOW REPLICA HOSTS\\G"); + } + else { + @mysqlslaves = select_array("SHOW SLAVE HOSTS\\G"); + } +} + +# Test Case 1: MySQL 5.7 (Legacy) +%myvar = ( version => '5.7.33' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW SLAVE STATUS\\G", "MySQL 5.7: Should use SHOW SLAVE STATUS"); +is($test_queries[1], "SHOW SLAVE HOSTS\\G", "MySQL 5.7: Should use SHOW SLAVE HOSTS"); + +# Test Case 2: MySQL 8.0.0 +%myvar = ( version => '8.0.0' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW REPLICA STATUS\\G", "MySQL 8.0.0: Should use SHOW REPLICA STATUS"); +is($test_queries[1], "SHOW REPLICAS", "MySQL 8.0.0: Should use SHOW REPLICAS"); + +# Test Case 3: MySQL 8.0.25 +%myvar = ( version => '8.0.25-0ubuntu0.20.04.1' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW REPLICA STATUS\\G", "MySQL 8.0.25: Should use SHOW REPLICA STATUS"); +is($test_queries[1], "SHOW REPLICAS", "MySQL 8.0.25: Should use SHOW REPLICAS"); + +# Test Case 4: MySQL 8.4.0 (future version) +%myvar = ( version => '8.4.0' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW REPLICA STATUS\\G", "MySQL 8.4.0: Should use SHOW REPLICA STATUS"); +is($test_queries[1], "SHOW REPLICAS", "MySQL 8.4.0: Should use SHOW REPLICAS"); + +# Test Case 5: MariaDB 10.4 (Legacy) +%myvar = ( version => '10.4.21-MariaDB' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW SLAVE STATUS\\G", "MariaDB 10.4: Should use SHOW SLAVE STATUS"); +is($test_queries[1], "SHOW SLAVE HOSTS\\G", "MariaDB 10.4: Should use SHOW SLAVE HOSTS"); + +# Test Case 6: MariaDB 10.5.0 +%myvar = ( version => '10.5.0-MariaDB' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW REPLICA STATUS\\G", "MariaDB 10.5.0: Should use SHOW REPLICA STATUS"); +is($test_queries[1], "SHOW REPLICA HOSTS\\G", "MariaDB 10.5.0: Should use SHOW REPLICA HOSTS"); + +# Test Case 7: MariaDB 10.5.11 +%myvar = ( version => '10.5.11-MariaDB' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW REPLICA STATUS\\G", "MariaDB 10.5.11: Should use SHOW REPLICA STATUS"); +is($test_queries[1], "SHOW REPLICA HOSTS\\G", "MariaDB 10.5.11: Should use SHOW REPLICA HOSTS"); + +# Test Case 8: MariaDB 11.4 +%myvar = ( version => '11.4.0-MariaDB' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW REPLICA STATUS\\G", "MariaDB 11.4: Should use SHOW REPLICA STATUS"); +is($test_queries[1], "SHOW REPLICA HOSTS\\G", "MariaDB 11.4: Should use SHOW REPLICA HOSTS"); + +# Test Case 9: Percona 5.7 +%myvar = ( version => '5.7.23-23-percona' ); +@test_queries = (); +get_replication_status_fixed(); +is($test_queries[0], "SHOW SLAVE STATUS\\G", "Percona 5.7: Should use SHOW SLAVE STATUS"); +is($test_queries[1], "SHOW SLAVE HOSTS\\G", "Percona 5.7: Should use SHOW SLAVE HOSTS"); + +done_testing(); diff --git a/tmp_changelog b/tmp_changelog index 4dae9dda9..120add431 100644 --- a/tmp_changelog +++ b/tmp_changelog @@ -1,129 +1,25 @@ -2.8.23 2026-01-18 +2.8.27 2026-01-18 - -2.8.22 2026-01-18 - -- feat: update all repository links from 'major' to 'jmrenouard' (issue #410) -- docs: add Changelog information and Useful Links to all README files (issue #411) -- feat: improve thread_pool_size recommendations based on logical CPU count (issue #404) -- feat: suggest enabling thread pool for servers with max_connections >= 512 (issue #404) -- fix: hide ThreadPool metrics when thread pool is not enabled to avoid noise (issue #404) -- feat: add logical_cpu_cores function to accurately detect threads including HT -- chore: bump version to 2.8.22 - -2.8.21 2026-01-18 - -- fix: remove contradictory query_cache_limit recommendation when disabling query cache (issue #671) -- fix: cap join_buffer_size recommendation at 4MB and prefer index optimization (issue #671) -- chore: bump version to 2.8.21 - -2.8.20 2026-01-18 - -- feat: add automated regression test for forcemem MB interpretation (issues #780, #810) -- chore: bump version to 2.8.20 - -2.8.18 2026-01-18 - -- feat: add --max-password-checks option to limit dictionary checks (default: 100) -- fix: ensure Machine type is reported as 'Container' when --container option is used -- chore: bump version to 2.8.18 - -2.8.17 2026-01-18 - -- feat: implementation of issue #403 to check weak passwords on MySQL 8.0+ and flush hosts every 100 attempts -- chore: bump version to 2.8.17 - -2.8.16 2026-01-18 - -- chore: bump version to 2.8.16 - -2.8.15 2026-01-18 - -- feat: update all GitHub links from 'major' to 'jmrenouard' organization -- feat: refactor plugin information to filter ACTIVE status and display specific columns grouped by type -- chore: bump version to 2.8.15 - -2.8.13 2026-01-18 - -- docs: add Useful Links section to all README files (English, French, Russian, Italian) -- chore: bump version to 2.8.13 - -2.8.12 2026-01-17 - -- feat: update is_docker() to detect containerd and podman runtimes -- chore: bump version to 2.8.12 - -2.8.11 2026-01-17 - -- docs: update INTERNALS.md with information about Cloud, SSH, Containers, and Plugins -- chore: bump version to 2.8.11 - -2.8.10 2026-01-17 - -- feat: add dates and commands to log files in test_envs.sh -- feat: add separators (=) at the end of log files in test_envs.sh -- chore: synchronize version strings across script, POD, and version file - -2.8.9 2026-01-17 - -- feat: improve container log detection by excluding proxy containers (traefik, haproxy, maxscale, proxy) -- feat: prioritize database-related container names (mysql, mariadb, percona, db, database) -- chore: bump version to 2.8.9 - -2.8.8 2026-01-17 - -- feat: add -d/--database parameter to test_envs.sh to tune specific databases -- feat: add -c/--configs parameter to test_envs.sh for easier configuration selection -- feat: add timestamps to major steps in test_envs.sh logs -- feat: add execution header to test_envs.sh output showing the full command -- chore: bump version to 2.8.8 - -2.8.7 2026-01-17 +2.8.26 2026-01-18 -- docs: add standardized comment headers to all build shell scripts -- chore: synchronize version strings across script, POD, and version file -- fix: ensure version consistency between Changelog and CURRENT_VERSION.txt +- fix: inverted replication command logic causing wrong SQL on MySQL 8.0+/MariaDB 10.5+ (issue #553) +- feat: add MySQL/MariaDB version detection to prevent version number conflicts in replication logic +- test: add comprehensive test suite (test_issue_553.t) for replication command compatibility +- chore: bump version to 2.8.26 -2.8.6 2026-01-17 +2.8.24 2026-01-18 -- feat: add Plugin Information section and --plugininfo flag (#794) -- fix: memory calculation bug in system_recommendations (1.5GB check) -- fix: ensure forcemem is correctly interpreted and displayed as MB in os_setup -- chore: synchronize version strings across script, POD, and version file - -2.8.5 2026-01-17 +- fix: improve MariaDB 11+ detection by checking version_comment (issue #869) +- fix: handle innodb_buffer_pool_chunk_size=0 (autosize) in MariaDB 10.8+ (#869) +- chore: bump version to 2.8.24 -- fix: noisy sysctl errors for sunrpc parameters when kernel module is not loaded -- fix: refactor get_kernel_info to handle missing sysctl parameters gracefully - -2.8.4 2026-01-17 - -- fix: database injection failing to find dump files due to incorrect working directory -- fix: ensure correct path handling for 'source' commands in employees.sql - -2.8.3 2026-01-17 - -- feat: detect docker/podman environment and automatically grab logs from container if local log file is not found -- feat: add --container option to manually specify a container for log retrieval - -2.8.2 2026-01-17 - -- fix: system command failures (ping/ifconfig/redirection) on modern Linux (Ubuntu 22.04/WSL2) -- feat: integrate external test dependencies (multi-db-docker-env, test_db) and automated employees database injection - -2.8.1 2026-01-17 - -- fix: resilient memory checks with /proc fallback on Linux and silencing expected ps failures +2.8.23 2026-01-18 -2.8.0 2026-01-17 +- feat: add --ignore-tables CLI option to filter specific tables from analysis (#749) +- chore: bump version to 2.8.23 -- Bump version to 2.8.0 -- enhance user hostname restriction checks -- feat: Translate comments and messages in updateCVElist.py to English -- chore: ignore VS Code workspace files -- build: update Debian File::Util dependency installation -- cleanup: MariaDB and MySQL support documentation (focus on LTS) 2.8.22 2026-01-18 - feat: update all repository links from 'major' to 'jmrenouard' (issue #410)