diff --git a/playbooks/delete.yml b/playbooks/delete.yml
new file mode 100644
index 0000000..b4e8215
--- /dev/null
+++ b/playbooks/delete.yml
@@ -0,0 +1,24 @@
+- name: List all disks
+  hosts: all
+  tasks:
+    - name: Get info disk information 2
+      shell: blkid | grep LABEL | awk {'print $2'}
+      register: blkid
+    
+    - name: Print valid labels
+      debug:
+        var: blkid
+
+    - name: Parsear stdout_lines para buscar etiquetas específicas
+      set_fact:
+        found_labels: >-
+          {{
+            blkid.stdout_lines
+            | map('regex_search', 'LABEL="(?P<label>[^"]+)"')
+            | select('defined')
+            | list
+          }}
+
+    - name: Print valid labels
+      debug:
+        var: found_labels
diff --git a/playbooks/new-machine-id.yml b/playbooks/new-machine-id.yml
new file mode 100644
index 0000000..f61bfdf
--- /dev/null
+++ b/playbooks/new-machine-id.yml
@@ -0,0 +1,22 @@
+- name: Change machine-id in Debian
+  hosts: all
+  gather_facts: no
+  become: yes
+  tasks:
+    - name: Remove files with old machine-id
+      file:
+        path: "{{ item }}"
+        state: absent
+      loop:
+        - /etc/machine-id
+        - /var/lib/dbus/machine-id
+
+    - name: Ensure a new UUID is generated for /etc/machine-id
+      command:
+        cmd: dbus-uuidgen --ensure=/etc/machine-id
+
+    - name: Create symbolic link for /var/lib/dbus/machine-id
+      file:
+        src: /etc/machine-id
+        dest: /var/lib/dbus/machine-id
+        state: link
diff --git a/playbooks/services.yml b/playbooks/services.yml
new file mode 100644
index 0000000..cf219b7
--- /dev/null
+++ b/playbooks/services.yml
@@ -0,0 +1,7 @@
+- name: Configure Directory, Time, and Database Services
+  hosts: all
+  tasks:
+  - name: Configure services to install in the server
+    import_role:
+      name: services
+
diff --git a/roles/services/defaults/main.yaml b/roles/services/defaults/main.yaml
new file mode 100644
index 0000000..f102d75
--- /dev/null
+++ b/roles/services/defaults/main.yaml
@@ -0,0 +1,45 @@
+samba_client_services:  
+  - smbd 
+  - nmbd 
+  - winbind
+dcsamba_base_packages:
+  - samba
+  - krb5-user
+  - winbind
+  - acl
+mariadb_base_packages:
+  - mariadb-server
+  - mariadb-backup
+  - pmm2-client
+mariadb_requeriments:
+  - curl
+  - apt-transport-https
+certificates:
+  - { content: '{{ ca_mysql }}', dest: '/etc/mysql/ca.pem', mode: 'u=rw,g=r,o=r' }
+  - { content: '{{ cert_mysql }}', dest: '/etc/mysql/cert.pem', mode: 'u=rw,g=r,o=r' }
+required_directories:
+  - { path: /mnt/local-backup, owner: root, group: root, mode: 'u=rwx,g=rx,o=rx' }
+  - { path: /mnt/mysqlbin, owner: root, group: root, mode: 'u=rwx,g=rx,o=rx' }
+  - { path: /mnt/mysqltmp, owner: root, group: root, mode: 'u=rwx,g=rwx,o=rwxt' }
+  - { path: /mnt/mysqlbin/binlog, owner: mysql, group: mysql, mode: 'u=rwx,g=,o=' }
+  - { path: /root/scripts, owner: root, group: root, mode: 'u=rwx,g=rx,o=rx' }
+  - { path: /root/mariabackup, owner: root, group: root, mode: 'u=rwx,g=rx,o=rx' }
+required_files_and_mariabackup_files_and_root_scripts:
+  - { src: "mariadb_override.conf", dest: "/etc/systemd/system/mariadb.service.d/override.conf", mode: "u=rw,g=r,o=r" }
+  - { src: "mysql-flush.sh", dest: "/etc/qemu/fsfreeze-hook.d/mysql-flush.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/mariabackup/bacula-before.sh", dest: "/root/mariabackup/bacula-before.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/mariabackup/config.sh", dest: "/root/mariabackup/config.sh", mode: "u=rwx,g=rx,o=x" }
+  - { src: "files/mariabackup/inc-backup.sh", dest: "/root/mariabackup/inc-backup.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/mariabackup/my.cnf", dest: "/root/mariabackup/my.cnf", mode: "u=rw,g=,o=" }
+  - { src: "files/mariabackup/restore-backup.sh", dest: "/root/mariabackup/restore-backup.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/check-memory.sh", dest: "/root/scripts/check-memory.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/export-privs.sh", dest: "/root/scripts/export-privs.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/mysqltuner.pl", dest: "/root/scripts/mysqltuner.pl", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/promote-master.sh", dest: "/root/scripts/promote-master.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/promote-slave.sh", dest: "/root/scripts/promote-slave.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/README.md", dest: "/root/scripts/README.md", mode: "u=rw,g=r,o=r" }
+  - { src: "files/scripts/scheduler-log.sh", dest: "/root/scripts/scheduler-log.sh", mode: "u=rwx,g=rx,o=rx" }
+  - { src: "files/scripts/sync-conf.sh", dest: "/root/scripts/sync-conf.sh", mode: "u=rwx,g=rx,o=rx" }
+downloads:
+  - { url: "https://r.mariadb.com/downloads/mariadb_repo_setup", dest: "/tmp/mariadb_repo_setup", mode: "u=rwx,g=rx,o=rx" }
+  - { url: "https://repo.percona.com/apt/percona-release_latest.generic_all.deb", dest: "/tmp/percona-release_latest.generic_all.deb", mode: "u=rw,g=r,o=r" }
diff --git a/roles/services/files/mariabackup/bacula-before.sh b/roles/services/files/mariabackup/bacula-before.sh
new file mode 100644
index 0000000..f1628fd
--- /dev/null
+++ b/roles/services/files/mariabackup/bacula-before.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+# https://mariadb.com/kb/en/mariabackup/
+set -e
+
+myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. "$myDir/config.sh"
+
+todayDir="$(date +%Y-%m-%d)"
+backupName="${todayDir}_$(date +"%H-%M")_full"
+backupFile="$backupDir/$backupName.gz"
+
+if [ -d "$backupDir" ]; then
+	rm -rf "$backupDir/"*
+fi
+
+ulimit -n 8192
+mariabackup \
+	--defaults-extra-file="$myDir/my.cnf" \
+	--backup \
+	--extra-lsndir="$backupDir/$backupName" \
+	--history="$todayDir" \
+	2>> "$logFile" \
+	| gzip \
+	> "$backupFile"
+
+if [ $? != "0" ]; then
+	echo "An error ocurred during backup, please take a look at log file: $logFile"
+	exit 1
+fi
+
diff --git a/roles/services/files/mariabackup/config.sh b/roles/services/files/mariabackup/config.sh
new file mode 100644
index 0000000..bb61fd8
--- /dev/null
+++ b/roles/services/files/mariabackup/config.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Destination file for backup logs
+logFile=/var/log/vn-mariabackup.log
+
+# Temporary local directory to save backups
+backupDir=/mnt/local-backup
+
+# Directory for backup history
+historyDir=/mnt/backup4mariadb
+
+# Number of days for backup rotation
+cleanDays=90
+
+# Directory for temporal restore data
+restoreDir=/mnt/mysqldata/mysql-restore
+
+# Directory of MySQL data
+dataDir=/mnt/mysqldata/mysql
+
diff --git a/roles/services/files/mariabackup/inc-backup.sh b/roles/services/files/mariabackup/inc-backup.sh
new file mode 100644
index 0000000..c6d6e91
--- /dev/null
+++ b/roles/services/files/mariabackup/inc-backup.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+# https://mariadb.com/kb/en/incremental-backup-and-restore-with-mariabackup/
+set -e
+
+myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. "$myDir/config.sh"
+
+todayDir="$(date +%Y-%m-%d)"
+todayPath="$historyDir/$todayDir"
+
+pattern="$todayPath/${todayDir}_??-??_full.xb.gz.enc"
+files=($pattern)
+backupFile="${files[0]}"
+backupBase=$(basename -- "$backupFile")
+backupName="${backupBase%%.*}"
+
+incrementalName="${todayDir}_$(date +"%H-%M")_incremental"
+incrementalFile="$backupDir/${incrementalName}.xb.gz.enc"
+
+ulimit -n 24098
+mariabackup \
+	--defaults-extra-file="$myDir/my.cnf" \
+	--backup \
+	--incremental-basedir="$backupDir/$backupName" \
+	--extra-lsndir="$backupDir/$incrementalName" \
+	--incremental-history-name="$todayDir" \
+	2>> "$logFile" \
+	| gzip \
+	| openssl enc -aes-256-cbc -pbkdf2 -kfile "$myDir/xbcrypt.key" \
+	> "$incrementalFile"
+
+if [ $? != "0" ]; then
+	echo "An error ocurred during backup, please take a look at log file: $logFile"
+	exit 1
+fi
+
+cp "$incrementalFile" "$todayPath"
+cp -r "$backupDir/$incrementalName" "$todayPath"
diff --git a/roles/services/files/mariabackup/my.cnf b/roles/services/files/mariabackup/my.cnf
new file mode 100644
index 0000000..14edb89
--- /dev/null
+++ b/roles/services/files/mariabackup/my.cnf
@@ -0,0 +1,7 @@
+[mariabackup]
+host = localhost
+user = {{ user_mariabackup }}
+password = {{ password_user_mariabackup }}
+use-memory = 1G
+parallel = 2
+stream = mbstream
diff --git a/roles/services/files/mariabackup/restore-backup.sh b/roles/services/files/mariabackup/restore-backup.sh
new file mode 100644
index 0000000..0673526
--- /dev/null
+++ b/roles/services/files/mariabackup/restore-backup.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# https://mariadb.com/kb/en/using-encryption-and-compression-tools-with-mariabackup/
+set -e
+
+myDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. "$myDir/config.sh"
+
+backupFile=$1
+
+if [ -z "$backupFile" ]; then
+	echo "Backup file not defined."
+	exit 1
+fi
+
+if [ ! -f "$backupFile" ]; then
+	echo "Backup file does not exist: $backupFile"
+	exit 2
+fi
+
+echo "Restoring MySQL data from backup."
+
+rm -rf "$restoreDir"
+mkdir -p "$restoreDir"
+
+echo "Decompresing backup."
+gzip --decompress --stdout "$backupFile" \
+	| mbstream -x --directory="$restoreDir"
+
+echo "Preparing backup."
+mariabackup \
+	--defaults-extra-file="$myDir/my.cnf" \
+	--prepare \
+	--target-dir="$restoreDir"
+
+echo "Stopping service."
+service mariadb stop
+if pgrep mariadbd; then pkill -9 mariadbd; fi
+
+echo "Restoring data."
+rm -rf "$dataDir"
+mariabackup \
+	--defaults-extra-file="$myDir/my.cnf" \
+	--move-back \
+	--target-dir="$restoreDir" \
+	2>> "$logFile"
+chown -R mysql:mysql "$dataDir"
+
+rm "$dataDir/mysql/slow_log."*
+rm "$dataDir/mysql/general_log."*
+
+echo "Removing restore data."
+rm -r "$restoreDir"
+
diff --git a/roles/services/files/mariadb_override.conf b/roles/services/files/mariadb_override.conf
new file mode 100644
index 0000000..678e16a
--- /dev/null
+++ b/roles/services/files/mariadb_override.conf
@@ -0,0 +1,3 @@
+[Service]
+LimitNOFILE=600000
+LimitMEMLOCK=2M
diff --git a/roles/services/files/mysql-flush.sh b/roles/services/files/mysql-flush.sh
new file mode 100644
index 0000000..88af1b8
--- /dev/null
+++ b/roles/services/files/mysql-flush.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+# https://github.com/qemu/qemu/blob/master/scripts/qemu-guest-agent/fsfreeze-hook.d/mysql-flush.sh.sample
+# Flush MySQL tables to the disk before the filesystem is frozen.
+# At the same time, this keeps a read lock in order to avoid write accesses
+# from the other clients until the filesystem is thawed.
+
+MYSQL="/usr/bin/mysql"
+MYSQL_OPTS="-uroot" #"-prootpassword"
+FIFO=/var/run/mysql-flush.fifo
+
+# Check mysql is installed and the server running
+[ -x "$MYSQL" ] && "$MYSQL" $MYSQL_OPTS < /dev/null || exit 0
+
+flush_and_wait() {
+    printf "FLUSH TABLES WITH READ LOCK \\G\n"
+    trap 'printf "$(date): $0 is killed\n">&2' HUP INT QUIT ALRM TERM
+    read < $FIFO
+    printf "UNLOCK TABLES \\G\n"
+    rm -f $FIFO
+}
+
+case "$1" in
+    freeze)
+        mkfifo $FIFO || exit 1
+        flush_and_wait | "$MYSQL" $MYSQL_OPTS &
+        # wait until every block is flushed
+        while [ "$(echo 'SHOW STATUS LIKE "Key_blocks_not_flushed"' |\
+                 "$MYSQL" $MYSQL_OPTS | tail -1 | cut -f 2)" -gt 0 ]; do
+            sleep 1
+        done
+        # for InnoDB, wait until every log is flushed
+        INNODB_STATUS=$(mktemp /tmp/mysql-flush.XXXXXX)
+        [ $? -ne 0 ] && exit 2
+        trap "rm -f $INNODB_STATUS; exit 1" HUP INT QUIT ALRM TERM
+        while :; do
+            printf "SHOW ENGINE INNODB STATUS \\G" |\
+                "$MYSQL" $MYSQL_OPTS > $INNODB_STATUS
+            LOG_CURRENT=$(grep 'Log sequence number' $INNODB_STATUS |\
+                          tr -s ' ' | cut -d' ' -f4)
+            LOG_FLUSHED=$(grep 'Log flushed up to' $INNODB_STATUS |\
+                          tr -s ' ' | cut -d' ' -f5)
+            [ "$LOG_CURRENT" = "$LOG_FLUSHED" ] && break
+            sleep 1
+        done
+        rm -f $INNODB_STATUS
+        ;;
+
+    thaw)
+        [ ! -p $FIFO ] && exit 1
+        echo > $FIFO
+        ;;
+
+    *)
+        exit 1
+        ;;
+esac
\ No newline at end of file
diff --git a/roles/services/files/scripts/README.md b/roles/services/files/scripts/README.md
new file mode 100644
index 0000000..1c5d401
--- /dev/null
+++ b/roles/services/files/scripts/README.md
@@ -0,0 +1,19 @@
+# Scripts to maintain MariaDB
+
+## scheduler-log.sh
+
+The following table should be created into MySQL/MariaDB database.
+
+```
+CREATE TABLE `eventLog` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `date` datetime NOT NULL,
+  `event` varchar(512) NOT NULL,
+  `error` varchar(1024) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `date` (`date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Event scheduler error log'
+```
+
+Then adjust the *$logTable* variable to the correct schema.
+
diff --git a/roles/services/files/scripts/check-memory.sh b/roles/services/files/scripts/check-memory.sh
new file mode 100755
index 0000000..d04ed75
--- /dev/null
+++ b/roles/services/files/scripts/check-memory.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+minFree=1
+memFree=$(free --gibi | awk '$1 == "Mem:" { print $7 }')
+
+if [ "$memFree" -le "$minFree" ]; then
+	echo "Free memory is ${memFree}Gi, restarting mariadb service to prevent OOM killer..."
+	systemctl restart mariadb
+fi
diff --git a/roles/services/files/scripts/export-privs.sh b/roles/services/files/scripts/export-privs.sh
new file mode 100755
index 0000000..e1f318d
--- /dev/null
+++ b/roles/services/files/scripts/export-privs.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+OUTFILE=privs.sql
+SCHEMA=mysql
+TABLES=(
+	global_priv
+	db
+	tables_priv
+	columns_priv
+	procs_priv
+	proxies_priv
+	roles_mapping
+)
+
+echo "USE \`$SCHEMA\`;" > "$OUTFILE"
+
+for TABLE in "${TABLES[@]}"
+do
+	echo "TRUNCATE TABLE \`$SCHEMA\`.\`$TABLE\`;" >> "$OUTFILE"
+done
+
+echo "" >> "$OUTFILE"
+mysqldump --no-create-info --skip-triggers "$SCHEMA" ${TABLES[@]} >> "$OUTFILE"
+
+echo "FLUSH PRIVILEGES;" >> "$OUTFILE"
diff --git a/roles/services/files/scripts/mysqltuner.pl b/roles/services/files/scripts/mysqltuner.pl
new file mode 100755
index 0000000..ebbfcbb
--- /dev/null
+++ b/roles/services/files/scripts/mysqltuner.pl
@@ -0,0 +1,7743 @@
+#!/usr/bin/env perl
+# mysqltuner.pl - Version 2.6.1
+# High Performance MySQL Tuning Script
+# Copyright (C) 2015-2023 Jean-Marie Renouard - jmrenouard@gmail.com
+# Copyright (C) 2006-2023 Major Hayden - major@mhtx.net
+
+# For the latest updates, please visit http://mysqltuner.pl/
+# Git repository available at https://github.com/major/MySQLTuner-perl
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+# This project would not be possible without help from:
+#   Matthew Montgomery     Paul Kehrer          Dave Burgess
+#   Jonathan Hinds         Mike Jackson         Nils Breunese
+#   Shawn Ashlee           Luuk Vosslamber      Ville Skytta
+#   Trent Hornibrook       Jason Gill           Mark Imbriaco
+#   Greg Eden              Aubin Galinotti      Giovanni Bechis
+#   Bill Bradford          Ryan Novosielski     Michael Scheidell
+#   Blair Christensen      Hans du Plooy        Victor Trac
+#   Everett Barnes         Tom Krouper          Gary Barrueto
+#   Simon Greenaway        Adam Stein           Isart Montane
+#   Baptiste M.            Cole Turner          Major Hayden
+#   Joe Ashcraft           Jean-Marie Renouard  Christian Loos
+#   Julien Francoz         Daniel Black         Long Radix
+#
+# Inspired by Matthew Montgomery's tuning-primer.sh script:
+# http://www.day32.com/MySQL/
+#
+package main;
+
+use 5.005;
+use strict;
+use warnings;
+
+use diagnostics;
+use File::Spec;
+use Getopt::Long;
+use Pod::Usage;
+use File::Basename;
+use Cwd 'abs_path';
+
+#use Data::Dumper;
+#$Data::Dumper::Pair = " : ";
+
+# for which()
+#use Env;
+
+# Set up a few variables for use in the script
+my $tunerversion = "2.6.1";
+my ( @adjvars, @generalrec );
+
+# Set defaults
+my %opt = (
+    "silent"              => 0,
+    "nobad"               => 0,
+    "nogood"              => 0,
+    "noinfo"              => 0,
+    "debug"               => 0,
+    "nocolor"             => ( !-t STDOUT ),
+    "color"               => ( -t STDOUT ),
+    "forcemem"            => 0,
+    "forceswap"           => 0,
+    "host"                => 0,
+    "socket"              => 0,
+    "port"                => 0,
+    "user"                => 0,
+    "pass"                => 0,
+    "password"            => 0,
+    "ssl-ca"              => 0,
+    "skipsize"            => 0,
+    "checkversion"        => 0,
+    "updateversion"       => 0,
+    "buffers"             => 0,
+    "passwordfile"        => 0,
+    "bannedports"         => '',
+    "maxportallowed"      => 0,
+    "outputfile"          => 0,
+    "noprocess"           => 0,
+    "dbstat"              => 0,
+    "nodbstat"            => 0,
+    "server-log"          => '',
+    "tbstat"              => 0,
+    "notbstat"            => 0,
+    "colstat"             => 0,
+    "nocolstat"           => 0,
+    "idxstat"             => 0,
+    "noidxstat"           => 0,
+    "nomyisamstat"        => 0,
+    "nostructstat"        => 0,
+    "sysstat"             => 0,
+    "nosysstat"           => 0,
+    "pfstat"              => 0,
+    "nopfstat"            => 0,
+    "skippassword"        => 0,
+    "noask"               => 0,
+    "template"            => 0,
+    "json"                => 0,
+    "prettyjson"          => 0,
+    "reportfile"          => 0,
+    "verbose"             => 0,
+    "experimental"        => 0,
+    "nondedicated"        => 0,
+    "defaults-file"       => '',
+    "defaults-extra-file" => '',
+    "protocol"            => '',
+    "dumpdir"             => '',
+    "feature"             => '',
+    "dbgpattern"          => '',
+    "defaultarch"         => 64,
+    "noprettyicon"        => 0
+);
+
+# Gather the options from the command line
+GetOptions(
+    \%opt,                   'nobad',
+    'nogood',                'noinfo',
+    'debug',                 'nocolor',
+    'forcemem=i',            'forceswap=i',
+    'host=s',                'socket=s',
+    'port=i',                'user=s',
+    'pass=s',                'skipsize',
+    'checkversion',          'mysqladmin=s',
+    'mysqlcmd=s',            'help',
+    'buffers',               'skippassword',
+    'passwordfile=s',        'outputfile=s',
+    'silent',                'noask',
+    'json',                  'prettyjson',
+    'template=s',            'reportfile=s',
+    'cvefile=s',             'bannedports=s',
+    'updateversion',         'maxportallowed=s',
+    'verbose',               'password=s',
+    'passenv=s',             'userenv=s',
+    'defaults-file=s',       'ssl-ca=s',
+    'color',                 'noprocess',
+    'dbstat',                'nodbstat',
+    'tbstat',                'notbstat',
+    'colstat',               'nocolstat',
+    'sysstat',               'nosysstat',
+    'pfstat',                'nopfstat',
+    'idxstat',               'noidxstat',
+    'structstat',            'nostructstat',
+    'myisamstat',            'nomyisamstat',
+    'server-log=s',          'protocol=s',
+    'defaults-extra-file=s', 'dumpdir=s',
+    'feature=s',             'dbgpattern=s',
+    'defaultarch=i',         'experimental',
+    'nondedicated',          'noprettyicon'
+  )
+  or pod2usage(
+    -exitval  => 1,
+    -verbose  => 99,
+    -sections => [
+        "NAME",
+        "IMPORTANT USAGE GUIDELINES",
+        "CONNECTION AND AUTHENTICATION",
+        "PERFORMANCE AND REPORTING OPTIONS",
+        "OUTPUT OPTIONS"
+    ]
+  );
+
+if ( defined $opt{'help'} && $opt{'help'} == 1 ) {
+    pod2usage(
+        -exitval  => 0,
+        -verbose  => 99,
+        -sections => [
+            "NAME",
+            "IMPORTANT USAGE GUIDELINES",
+            "CONNECTION AND AUTHENTICATION",
+            "PERFORMANCE AND REPORTING OPTIONS",
+            "OUTPUT OPTIONS"
+        ]
+    );
+}
+
+my $devnull = File::Spec->devnull();
+my $basic_password_files =
+  ( $opt{passwordfile} eq "0" )
+  ? abs_path( dirname(__FILE__) ) . "/basic_passwords.txt"
+  : abs_path( $opt{passwordfile} );
+
+# Username from envvar
+if ( exists $opt{userenv} && exists $ENV{ $opt{userenv} } ) {
+    $opt{user} = $ENV{ $opt{userenv} };
+}
+
+# Related to password option
+if ( exists $opt{passenv} && exists $ENV{ $opt{passenv} } ) {
+    $opt{pass} = $ENV{ $opt{passenv} };
+}
+$opt{pass} = $opt{password} if ( $opt{pass} eq 0 and $opt{password} ne 0 );
+
+if ( $opt{dumpdir} ne '' ) {
+    $opt{dumpdir} = abs_path( $opt{dumpdir} );
+    if ( !-d $opt{dumpdir} ) {
+        mkdir $opt{dumpdir} or die "Cannot create directory $opt{dumpdir}: $!";
+    }
+}
+
+# for RPM distributions
+$basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt"
+  unless -f "$basic_password_files";
+
+$opt{dbgpattern} = '.*' if ( $opt{dbgpattern} eq '' );
+
+# Activate debug variables
+#if ( $opt{debug} ne '' ) { $opt{debug} = 2; }
+# Activate experimental calculations and analysis
+#if ( $opt{experimental} ne '' ) { $opt{experimental} = 1; }
+
+# check if we need to enable verbose mode
+if ( $opt{feature} ne '' ) { $opt{verbose} = 1; }
+if ( $opt{verbose} ) {
+    $opt{checkversion} = 0;    # Check for updates to MySQLTuner
+    $opt{dbstat}       = 1;    # Print database information
+    $opt{tbstat}       = 1;    # Print database information
+    $opt{idxstat}      = 1;    # Print index information
+    $opt{sysstat}      = 1;    # Print index information
+    $opt{buffers}      = 1;    # Print global and per-thread buffer values
+    $opt{pfstat}       = 1;    # Print performance schema info.
+    $opt{structstat}   = 1;    # Print table structure information
+    $opt{myisamstat}   = 1;    # Print MyISAM table information
+
+    $opt{cvefile} = 'vulnerabilities.csv';    #CVE File for vulnerability checks
+}
+$opt{noprettyicon}=0 if $opt{noprettyicon}!=1;
+$opt{nocolor} = 1 if defined( $opt{outputfile} );
+$opt{tbstat}  = 0 if ( $opt{notbstat} == 1 );    # Don't print table information
+$opt{colstat} = 0 if ( $opt{nocolstat} == 1 );  # Don't print column information
+$opt{dbstat}  = 0 if ( $opt{nodbstat} == 1 ); # Don't print database information
+$opt{noprocess} = 0
+  if ( $opt{noprocess} == 1 );                # Don't print process information
+$opt{sysstat} = 0 if ( $opt{nosysstat} == 1 ); # Don't print sysstat information
+$opt{pfstat}  = 0
+  if ( $opt{nopfstat} == 1 );    # Don't print performance schema information
+$opt{idxstat} = 0 if ( $opt{noidxstat} == 1 );   # Don't print index information
+$opt{structstat} = 0
+  if ( not defined( $opt{structstat} ) or $opt{nostructstat} == 1 )
+  ;    # Don't print table struct information
+$opt{myisamstat} = 1
+  if ( not defined( $opt{myisamstat} ) );
+$opt{myisamstat} = 0
+  if ( $opt{nomyisamstat} == 1 );    # Don't print MyISAM table information
+
+# for RPM distributions
+$opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv"
+  unless ( defined $opt{cvefile} and -f "$opt{cvefile}" );
+$opt{cvefile} = '' unless -f "$opt{cvefile}";
+$opt{cvefile} = './vulnerabilities.csv' if -f './vulnerabilities.csv';
+
+$opt{'bannedports'} = '' unless defined( $opt{'bannedports'} );
+my @banned_ports = split ',', $opt{'bannedports'};
+
+#
+my $outputfile = undef;
+$outputfile = abs_path( $opt{outputfile} ) unless $opt{outputfile} eq "0";
+
+my $fh = undef;
+open( $fh, '>', $outputfile )
+  or die("Fail opening $outputfile")
+  if defined($outputfile);
+$opt{nocolor} = 1 if defined($outputfile);
+$opt{nocolor} = 1 unless ( -t STDOUT );
+
+$opt{nocolor} = 0 if ( $opt{color} == 1 );
+
+# Setting up the colors for the print styles
+my $me = `whoami`;
+$me =~ s/\n//g;
+
+
+my $good = ( $opt{nocolor} == 0 ) ? "[\e[0;32mOK\e[0m]"  : "[OK]";
+my $bad  = ( $opt{nocolor} == 0 ) ? "[\e[0;31m!!\e[0m]"  : "[!!]";
+my $info = ( $opt{nocolor} == 0 ) ? "[\e[0;34m--\e[0m]"  : "[--]";
+my $deb  = ( $opt{nocolor} == 0 ) ? "[\e[0;31mDG\e[0m]"  : "[DG]";
+my $cmd  = ( $opt{nocolor} == 0 ) ? "\e[1;32m[CMD]($me)" : "[CMD]($me)";
+my $end  = ( $opt{nocolor} == 0 ) ? "\e[0m"              : "";
+
+if ($opt{noprettyicon} == 0) {
+  $good = "✔ ";
+  $bad  = "✘ ";
+  $info = "ℹ ";
+  $deb  = "⚙ ";
+  $cmd  = "⌨️($me)";
+  $end  = "  ";
+}
+
+# Maximum lines of log output to read from end
+my $maxlines = 30000;
+
+# Checks for supported or EOL'ed MySQL versions
+my ( $mysqlvermajor, $mysqlverminor, $mysqlvermicro );
+
+# Database
+my @dblist;
+
+# Super structure containing all information
+my %result;
+$result{'MySQLTuner'}{'version'}  = $tunerversion;
+$result{'MySQLTuner'}{'datetime'} = `date '+%d-%m-%Y %H:%M:%S'`;
+$result{'MySQLTuner'}{'options'}  = \%opt;
+
+# Functions that handle the print styles
+sub prettyprint {
+    print $_[0] . "\n" unless ( $opt{'silent'} or $opt{'json'} );
+    print $fh $_[0] . "\n" if defined($fh);
+}
+
+sub goodprint {
+    prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 );
+}
+
+sub infoprint {
+    prettyprint $info. " " . $_[0] unless ( $opt{noinfo} == 1 );
+}
+
+sub badprint {
+    prettyprint $bad. " " . $_[0] unless ( $opt{nobad} == 1 );
+}
+
+sub debugprint {
+    prettyprint $deb. " " . $_[0] unless ( $opt{debug} == 0 );
+}
+
+sub redwrap {
+    return ( $opt{nocolor} == 0 ) ? "\e[0;31m" . $_[0] . "\e[0m" : $_[0];
+}
+
+sub greenwrap {
+    return ( $opt{nocolor} == 0 ) ? "\e[0;32m" . $_[0] . "\e[0m" : $_[0];
+}
+
+sub cmdprint {
+    prettyprint $cmd. " " . $_[0] . $end;
+}
+
+sub infoprintml {
+    for my $ln (@_) { $ln =~ s/\n//g; infoprint "\t$ln"; }
+}
+
+sub infoprintcmd {
+    cmdprint "@_";
+    infoprintml grep { $_ ne '' and $_ !~ /^\s*$/ } `@_ 2>&1`;
+}
+
+sub subheaderprint {
+    my $tln = 100;
+    my $sln = 8;
+    my $ln  = length("@_") + 2;
+
+    prettyprint " ";
+    prettyprint "-" x $sln . " @_ " . "-" x ( $tln - $ln - $sln );
+}
+
+sub infoprinthcmd {
+    subheaderprint "$_[0]";
+    infoprintcmd "$_[1]";
+}
+
+sub is_remote() {
+    my $host = $opt{'host'};
+    return 0 if ( $host eq '' );
+    return 0 if ( $host eq 'localhost' );
+    return 0 if ( $host eq '127.0.0.1' );
+    return 1;
+}
+
+sub is_int {
+    return 0 unless defined $_[0];
+    my $str = $_[0];
+
+    #trim whitespace both sides
+    $str =~ s/^\s+|\s+$//g;
+
+    #Alternatively, to match any float-like numeric, use:
+    # m/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
+
+    #flatten to string and match dash or plus and one or more digits
+    if ( $str =~ /^(\-|\+)?\d+?$/ ) {
+        return 1;
+    }
+    return 0;
+}
+
+# Calculates the number of physical cores considering HyperThreading
+sub cpu_cores {
+    if ( $^O eq 'linux' ) {
+        my $cntCPU =
+`awk -F: '/^core id/ && !P[\$2] { CORES++; P[\$2]=1 }; /^physical id/ && !N[\$2] { CPUs++; N[\$2]=1 };  END { print CPUs*CORES }' /proc/cpuinfo`;
+        chomp $cntCPU;
+        return ( $cntCPU == 0 ? `nproc` : $cntCPU );
+    }
+
+    if ( $^O eq 'freebsd' ) {
+        my $cntCPU = `sysctl -n kern.smp.cores`;
+        chomp $cntCPU;
+        return $cntCPU + 0;
+    }
+    return 0;
+}
+
+# Calculates the parameter passed in bytes, then rounds it to one decimal place
+sub hr_bytes {
+    my $num = shift;
+    return "0B" unless defined($num);
+    return "0B" if $num eq "NULL";
+    return "0B" if $num eq "";
+
+    if ( $num >= ( 1024**3 ) ) {    # GB
+        return sprintf( "%.1f", ( $num / ( 1024**3 ) ) ) . "G";
+    }
+    elsif ( $num >= ( 1024**2 ) ) {    # MB
+        return sprintf( "%.1f", ( $num / ( 1024**2 ) ) ) . "M";
+    }
+    elsif ( $num >= 1024 ) {           # KB
+        return sprintf( "%.1f", ( $num / 1024 ) ) . "K";
+    }
+    else {
+        return $num . "B";
+    }
+}
+
+sub hr_raw {
+    my $num = shift;
+    return "0" unless defined($num);
+    return "0" if $num eq "NULL";
+    if ( $num =~ /^(\d+)G$/ ) {
+        return $1 * 1024 * 1024 * 1024;
+    }
+    if ( $num =~ /^(\d+)M$/ ) {
+        return $1 * 1024 * 1024;
+    }
+    if ( $num =~ /^(\d+)K$/ ) {
+        return $1 * 1024;
+    }
+    if ( $num =~ /^(\d+)$/ ) {
+        return $1;
+    }
+    return $num;
+}
+
+# Calculates the parameter passed in bytes, then rounds it to the nearest integer
+sub hr_bytes_rnd {
+    my $num = shift;
+    return "0B" unless defined($num);
+    return "0B" if $num eq "NULL";
+
+    if ( $num >= ( 1024**3 ) ) {    # GB
+        return int( ( $num / ( 1024**3 ) ) ) . "G";
+    }
+    elsif ( $num >= ( 1024**2 ) ) {    # MB
+        return int( ( $num / ( 1024**2 ) ) ) . "M";
+    }
+    elsif ( $num >= 1024 ) {           # KB
+        return int( ( $num / 1024 ) ) . "K";
+    }
+    else {
+        return $num . "B";
+    }
+}
+
+# Calculates the parameter passed to the nearest power of 1000, then rounds it to the nearest integer
+sub hr_num {
+    my $num = shift;
+    if ( $num >= ( 1000**3 ) ) {       # Billions
+        return int( ( $num / ( 1000**3 ) ) ) . "B";
+    }
+    elsif ( $num >= ( 1000**2 ) ) {    # Millions
+        return int( ( $num / ( 1000**2 ) ) ) . "M";
+    }
+    elsif ( $num >= 1000 ) {           # Thousands
+        return int( ( $num / 1000 ) ) . "K";
+    }
+    else {
+        return $num;
+    }
+}
+
+# Calculate Percentage
+sub percentage {
+    my $value = shift;
+    my $total = shift;
+    $total = 0 unless defined $total;
+    $total = 0 if $total eq "NULL";
+    return 100, 00 if $total == 0;
+    return sprintf( "%.2f", ( $value * 100 / $total ) );
+}
+
+# Calculates uptime to display in a human-readable form
+sub pretty_uptime {
+    my $uptime  = shift;
+    my $seconds = $uptime % 60;
+    my $minutes = int( ( $uptime % 3600 ) / 60 );
+    my $hours   = int( ( $uptime % 86400 ) / (3600) );
+    my $days    = int( $uptime / (86400) );
+    my $uptimestring;
+    if ( $days > 0 ) {
+        $uptimestring = "${days}d ${hours}h ${minutes}m ${seconds}s";
+    }
+    elsif ( $hours > 0 ) {
+        $uptimestring = "${hours}h ${minutes}m ${seconds}s";
+    }
+    elsif ( $minutes > 0 ) {
+        $uptimestring = "${minutes}m ${seconds}s";
+    }
+    else {
+        $uptimestring = "${seconds}s";
+    }
+    return $uptimestring;
+}
+
+# Retrieves the memory installed on this machine
+my ( $physical_memory, $swap_memory, $duflags, $xargsflags );
+
+sub memerror {
+    badprint
+"Unable to determine total memory/swap; use '--forcemem' and '--forceswap'";
+    exit 1;
+}
+
+sub os_setup {
+    my $os = `uname`;
+    $duflags    = ( $os =~ /Linux/ )        ? '-b' : '';
+    $xargsflags = ( $os =~ /Darwin|SunOS/ ) ? ''   : '-r';
+    if ( $opt{'forcemem'} > 0 ) {
+        $physical_memory = $opt{'forcemem'} * 1048576;
+        infoprint "Assuming $opt{'forcemem'} MB of physical memory";
+        if ( $opt{'forceswap'} > 0 ) {
+            $swap_memory = $opt{'forceswap'} * 1048576;
+            infoprint "Assuming $opt{'forceswap'} MB of swap space";
+        }
+        else {
+            $swap_memory = 0;
+            badprint "Assuming 0 MB of swap space (use --forceswap to specify)";
+        }
+    }
+    else {
+        if ( $os =~ /Linux|CYGWIN/ ) {
+            $physical_memory =
+              `grep -i memtotal: /proc/meminfo | awk '{print \$2}'`
+              or memerror;
+            $physical_memory *= 1024;
+
+            $swap_memory =
+              `grep -i swaptotal: /proc/meminfo | awk '{print \$2}'`
+              or memerror;
+            $swap_memory *= 1024;
+        }
+        elsif ( $os =~ /Darwin/ ) {
+            $physical_memory = `sysctl -n hw.memsize` or memerror;
+            $swap_memory =
+              `sysctl -n vm.swapusage | awk '{print \$3}' | sed 's/\..*\$//'`
+              or memerror;
+        }
+        elsif ( $os =~ /NetBSD|OpenBSD|FreeBSD/ ) {
+            $physical_memory = `sysctl -n hw.physmem` or memerror;
+            if ( $physical_memory < 0 ) {
+                $physical_memory = `sysctl -n hw.physmem64` or memerror;
+            }
+            $swap_memory =
+              `swapctl -l | grep '^/' | awk '{ s+= \$2 } END { print s }'`
+              or memerror;
+        }
+        elsif ( $os =~ /BSD/ ) {
+            $physical_memory = `sysctl -n hw.realmem` or memerror;
+            $swap_memory =
+              `swapinfo | grep '^/' | awk '{ s+= \$2 } END { print s }'`;
+        }
+        elsif ( $os =~ /SunOS/ ) {
+            $physical_memory =
+              `/usr/sbin/prtconf | grep Memory | cut -f 3 -d ' '`
+              or memerror;
+            chomp($physical_memory);
+            $physical_memory = $physical_memory * 1024 * 1024;
+        }
+        elsif ( $os =~ /AIX/ ) {
+            $physical_memory =
+              `lsattr -El sys0 | grep realmem | awk '{print \$2}'`
+              or memerror;
+            chomp($physical_memory);
+            $physical_memory = $physical_memory * 1024;
+            $swap_memory     = `lsps -as | awk -F"(MB| +)" '/MB /{print \$2}'`
+              or memerror;
+            chomp($swap_memory);
+            $swap_memory = $swap_memory * 1024 * 1024;
+        }
+        elsif ( $os =~ /windows/i ) {
+            $physical_memory =
+`wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"`
+              or memerror;
+            $swap_memory =
+`wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"`
+              or memerror;
+        }
+    }
+    debugprint "Physical Memory: $physical_memory";
+    debugprint "Swap Memory: $swap_memory";
+    chomp($physical_memory);
+    chomp($swap_memory);
+    chomp($os);
+    $physical_memory = $opt{forcemem}
+      if ( defined( $opt{forcemem} ) and $opt{forcemem} gt 0 );
+    $result{'OS'}{'OS Type'}                   = $os;
+    $result{'OS'}{'Physical Memory'}{'bytes'}  = $physical_memory;
+    $result{'OS'}{'Physical Memory'}{'pretty'} = hr_bytes($physical_memory);
+    $result{'OS'}{'Swap Memory'}{'bytes'}      = $swap_memory;
+    $result{'OS'}{'Swap Memory'}{'pretty'}     = hr_bytes($swap_memory);
+    $result{'OS'}{'Other Processes'}{'bytes'}  = get_other_process_memory();
+    $result{'OS'}{'Other Processes'}{'pretty'} =
+      hr_bytes( get_other_process_memory() );
+}
+
+sub get_http_cli {
+    my $httpcli = which( "curl", $ENV{'PATH'} );
+    chomp($httpcli);
+    if ($httpcli) {
+        return $httpcli;
+    }
+
+    $httpcli = which( "wget", $ENV{'PATH'} );
+    chomp($httpcli);
+    if ($httpcli) {
+        return $httpcli;
+    }
+    return "";
+}
+
+# Checks for updates to MySQLTuner
+sub validate_tuner_version {
+    if ( $opt{'checkversion'} eq 0 ) {
+        print "\n" unless ( $opt{'silent'} or $opt{'json'} );
+        infoprint "Skipped version check for MySQLTuner script";
+        return;
+    }
+
+    my $update;
+    my $url =
+"https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl";
+    my $httpcli = get_http_cli();
+    if ( $httpcli =~ /curl$/ ) {
+        debugprint "$httpcli is available.";
+
+        debugprint
+"$httpcli -m 3 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2";
+        $update =
+`$httpcli -m 3 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`;
+        chomp($update);
+        debugprint "VERSION: $update";
+
+        compare_tuner_version($update);
+        return;
+    }
+
+    if ( $httpcli =~ /wget$/ ) {
+        debugprint "$httpcli is available.";
+
+        debugprint
+"$httpcli -e timestamping=off -t 1 -T 3 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2";
+        $update =
+`$httpcli -e timestamping=off -t 1 -T 3 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`;
+        chomp($update);
+        compare_tuner_version($update);
+        return;
+    }
+    debugprint "curl and wget are not available.";
+    infoprint "Unable to check for the latest MySQLTuner version";
+    infoprint
+"Using --pass and --password option is insecure during MySQLTuner execution (password disclosure)"
+      if ( defined( $opt{'pass'} ) );
+}
+
+# Checks for updates to MySQLTuner
+sub update_tuner_version {
+    if ( $opt{'updateversion'} eq 0 ) {
+        badprint "Skipped version update for MySQLTuner script";
+        print "\n" unless ( $opt{'silent'} or $opt{'json'} );
+        return;
+    }
+
+    my $update;
+    my $fullpath = "";
+    my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/";
+    my @scripts =
+      ( "mysqltuner.pl", "basic_passwords.txt", "vulnerabilities.csv" );
+    my $totalScripts    = scalar(@scripts);
+    my $receivedScripts = 0;
+    my $httpcli         = get_http_cli();
+
+    foreach my $script (@scripts) {
+
+        if ( $httpcli =~ /curl$/ ) {
+            debugprint "$httpcli is available.";
+
+            $fullpath = dirname(__FILE__) . "/" . $script;
+            debugprint "FullPath: $fullpath";
+            debugprint
+"$httpcli --connect-timeout 3 '$url$script' 2>$devnull > $fullpath";
+            $update =
+`$httpcli --connect-timeout 3 '$url$script' 2>$devnull > $fullpath`;
+            chomp($update);
+            debugprint "$script updated: $update";
+
+            if ( -s $script eq 0 ) {
+                badprint "Couldn't update $script";
+            }
+            else {
+                ++$receivedScripts;
+                debugprint "$script updated: $update";
+            }
+        }
+        elsif ( $httpcli =~ /wget$/ ) {
+
+            debugprint "$httpcli is available.";
+
+            debugprint
+"$httpcli -qe timestamping=off -t 1 -T 3 -O $script '$url$script'";
+            $update =
+`$httpcli -qe timestamping=off -t 1 -T 3 -O $script '$url$script'`;
+            chomp($update);
+
+            if ( -s $script eq 0 ) {
+                badprint "Couldn't update $script";
+            }
+            else {
+                ++$receivedScripts;
+                debugprint "$script updated: $update";
+            }
+        }
+        else {
+            debugprint "curl and wget are not available.";
+            infoprint "Unable to check for the latest MySQLTuner version";
+        }
+
+    }
+
+    if ( $receivedScripts eq $totalScripts ) {
+        goodprint "Successfully updated MySQLTuner script";
+    }
+    else {
+        badprint "Couldn't update MySQLTuner script";
+    }
+    infoprint "Stopping program: MySQLTuner script must be updated first.";
+    exit 0;
+}
+
+sub compare_tuner_version {
+    my $remoteversion = shift;
+    debugprint "Remote data: $remoteversion";
+
+    #exit 0;
+    if ( $remoteversion ne $tunerversion ) {
+        badprint
+          "There is a new version of MySQLTuner available ($remoteversion)";
+        update_tuner_version();
+        return;
+    }
+    goodprint "You have the latest version of MySQLTuner ($tunerversion)";
+    return;
+}
+
+# Checks to see if a MySQL login is possible
+my ( $mysqllogin, $doremote, $remotestring, $mysqlcmd, $mysqladmincmd );
+
+my $osname = $^O;
+if ( $osname eq 'MSWin32' ) {
+    eval { require Win32; } or last;
+    $osname = Win32::GetOSName();
+    infoprint "* Windows OS ($osname) is not fully supported.\n";
+
+    #exit 1;
+}
+
+sub mysql_setup {
+    $doremote     = 0;
+    $remotestring = '';
+    if ( $opt{mysqladmin} ) {
+        $mysqladmincmd = $opt{mysqladmin};
+    }
+    else {
+        $mysqladmincmd = which( "mariadb-admin", $ENV{'PATH'} );
+        if ( !-e $mysqladmincmd ) {
+            $mysqladmincmd = which( "mysqladmin", $ENV{'PATH'} );
+        }
+    }
+    chomp($mysqladmincmd);
+    if ( !-e $mysqladmincmd && $opt{mysqladmin} ) {
+        badprint "Unable to find the mysqladmin command you specified: "
+          . $mysqladmincmd . "";
+        exit 1;
+    }
+    elsif ( !-e $mysqladmincmd ) {
+        badprint
+"Couldn't find mysqladmin/mariadb-admin in your \$PATH. Is MySQL installed?";
+
+        #exit 1;
+    }
+    if ( $opt{mysqlcmd} ) {
+        $mysqlcmd = $opt{mysqlcmd};
+    }
+    else {
+        $mysqlcmd = which( "mariadb", $ENV{'PATH'} );
+        if ( !-e $mysqlcmd ) {
+            $mysqlcmd = which( "mysql", $ENV{'PATH'} );
+        }
+    }
+    chomp($mysqlcmd);
+    if ( !-e $mysqlcmd && $opt{mysqlcmd} ) {
+        badprint "Unable to find the mysql command you specified: "
+          . $mysqlcmd . "";
+        exit 1;
+    }
+    elsif ( !-e $mysqlcmd ) {
+        badprint
+          "Couldn't find mysql/mariadb in your \$PATH. Is MySQL installed?";
+        exit 1;
+    }
+    $mysqlcmd =~ s/\n$//g;
+    my $mysqlclidefaults = `$mysqlcmd --print-defaults`;
+    debugprint "MySQL Client: $mysqlclidefaults";
+    if ( $mysqlclidefaults =~ /auto-vertical-output/ ) {
+        badprint
+          "Avoid auto-vertical-output in configuration file(s) for MySQL like";
+        exit 1;
+    }
+
+    debugprint "MySQL Client: $mysqlcmd";
+
+    # Are we being asked to connect via a socket?
+    if ( $opt{socket} ne 0 ) {
+        if ( $opt{port} ne 0 ) {
+            $remotestring = " -S $opt{socket} -P $opt{port}";
+        }
+        else {
+            $remotestring = " -S $opt{socket}";
+        }
+    }
+
+    if ( $opt{protocol} ne '' ) {
+        $remotestring = " --protocol=$opt{protocol}";
+    }
+
+    # Are we being asked to connect to a remote server?
+    if ( $opt{host} ne 0 ) {
+        chomp( $opt{host} );
+        $opt{port} = ( $opt{port} eq 0 ) ? 3306 : $opt{port};
+
+# If we're doing a remote connection, but forcemem wasn't specified, we need to exit
+        if ( $opt{'forcemem'} eq 0 && is_remote eq 1 ) {
+            badprint "The --forcemem option is required for remote connections";
+            badprint
+              "Assuming RAM memory is 1Gb for simplify remote connection usage";
+            $opt{'forcemem'} = 1024;
+
+            #exit 1;
+        }
+        if ( $opt{'forceswap'} eq 0 && is_remote eq 1 ) {
+            badprint
+              "The --forceswap option is required for remote connections";
+            badprint
+              "Assuming Swap size is 1Gb for simplify remote connection usage";
+            $opt{'forceswap'} = 1024;
+
+            #exit 1;
+        }
+        infoprint "Performing tests on $opt{host}:$opt{port}";
+        $remotestring = " -h $opt{host} -P $opt{port}";
+        $doremote     = is_remote();
+
+    }
+    else {
+        $opt{host} = '127.0.0.1';
+    }
+
+    if ( $opt{'ssl-ca'} ne 0 ) {
+        if ( -e -r -f $opt{'ssl-ca'} ) {
+            $remotestring .= " --ssl-ca=$opt{'ssl-ca'}";
+            infoprint
+              "Will connect using ssl public key passed on the command line";
+            return 1;
+        }
+        else {
+            badprint
+"Attempted to use passed ssl public key, but it was not found or could not be read";
+            exit 1;
+        }
+    }
+
+   # Did we already get a username with or without password on the command line?
+    if ( $opt{user} ne 0 ) {
+        $mysqllogin =
+            "-u $opt{user} "
+          . ( ( $opt{pass} ne 0 ) ? "-p'$opt{pass}' " : " " )
+          . $remotestring;
+        my $loginstatus =
+          `$mysqlcmd -Nrs -e 'select "mysqld is alive";' $mysqllogin 2>&1`;
+        if ( $loginstatus =~ /mysqld is alive/ ) {
+            goodprint "Logged in using credentials passed on the command line";
+            return 1;
+        }
+        else {
+            badprint
+              "Attempted to use login credentials, but they were invalid";
+            exit 1;
+        }
+    }
+
+    my $svcprop = which( "svcprop", $ENV{'PATH'} );
+    if ( substr( $svcprop, 0, 1 ) =~ "/" ) {
+
+        # We are on solaris
+        ( my $mysql_login =
+`svcprop -p quickbackup/username svc:/network/mysql-quickbackup:default`
+        ) =~ s/\s+$//;
+        ( my $mysql_pass =
+`svcprop -p quickbackup/password svc:/network/mysql-quickbackup:default`
+        ) =~ s/\s+$//;
+        if ( substr( $mysql_login, 0, 7 ) ne "svcprop" ) {
+
+            # mysql-quickbackup is installed
+            $mysqllogin = "-u $mysql_login -p$mysql_pass";
+            my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`;
+            if ( $loginstatus =~ /mysqld is alive/ ) {
+                goodprint "Logged in using credentials from mysql-quickbackup.";
+                return 1;
+            }
+            else {
+                badprint
+"Attempted to use login credentials from mysql-quickbackup, but they failed.";
+                exit 1;
+            }
+        }
+    }
+    elsif ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) {
+
+        # 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`;
+        unless ( $loginstatus =~ /mysqld is alive/ ) {
+
+            # Plesk 10+
+            $mysqllogin =
+              "-u admin -p`/usr/local/psa/bin/admin --show-password`";
+            $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`;
+            unless ( $loginstatus =~ /mysqld is alive/ ) {
+                badprint
+"Attempted to use login credentials from Plesk and Plesk 10+, but they failed.";
+                exit 1;
+            }
+        }
+    }
+    elsif ( -r "/usr/local/directadmin/conf/mysql.conf" and $doremote == 0 ) {
+
+        # It's a DirectAdmin box, use the available credentials
+        my $mysqluser =
+          `cat /usr/local/directadmin/conf/mysql.conf | egrep '^user=.*'`;
+        my $mysqlpass =
+          `cat /usr/local/directadmin/conf/mysql.conf | egrep '^passwd=.*'`;
+
+        $mysqluser =~ s/user=//;
+        $mysqluser =~ s/[\r\n]//;
+        $mysqlpass =~ s/passwd=//;
+        $mysqlpass =~ s/[\r\n]//;
+
+        $mysqllogin = "-u $mysqluser -p$mysqlpass";
+
+        my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`;
+        unless ( $loginstatus =~ /mysqld is alive/ ) {
+            badprint
+"Attempted to use login credentials from DirectAdmin, but they failed.";
+            exit 1;
+        }
+    }
+    elsif ( -r "/etc/mysql/debian.cnf"
+        and $doremote == 0
+        and $opt{'defaults-file'} eq '' )
+    {
+
+        # We have a Debian maintenance account, use it
+        $mysqllogin = "--defaults-file=/etc/mysql/debian.cnf";
+        my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`;
+        if ( $loginstatus =~ /mysqld is alive/ ) {
+            goodprint
+              "Logged in using credentials from Debian maintenance account.";
+            return 1;
+        }
+        else {
+            badprint
+"Attempted to use login credentials from Debian maintenance account, but they failed.";
+            exit 1;
+        }
+    }
+    elsif ( $opt{'defaults-file'} ne '' and -r "$opt{'defaults-file'}" ) {
+
+        # defaults-file
+        debugprint "defaults file detected: $opt{'defaults-file'}";
+        my $mysqlclidefaults = `$mysqlcmd --print-defaults`;
+        debugprint "MySQL Client Default File: $opt{'defaults-file'}";
+
+        $mysqllogin = "--defaults-file=" . $opt{'defaults-file'};
+        my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`;
+        if ( $loginstatus =~ /mysqld is alive/ ) {
+            goodprint "Logged in using credentials from defaults file account.";
+            return 1;
+        }
+    }
+    elsif ( $opt{'defaults-extra-file'} ne ''
+        and -r "$opt{'defaults-extra-file'}" )
+    {
+
+        # defaults-extra-file
+        debugprint "defaults extra file detected: $opt{'defaults-extra-file'}";
+        my $mysqlclidefaults = `$mysqlcmd --print-defaults`;
+        debugprint
+          "MySQL Client Extra Default File: $opt{'defaults-extra-file'}";
+
+        $mysqllogin = "--defaults-extra-file=" . $opt{'defaults-extra-file'};
+        my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`;
+        if ( $loginstatus =~ /mysqld is alive/ ) {
+            goodprint
+              "Logged in using credentials from extra defaults file account.";
+            return 1;
+        }
+    }
+    else {
+        # It's not Plesk or Debian, we should try a login
+        debugprint "$mysqladmincmd $remotestring ping 2>&1";
+
+        #my $loginstatus = "";
+        debugprint "Using mysqlcmd: $mysqlcmd";
+
+        #if (defined($mysqladmincmd)) {
+        #  infoprint "Using mysqladmin to check login";
+        #  $loginstatus=`$mysqladmincmd $remotestring ping 2>&1`;
+        #} else {
+        infoprint "Using mysql to check login";
+        my $loginstatus =
+`$mysqlcmd $remotestring -Nrs -e 'select "mysqld is alive"' --connect-timeout=3 2>&1`;
+
+        #}
+
+        if ( $loginstatus =~ /mysqld is alive/ ) {
+
+            # Login went just fine
+            $mysqllogin = " $remotestring ";
+
+       # Did this go well because of a .my.cnf file or is there no password set?
+            my $userpath = `printenv HOME`;
+            if ( length($userpath) > 0 ) {
+                chomp($userpath);
+            }
+            unless ( -e "${userpath}/.my.cnf" or -e "${userpath}/.mylogin.cnf" )
+            {
+                badprint
+                  "SECURITY RISK: Successfully authenticated without password";
+            }
+            return 1;
+        }
+        else {
+            if ( $opt{'noask'} == 1 ) {
+                badprint
+                  "Attempted to use login credentials, but they were invalid";
+                exit 1;
+            }
+            my ( $name, $password );
+
+            # If --user is defined no need to ask for username
+            if ( $opt{user} ne 0 ) {
+                $name = $opt{user};
+            }
+            else {
+                print STDERR "Please enter your MySQL administrative login: ";
+                $name = <STDIN>;
+            }
+
+            # If --pass is defined no need to ask for password
+            if ( $opt{pass} ne 0 ) {
+                $password = $opt{pass};
+            }
+            else {
+                print STDERR
+                  "Please enter your MySQL administrative password: ";
+                system("stty -echo >$devnull 2>&1");
+                $password = <STDIN>;
+                system("stty echo >$devnull 2>&1");
+            }
+            chomp($password);
+            chomp($name);
+            $mysqllogin = "-u $name";
+
+            if ( length($password) > 0 ) {
+                $mysqllogin .= " -p'$password'";
+            }
+            $mysqllogin .= $remotestring;
+            my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`;
+            if ( $loginstatus =~ /mysqld is alive/ ) {
+
+                #print STDERR "";
+                if ( !length($password) ) {
+
+       # Did this go well because of a .my.cnf file or is there no password set?
+                    my $userpath = `printenv HOME`;
+                    chomp($userpath);
+                    unless ( -e "$userpath/.my.cnf" ) {
+                        print STDERR "";
+                        badprint
+"SECURITY RISK: Successfully authenticated without password";
+                    }
+                }
+                return 1;
+            }
+            else {
+                #print STDERR "";
+                badprint
+                  "Attempted to use login credentials, but they were invalid.";
+                exit 1;
+            }
+            exit 1;
+        }
+    }
+}
+
+# MySQL Request Array
+sub select_array {
+    my $req = shift;
+    debugprint "PERFORM: $req ";
+    my @result = `$mysqlcmd $mysqllogin -Bse "\\w$req" 2>>/dev/null`;
+    if ( $? != 0 ) {
+        badprint "Failed to execute: $req";
+        badprint "FAIL Execute SQL / return code: $?";
+        debugprint "CMD    : $mysqlcmd";
+        debugprint "OPTIONS: $mysqllogin";
+        debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`;
+
+        #exit $?;
+    }
+    debugprint "select_array: return code : $?";
+    chomp(@result);
+    return @result;
+}
+
+# MySQL Request Array
+sub select_array_with_headers {
+    my $req = shift;
+    debugprint "PERFORM: $req ";
+    my @result = `$mysqlcmd $mysqllogin -Bre "\\w$req" 2>>/dev/null`;
+    if ( $? != 0 ) {
+        badprint "Failed to execute: $req";
+        badprint "FAIL Execute SQL / return code: $?";
+        debugprint "CMD    : $mysqlcmd";
+        debugprint "OPTIONS: $mysqllogin";
+        debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`;
+
+        #exit $?;
+    }
+    debugprint "select_array_with_headers: return code : $?";
+    chomp(@result);
+    return @result;
+}
+
+# MySQL Request Array
+sub select_csv_file {
+    my $tfile = shift;
+    my $req   = shift;
+    debugprint "PERFORM: $req CSV into $tfile";
+
+    #return;
+    my @result = select_array_with_headers($req);
+    open( my $fh, '>', $tfile ) or die "Could not open file '$tfile' $!";
+    for my $l (@result) {
+        $l =~ s/\t/","/g;
+        $l =~ s/^/"/;
+        $l =~ s/$/"\n/;
+        print $fh $l;
+        print $l if $opt{debug};
+    }
+    close $fh;
+    infoprint "CSV file $tfile created";
+}
+
+sub human_size {
+    my ( $size, $n ) = ( shift, 0 );
+    ++$n and $size /= 1024 until $size < 1024;
+    return sprintf "%.2f %s", $size, (qw[ bytes KB MB GB TB ])[$n];
+}
+
+# MySQL Request one
+sub select_one {
+    my $req = shift;
+    debugprint "PERFORM: $req ";
+    my $result = `$mysqlcmd $mysqllogin -Bse "\\w$req" 2>>/dev/null`;
+    if ( $? != 0 ) {
+        badprint "Failed to execute: $req";
+        badprint "FAIL Execute SQL / return code: $?";
+        debugprint "CMD    : $mysqlcmd";
+        debugprint "OPTIONS: $mysqllogin";
+        debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`;
+
+        #exit $?;
+    }
+    debugprint "select_array: return code : $?";
+    chomp($result);
+    return $result;
+}
+
+# MySQL Request one
+sub select_one_g {
+    my $pattern = shift;
+
+    my $req = shift;
+    debugprint "PERFORM: $req ";
+    my @result = `$mysqlcmd $mysqllogin -re "\\w$req\\G" 2>>/dev/null`;
+    if ( $? != 0 ) {
+        badprint "Failed to execute: $req";
+        badprint "FAIL Execute SQL / return code: $?";
+        debugprint "CMD    : $mysqlcmd";
+        debugprint "OPTIONS: $mysqllogin";
+        debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`;
+
+        #exit $?;
+    }
+    debugprint "select_array: return code : $?";
+    chomp(@result);
+    return ( grep { /$pattern/ } @result )[0];
+}
+
+sub select_str_g {
+    my $pattern = shift;
+
+    my $req = shift;
+    my $str = select_one_g $pattern, $req;
+    return () unless defined $str;
+    my @val = split /:/, $str;
+    shift @val;
+    return trim(@val);
+}
+
+sub select_user_dbs {
+    return select_array(
+"SELECT DISTINCT TABLE_SCHEMA FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'percona', 'sys')"
+    );
+}
+
+sub select_tables_db {
+    my $schema = shift;
+    return select_array(
+"SELECT DISTINCT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$schema'"
+    );
+}
+
+sub select_indexes_db {
+    my $schema = shift;
+    return select_array(
+"SELECT DISTINCT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='$schema'"
+    );
+}
+
+sub select_views_db {
+    my $schema = shift;
+    return select_array(
+"SELECT DISTINCT TABLE_NAME FROM information_schema.VIEWS WHERE TABLE_SCHEMA='$schema'"
+    );
+}
+
+sub select_triggers_db {
+    my $schema = shift;
+    return select_array(
+"SELECT DISTINCT TRIGGER_NAME FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA='$schema'"
+    );
+}
+
+sub select_routines_db {
+    my $schema = shift;
+    return select_array(
+"SELECT DISTINCT ROUTINE_NAME FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA='$schema'"
+    );
+}
+
+sub select_table_indexes_db {
+    my $schema = shift;
+    my $tbname = shift;
+    return select_array(
+"SELECT INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='$schema' AND TABLE_NAME='$tbname'"
+    );
+}
+
+sub select_table_columns_db {
+    my $schema = shift;
+    my $table  = shift;
+    return select_array(
+"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$schema' AND TABLE_NAME='$table'"
+    );
+}
+
+sub get_tuning_info {
+    my @infoconn = select_array "\\s";
+    my ( $tkey, $tval );
+    @infoconn =
+      grep { !/Threads:/ and !/Connection id:/ and !/pager:/ and !/Using/ }
+      @infoconn;
+    foreach my $line (@infoconn) {
+        if ( $line =~ /\s*(.*):\s*(.*)/ ) {
+            debugprint "$1 => $2";
+            $tkey = $1;
+            $tval = $2;
+            chomp($tkey);
+            chomp($tval);
+            $result{'MySQL Client'}{$tkey} = $tval;
+        }
+    }
+    $result{'MySQL Client'}{'Client Path'}         = $mysqlcmd;
+    $result{'MySQL Client'}{'Admin Path'}          = $mysqladmincmd;
+    $result{'MySQL Client'}{'Authentication Info'} = $mysqllogin;
+
+}
+
+# Populates all of the variable and status hashes
+my ( %mystat, %myvar, $dummyselect, %myrepl, %myslaves );
+
+sub arr2hash {
+    my $href = shift;
+    my $harr = shift;
+    my $sep  = shift;
+    my $key  = '';
+    my $val  = '';
+
+    $sep = '\s' unless defined($sep);
+    foreach my $line (@$harr) {
+        next if ( $line =~ m/^\*\*\*\*\*\*\*/ );
+        $line =~ /([a-zA-Z_]*)\s*$sep\s*(.*)/;
+        $key         = $1;
+        $val         = $2;
+        $$href{$key} = $val;
+
+        debugprint " * $key = $val" if $key =~ /$opt{dbgpattern}/i;
+    }
+}
+
+sub get_all_vars {
+
+    # We need to initiate at least one query so that our data is useable
+    $dummyselect = select_one "SELECT VERSION()";
+    if ( not defined($dummyselect) or $dummyselect eq "" ) {
+        badprint
+          "You probably do not have enough privileges to run MySQLTuner ...";
+        exit(256);
+    }
+    $dummyselect =~ s/(.*?)\-.*/$1/;
+    debugprint "VERSION: " . $dummyselect . "";
+    $result{'MySQL Client'}{'Version'} = $dummyselect;
+
+    my @mysqlvarlist = select_array("SHOW VARIABLES");
+    push( @mysqlvarlist, select_array("SHOW GLOBAL VARIABLES") );
+    arr2hash( \%myvar, \@mysqlvarlist );
+    $result{'Variables'} = \%myvar;
+
+    my @mysqlstatlist = select_array("SHOW STATUS");
+    push( @mysqlstatlist, select_array("SHOW GLOBAL STATUS") );
+    arr2hash( \%mystat, \@mysqlstatlist );
+    $result{'Status'} = \%mystat;
+    unless ( defined( $myvar{'innodb_support_xa'} ) ) {
+        $myvar{'innodb_support_xa'} = 'ON';
+    }
+    $mystat{'Uptime'} = 1
+      unless defined( $mystat{'Uptime'} )
+      and $mystat{'Uptime'} > 0;
+    $myvar{'have_galera'} = "NO";
+    if (   defined( $myvar{'wsrep_provider_options'} )
+        && $myvar{'wsrep_provider_options'} ne ""
+        && $myvar{'wsrep_on'} ne "OFF" )
+    {
+        $myvar{'have_galera'} = "YES";
+        debugprint "Galera options: " . $myvar{'wsrep_provider_options'};
+    }
+
+    # Workaround for MySQL bug #59393 wrt. ignore-builtin-innodb
+    if ( ( $myvar{'ignore_builtin_innodb'} || "" ) eq "ON" ) {
+        $myvar{'have_innodb'} = "NO";
+    }
+
+    # Support GTID MODE FOR MARIADB
+    # Issue MariaDB GTID mode #513
+    $myvar{'gtid_mode'} = 'ON'
+      if ( defined( $myvar{'gtid_current_pos'} )
+        and $myvar{'gtid_current_pos'} ne '' );
+
+    # Whether the server uses a thread pool to handle client connections
+    # MariaDB: thread_handling = pool-of-threads
+    # MySQL: thread_handling = loaded-dynamically
+    $myvar{'have_threadpool'} = "NO";
+    if (
+        defined( $myvar{'thread_handling'} )
+        and (  $myvar{'thread_handling'} eq 'pool-of-threads'
+            || $myvar{'thread_handling'} eq 'loaded-dynamically' )
+      )
+    {
+        $myvar{'have_threadpool'} = "YES";
+    }
+
+    # have_* for engines is deprecated and will be removed in MySQL 5.6;
+    # check SHOW ENGINES and set corresponding old style variables.
+    # Also works around MySQL bug #59393 wrt. skip-innodb
+    my @mysqlenginelist = select_array "SHOW ENGINES";
+    foreach my $line (@mysqlenginelist) {
+        if ( $line =~ /^([a-zA-Z_]+)\s+(\S+)/ ) {
+            my $engine = lc($1);
+
+            if ( $engine eq "federated" || $engine eq "blackhole" ) {
+                $engine .= "_engine";
+            }
+            elsif ( $engine eq "berkeleydb" ) {
+                $engine = "bdb";
+            }
+            my $val = ( $2 eq "DEFAULT" ) ? "YES" : $2;
+            $myvar{"have_$engine"} = $val;
+            $result{'Storage Engines'}{$engine} = $2;
+        }
+    }
+
+    #debugprint Dumper(@mysqlenginelist);
+
+    my @mysqlslave;
+    if ( mysql_version_eq(8) or mysql_version_ge( 10, 5 ) ) {
+        @mysqlslave = select_array("SHOW REPLICA STATUS\\G");
+    }
+    else {
+        @mysqlslave = select_array("SHOW SLAVE STATUS\\G");
+    }
+    arr2hash( \%myrepl, \@mysqlslave, ':' );
+    $result{'Replication'}{'Status'} = \%myrepl;
+
+    my @mysqlslaves;
+    if ( mysql_version_eq(8) or mysql_version_ge( 10, 5 ) ) {
+        @mysqlslaves = select_array "SHOW SLAVE STATUS";
+    }
+    else {
+        @mysqlslaves = select_array("SHOW SLAVE HOSTS\\G");
+    }
+
+    my @lineitems = ();
+    foreach my $line (@mysqlslaves) {
+        debugprint "L: $line ";
+        @lineitems                                        = split /\s+/, $line;
+        $myslaves{ $lineitems[0] }                        = $line;
+        $result{'Replication'}{'Slaves'}{ $lineitems[0] } = $lineitems[4];
+    }
+}
+
+sub remove_cr {
+    return map {
+        my $line = $_;
+        $line =~ s/\n$//g;
+        $line =~ s/^\s+$//g;
+        $line;
+    } @_;
+}
+
+sub remove_empty {
+    grep { $_ ne '' } @_;
+}
+
+sub grep_file_contents {
+    my $file = shift;
+    my $patt;
+}
+
+sub get_file_contents {
+    my $file = shift;
+    open( my $fh, "<", $file ) or die "Can't open $file for read: $!";
+    my @lines = <$fh>;
+    close $fh or die "Cannot close $file: $!";
+    @lines = remove_cr @lines;
+    return @lines;
+}
+
+sub get_basic_passwords {
+    return get_file_contents(shift);
+}
+
+sub get_log_file_real_path {
+    my $file     = shift;
+    my $hostname = shift;
+    my $datadir  = shift;
+    if ( -f "$file" ) {
+        return $file;
+    }
+    elsif ( -f "$hostname.log" ) {
+        return "$hostname.log";
+    }
+    elsif ( -f "$hostname.err" ) {
+        return "$hostname.err";
+    }
+    elsif ( -f "$datadir$hostname.err" ) {
+        return "$datadir$hostname.err";
+    }
+    elsif ( -f "$datadir$hostname.log" ) {
+        return "$datadir$hostname.log";
+    }
+    elsif ( -f "$datadir" . "mysql_error.log" ) {
+        return "$datadir" . "mysql_error.log";
+    }
+    elsif ( -f "/var/log/mysql.log" ) {
+        return "/var/log/mysql.log";
+    }
+    elsif ( -f "/var/log/mysqld.log" ) {
+        return "/var/log/mysqld.log";
+    }
+    elsif ( -f "/var/log/mysql/$hostname.err" ) {
+        return "/var/log/mysql/$hostname.err";
+    }
+    elsif ( -f "/var/log/mysql/$hostname.log" ) {
+        return "/var/log/mysql/$hostname.log";
+    }
+    elsif ( -f "/var/log/mysql/" . "mysql_error.log" ) {
+        return "/var/log/mysql/" . "mysql_error.log";
+    }
+    else {
+        return $file;
+    }
+}
+
+sub log_file_recommendations {
+    if ( is_remote eq 1 ) {
+        infoprint "Skipping error log files checks on remote host";
+        return;
+    }
+    my $fh;
+    $myvar{'log_error'} = $opt{'server-log'}
+      || get_log_file_real_path( $myvar{'log_error'}, $myvar{'hostname'},
+        $myvar{'datadir'} );
+
+    subheaderprint "Log file Recommendations";
+    if ( "$myvar{'log_error'}" eq "stderr" ) {
+        badprint
+"log_error is set to $myvar{'log_error'}, but this script can't read stderr";
+        return;
+    }
+    elsif ( $myvar{'log_error'} =~ /^(docker|podman|kubectl):(.*)/ ) {
+        open( $fh, '-|', "$1 logs --tail=$maxlines '$2'" )
+          // die "Can't start $1 $!";
+        goodprint "Log from cloud` $myvar{'log_error'} exists";
+    }
+    elsif ( $myvar{'log_error'} =~ /^systemd:(.*)/ ) {
+        open( $fh, '-|', "journalctl -n $maxlines -b  -u '$1'" )
+          // die "Can't start journalctl $!";
+        goodprint "Log journal` $myvar{'log_error'} exists";
+    }
+    elsif ( -f "$myvar{'log_error'}" ) {
+        goodprint "Log file $myvar{'log_error'} exists";
+        my $size = ( stat $myvar{'log_error'} )[7];
+        infoprint "Log file: "
+          . $myvar{'log_error'} . " ("
+          . hr_bytes_rnd($size) . ")";
+
+        if ( $size > 0 ) {
+            goodprint "Log file $myvar{'log_error'} is not empty";
+            if ( $size < 32 * 1024 * 1024 ) {
+                goodprint "Log file $myvar{'log_error'} is smaller than 32 MB";
+            }
+            else {
+                badprint "Log file $myvar{'log_error'} is bigger than 32 MB";
+                push @generalrec,
+                  $myvar{'log_error'}
+                  . " is > 32MB, you should analyze why or implement a rotation log strategy such as logrotate!";
+            }
+        }
+        else {
+            infoprint
+"Log file $myvar{'log_error'} is empty. Assuming log-rotation. Use --server-log={file} for explicit file";
+            return;
+        }
+        if ( !open( $fh, '<', $myvar{'log_error'} ) ) {
+            badprint "Log file $myvar{'log_error'} isn't readable.";
+            return;
+        }
+        goodprint "Log file $myvar{'log_error'} is readable.";
+
+        if ( $maxlines * 80 < $size ) {
+            seek( $fh, -$maxlines * 80, 2 );
+            <$fh>;    # discard line fragment
+        }
+    }
+    else {
+        badprint "Log file $myvar{'log_error'} doesn't exist";
+        return;
+    }
+
+    my $numLi     = 0;
+    my $nbWarnLog = 0;
+    my $nbErrLog  = 0;
+    my @lastShutdowns;
+    my @lastStarts;
+
+    while ( my $logLi = <$fh> ) {
+        chomp $logLi;
+        $numLi++;
+        debugprint "$numLi: $logLi" if $logLi =~ /\[(warning|error)\]/i;
+        $nbErrLog++  if $logLi =~ /\[error\]/i;
+        $nbWarnLog++ if $logLi =~ /\[warning\]/i;
+        push @lastShutdowns, $logLi
+          if $logLi =~ /Shutdown complete/ and $logLi !~ /Innodb/i;
+        push @lastStarts, $logLi if $logLi =~ /ready for connections/;
+    }
+    close $fh;
+
+    if ( $nbWarnLog > 0 ) {
+        badprint "$myvar{'log_error'} contains $nbWarnLog warning(s).";
+        push @generalrec, "Check warning line(s) in $myvar{'log_error'} file";
+    }
+    else {
+        goodprint "$myvar{'log_error'} doesn't contain any warning.";
+    }
+    if ( $nbErrLog > 0 ) {
+        badprint "$myvar{'log_error'} contains $nbErrLog error(s).";
+        push @generalrec, "Check error line(s) in $myvar{'log_error'} file";
+    }
+    else {
+        goodprint "$myvar{'log_error'} doesn't contain any error.";
+    }
+
+    infoprint scalar @lastStarts . " start(s) detected in $myvar{'log_error'}";
+    my $nStart = 0;
+    my $nEnd   = 10;
+    if ( scalar @lastStarts < $nEnd ) {
+        $nEnd = scalar @lastStarts;
+    }
+    for my $startd ( reverse @lastStarts[ -$nEnd .. -1 ] ) {
+        $nStart++;
+        infoprint "$nStart) $startd";
+    }
+    infoprint scalar @lastShutdowns
+      . " shutdown(s) detected in $myvar{'log_error'}";
+    $nStart = 0;
+    $nEnd   = 10;
+    if ( scalar @lastShutdowns < $nEnd ) {
+        $nEnd = scalar @lastShutdowns;
+    }
+    for my $shutd ( reverse @lastShutdowns[ -$nEnd .. -1 ] ) {
+        $nStart++;
+        infoprint "$nStart) $shutd";
+    }
+
+    #exit 0;
+}
+
+sub cve_recommendations {
+    subheaderprint "CVE Security Recommendations";
+    unless ( defined( $opt{cvefile} ) && -f "$opt{cvefile}" ) {
+        infoprint "Skipped due to --cvefile option undefined";
+        return;
+    }
+
+#$mysqlvermajor=10;
+#$mysqlverminor=1;
+#$mysqlvermicro=17;
+#prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}";
+    my $cvefound = 0;
+    open( my $fh, "<", $opt{cvefile} )
+      or die "Can't open $opt{cvefile} for read: $!";
+    while ( my $cveline = <$fh> ) {
+        my @cve = split( ';', $cveline );
+        debugprint
+"Comparing $mysqlvermajor\.$mysqlverminor\.$mysqlvermicro with $cve[1]\.$cve[2]\.$cve[3] : "
+          . ( mysql_version_le( $cve[1], $cve[2], $cve[3] ) ? '<=' : '>' );
+
+        # Avoid not major/minor version corresponding CVEs
+        next
+          unless ( int( $cve[1] ) == $mysqlvermajor
+            && int( $cve[2] ) == $mysqlverminor );
+        if ( int( $cve[3] ) >= $mysqlvermicro ) {
+            badprint "$cve[4](<= $cve[1]\.$cve[2]\.$cve[3]) : $cve[6]";
+            $result{'CVE'}{'List'}{$cvefound} =
+              "$cve[4](<= $cve[1]\.$cve[2]\.$cve[3]) : $cve[6]";
+            $cvefound++;
+        }
+    }
+    close $fh or die "Cannot close $opt{cvefile}: $!";
+    $result{'CVE'}{'nb'} = $cvefound;
+
+    my $cve_warning_notes = "";
+    if ( $cvefound == 0 ) {
+        goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION";
+        return;
+    }
+    if ( $mysqlvermajor eq 5 and $mysqlverminor eq 5 ) {
+        infoprint
+          "False positive CVE(s) for MySQL and MariaDB 5.5.x can be found.";
+        infoprint "Check carefully each CVE for those particular versions";
+    }
+    badprint $cvefound . " CVE(s) found for your MySQL release.";
+    push( @generalrec,
+        $cvefound
+          . " CVE(s) found for your MySQL release. Consider upgrading your version !"
+    );
+}
+
+sub get_opened_ports {
+    my @opened_ports = `netstat -ltn`;
+    @opened_ports = map {
+        my $v = $_;
+        $v =~ s/.*:(\d+)\s.*$/$1/;
+        $v =~ s/\D//g;
+        $v;
+    } @opened_ports;
+    @opened_ports = sort { $a <=> $b } grep { !/^$/ } @opened_ports;
+
+    #debugprint Dumper \@opened_ports;
+    $result{'Network'}{'TCP Opened'} = \@opened_ports;
+    return @opened_ports;
+}
+
+sub is_open_port {
+    my $port = shift;
+    if ( grep { /^$port$/ } get_opened_ports ) {
+        return 1;
+    }
+    return 0;
+}
+
+sub get_process_memory {
+    my $pid = shift;
+    my @mem = `ps -p $pid -o rss`;
+    return 0 if scalar @mem != 2;
+    return $mem[1] * 1024;
+}
+
+sub get_other_process_memory {
+    return 0 if ( $opt{tbstat} == 0 );
+    my @procs = `ps eaxo pid,command`;
+    @procs = map {
+        my $v = $_;
+        $v =~ s/.*PID.*//;
+        $v =~ s/.*mysqld.*//;
+        $v =~ s/.*\[.*\].*//;
+        $v =~ s/^\s+$//g;
+        $v =~ s/.*PID.*CMD.*//;
+        $v =~ s/.*systemd.*//;
+        $v =~ s/\s*?(\d+)\s*.*/$1/g;
+        $v;
+    } @procs;
+    @procs = remove_cr @procs;
+    @procs = remove_empty @procs;
+    my $totalMemOther = 0;
+    map { $totalMemOther += get_process_memory($_); } @procs;
+    return $totalMemOther;
+}
+
+sub get_os_release {
+    if ( -f "/etc/lsb-release" ) {
+        my @info_release = get_file_contents "/etc/lsb-release";
+        my $os_release   = $info_release[3];
+        $os_release =~ s/.*="//;
+        $os_release =~ s/"$//;
+        return $os_release;
+    }
+
+    if ( -f "/etc/system-release" ) {
+        my @info_release = get_file_contents "/etc/system-release";
+        return $info_release[0];
+    }
+
+    if ( -f "/etc/os-release" ) {
+        my @info_release = get_file_contents "/etc/os-release";
+        my $os_release   = $info_release[0];
+        $os_release =~ s/.*="//;
+        $os_release =~ s/"$//;
+        return $os_release;
+    }
+
+    if ( -f "/etc/issue" ) {
+        my @info_release = get_file_contents "/etc/issue";
+        my $os_release   = $info_release[0];
+        $os_release =~ s/\s+\\n.*//;
+        return $os_release;
+    }
+    return "Unknown OS release";
+}
+
+sub get_fs_info {
+    my @sinfo = `df -P | grep '%'`;
+    my @iinfo = `df -Pi| grep '%'`;
+    shift @sinfo;
+    shift @iinfo;
+
+    foreach my $info (@sinfo) {
+
+        #exit(0);
+        if ( $info =~ /.*?(\d+)\s+(\d+)\s+(\d+)\s+(\d+)%\s+(.*)$/ ) {
+            next if $5 =~ m{(run|dev|sys|proc|snap|init)};
+            if ( $4 > 85 ) {
+                badprint "mount point $5 is using $4 % total space ("
+                  . human_size( $2 * 1024 ) . " / "
+                  . human_size( $1 * 1024 ) . ")";
+                push( @generalrec, "Add some space to $4 mountpoint." );
+            }
+            else {
+                infoprint "mount point $5 is using $4 % total space ("
+                  . human_size( $2 * 1024 ) . " / "
+                  . human_size( $1 * 1024 ) . ")";
+            }
+            $result{'Filesystem'}{'Space Pct'}{$5}   = $4;
+            $result{'Filesystem'}{'Used Space'}{$5}  = $2;
+            $result{'Filesystem'}{'Free Space'}{$5}  = $3;
+            $result{'Filesystem'}{'Total Space'}{$5} = $1;
+        }
+    }
+
+    @iinfo = map {
+        my $v = $_;
+        $v =~ s/.*\s(\d+)%\s+(.*)/$1\t$2/g;
+        $v;
+    } @iinfo;
+    foreach my $info (@iinfo) {
+        next if $info =~ m{(\d+)\t/(run|dev|sys|proc|snap)($|/)};
+        if ( $info =~ /(\d+)\t(.*)/ ) {
+            if ( $1 > 85 ) {
+                badprint "mount point $2 is using $1 % of max allowed inodes";
+                push( @generalrec,
+"Cleanup files from $2 mountpoint or reformat your filesystem."
+                );
+            }
+            else {
+                infoprint "mount point $2 is using $1 % of max allowed inodes";
+            }
+            $result{'Filesystem'}{'Inode Pct'}{$2} = $1;
+        }
+    }
+}
+
+sub merge_hash {
+    my $h1     = shift;
+    my $h2     = shift;
+    my %result = {};
+    foreach my $substanceref ( $h1, $h2 ) {
+        while ( my ( $k, $v ) = each %$substanceref ) {
+            next if ( exists $result{$k} );
+            $result{$k} = $v;
+        }
+    }
+    return \%result;
+}
+
+sub is_virtual_machine {
+    if ( $^O eq 'linux' ) {
+        my $isVm = `grep -Ec '^flags.*\ hypervisor\ ' /proc/cpuinfo`;
+        return ( $isVm == 0 ? 0 : 1 );
+    }
+
+    if ( $^O eq 'freebsd' ) {
+        my $isVm = `sysctl -n kern.vm_guest`;
+        chomp $isVm;
+        print "FARK DEBUG isVm=[$isVm]";
+        return ( $isVm eq 'none' ? 0 : 1 );
+    }
+    return 0;
+}
+
+sub infocmd {
+    my $cmd = "@_";
+    debugprint "CMD: $cmd";
+    my @result = `$cmd`;
+    @result = remove_cr @result;
+    for my $l (@result) {
+        infoprint "$l";
+    }
+}
+
+sub infocmd_tab {
+    my $cmd = "@_";
+    debugprint "CMD: $cmd";
+    my @result = `$cmd`;
+    @result = remove_cr @result;
+    for my $l (@result) {
+        infoprint "\t$l";
+    }
+}
+
+sub infocmd_one {
+    my $cmd    = "@_";
+    my @result = `$cmd 2>&1`;
+    @result = remove_cr @result;
+    return join ', ', @result;
+}
+
+sub get_kernel_info {
+    my @params = (
+        'fs.aio-max-nr',                 'fs.aio-nr',
+        'fs.nr_open',                    'fs.file-max',
+        'sunrpc.tcp_fin_timeout',        'sunrpc.tcp_max_slot_table_entries',
+        'sunrpc.tcp_slot_table_entries', 'vm.swappiness'
+    );
+    infoprint "Information about kernel tuning:";
+    foreach my $param (@params) {
+        infocmd_tab("sysctl $param 2>/dev/null");
+        $result{'OS'}{'Config'}{$param} = `sysctl -n $param 2>/dev/null`;
+    }
+    if ( `sysctl -n vm.swappiness` > 10 ) {
+        badprint
+          "Swappiness is > 10, please consider having a value lower than 10";
+        push @generalrec, "setup swappiness lower or equal to 10";
+        push @adjvars,
+'vm.swappiness <= 10 (echo 10 > /proc/sys/vm/swappiness) or vm.swappiness=10 in /etc/sysctl.conf';
+    }
+    else {
+        infoprint "Swappiness is < 10.";
+    }
+
+    # only if /proc/sys/sunrpc exists
+    my $tcp_slot_entries =
+      `sysctl -n sunrpc.tcp_slot_table_entries 2>/dev/null`;
+    if ( -f "/proc/sys/sunrpc"
+        and ( $tcp_slot_entries eq '' or $tcp_slot_entries < 100 ) )
+    {
+        badprint
+"Initial TCP slot entries is < 1M, please consider having a value greater than 100";
+        push @generalrec, "setup Initial TCP slot entries greater than 100";
+        push @adjvars,
+'sunrpc.tcp_slot_table_entries > 100 (echo 128 > /proc/sys/sunrpc/tcp_slot_table_entries)  or sunrpc.tcp_slot_table_entries=128 in /etc/sysctl.conf';
+    }
+    else {
+        infoprint "TCP slot entries is > 100.";
+    }
+
+    if ( -f "/proc/sys/fs/aio-max-nr" ) {
+        if ( `sysctl -n fs.aio-max-nr` < 1000000 ) {
+            badprint
+"Max running total of the number of max. events is < 1M, please consider having a value greater than 1M";
+            push @generalrec, "setup Max running number events greater than 1M";
+            push @adjvars,
+'fs.aio-max-nr > 1M (echo 1048576 > /proc/sys/fs/aio-max-nr) or fs.aio-max-nr=1048576 in /etc/sysctl.conf';
+        }
+        else {
+            infoprint "Max Number of AIO events is > 1M.";
+        }
+    }
+    if ( -f "/proc/sys/fs/nr_open" ) {
+        if ( `sysctl -n fs.nr_open` < 1000000 ) {
+            badprint
+"Max running total of the number of file open request is < 1M, please consider having a value greater than 1M";
+            push @generalrec,
+              "setup running number of open request greater than 1M";
+            push @adjvars,
+'fs.aio-nr > 1M (echo 1048576 > /proc/sys/fs/nr_open) or fs.nr_open=1048576 in /etc/sysctl.conf';
+        }
+        else {
+            infoprint "Max Number of open file requests is > 1M.";
+        }
+    }
+}
+
+sub get_system_info {
+    $result{'OS'}{'Release'} = get_os_release();
+    infoprint get_os_release;
+    if (is_virtual_machine) {
+        infoprint "Machine type          : Virtual machine";
+        $result{'OS'}{'Virtual Machine'} = 'YES';
+    }
+    else {
+        infoprint "Machine type          : Physical machine";
+        $result{'OS'}{'Virtual Machine'} = 'NO';
+    }
+
+    $result{'Network'}{'Connected'} = 'NO';
+    `ping -c 1 ipecho.net &>/dev/null`;
+    my $isConnected = $?;
+    if ( $? == 0 ) {
+        infoprint "Internet              : Connected";
+        $result{'Network'}{'Connected'} = 'YES';
+    }
+    else {
+        badprint "Internet              : Disconnected";
+    }
+    $result{'OS'}{'NbCore'} = cpu_cores;
+    infoprint "Number of Core CPU : " . cpu_cores;
+    $result{'OS'}{'Type'} = `uname -o`;
+    infoprint "Operating System Type : " . infocmd_one "uname -o";
+    $result{'OS'}{'Kernel'} = `uname -r`;
+    infoprint "Kernel Release        : " . infocmd_one "uname -r";
+    $result{'OS'}{'Hostname'}         = `hostname`;
+    $result{'Network'}{'Internal Ip'} = `hostname -I`;
+    infoprint "Hostname              : " . infocmd_one "hostname";
+    infoprint "Network Cards         : ";
+    infocmd_tab "ifconfig| grep -A1 mtu";
+    infoprint "Internal IP           : " . infocmd_one "hostname -I";
+    $result{'Network'}{'Internal Ip'} = `ifconfig| grep -A1 mtu`;
+    my $httpcli = get_http_cli();
+    infoprint "HTTP client found: $httpcli" if defined $httpcli;
+
+    my $ext_ip = "";
+    if ( $httpcli =~ /curl$/ ) {
+        $ext_ip = infocmd_one "$httpcli -m 3 ipecho.net/plain";
+    }
+    elsif ( $httpcli =~ /wget$/ ) {
+
+        $ext_ip = infocmd_one "$httpcli -t 1 -T 3 -q -O - ipecho.net/plain";
+    }
+    infoprint "External IP           : " . $ext_ip;
+    $result{'Network'}{'External Ip'} = $ext_ip;
+    badprint "External IP           : Can't check, no Internet connectivity"
+      unless defined($httpcli);
+    infoprint "Name Servers          : "
+      . infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'";
+    infoprint "Logged In users       : ";
+    infocmd_tab "who";
+    $result{'OS'}{'Logged users'} = `who`;
+    infoprint "Ram Usages in MB      : ";
+    infocmd_tab "free -m | grep -v +";
+    $result{'OS'}{'Free Memory RAM'} = `free -m | grep -v +`;
+    infoprint "Load Average          : ";
+    infocmd_tab "top -n 1 -b | grep 'load average:'";
+    $result{'OS'}{'Load Average'} = `top -n 1 -b | grep 'load average:'`;
+
+    infoprint "System Uptime         : ";
+    infocmd_tab "uptime";
+    $result{'OS'}{'Uptime'} = `uptime`;
+}
+
+sub system_recommendations {
+    if ( is_remote eq 1 ) {
+        infoprint "Skipping system checks on remote host";
+        return;
+    }
+    return if ( $opt{sysstat} == 0 );
+    subheaderprint "System Linux Recommendations";
+    my $os = `uname`;
+    unless ( $os =~ /Linux/i ) {
+        infoprint "Skipped due to non Linux server";
+        return;
+    }
+    prettyprint "Look for related Linux system recommendations";
+
+    #prettyprint '-'x78;
+    get_system_info();
+
+    my $nb_cpus = cpu_cores;
+    if ( $nb_cpus > 1 ) {
+        goodprint "There is at least one CPU dedicated to database server.";
+    }
+    else {
+        badprint
+"There is only one CPU, consider dedicated one CPU for your database server";
+        push @generalrec,
+          "Consider increasing number of CPU for your database server";
+    }
+
+    if ( $physical_memory >= 1.5 * 1024 ) {
+        goodprint "There is at least 1 Gb of RAM dedicated to Linux server.";
+    }
+    else {
+        badprint
+"There is less than 1,5 Gb of RAM, consider dedicated 1 Gb for your Linux server";
+        push @generalrec,
+          "Consider increasing 1,5 / 2 Gb of RAM for your Linux server";
+    }
+
+    my $omem = get_other_process_memory;
+    infoprint "User process except mysqld used "
+      . hr_bytes_rnd($omem) . " RAM.";
+    if ( ( 0.15 * $physical_memory ) < $omem ) {
+        if ( $opt{nondedicated} ) {
+            infoprint "No warning with --nondedicated option";
+            infoprint
+"Other user process except mysqld used more than 15% of total physical memory "
+              . percentage( $omem, $physical_memory ) . "% ("
+              . hr_bytes_rnd($omem) . " / "
+              . hr_bytes_rnd($physical_memory) . ")";
+        }
+        else {
+
+            badprint
+"Other user process except mysqld used more than 15% of total physical memory "
+              . percentage( $omem, $physical_memory ) . "% ("
+              . hr_bytes_rnd($omem) . " / "
+              . hr_bytes_rnd($physical_memory) . ")";
+            push( @generalrec,
+"Consider stopping or dedicate server for additional process other than mysqld."
+            );
+            push( @adjvars,
+"DON'T APPLY SETTINGS BECAUSE THERE ARE TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCUR!"
+            );
+        }
+    }
+    else {
+        infoprint
+"Other user process except mysqld used less than 15% of total physical memory "
+          . percentage( $omem, $physical_memory ) . "% ("
+          . hr_bytes_rnd($omem) . " / "
+          . hr_bytes_rnd($physical_memory) . ")";
+    }
+
+    if ( $opt{'maxportallowed'} > 0 ) {
+        my @opened_ports = get_opened_ports;
+        infoprint "There is "
+          . scalar @opened_ports
+          . " listening port(s) on this server.";
+        if ( scalar(@opened_ports) > $opt{'maxportallowed'} ) {
+            badprint "There are too many listening ports: "
+              . scalar(@opened_ports)
+              . " opened > "
+              . $opt{'maxportallowed'}
+              . "allowed.";
+            push( @generalrec,
+"Consider dedicating a server for your database installation with fewer services running on it!"
+            );
+        }
+        else {
+            goodprint "There are less than "
+              . $opt{'maxportallowed'}
+              . " opened ports on this server.";
+        }
+    }
+
+    foreach my $banport (@banned_ports) {
+        if ( is_open_port($banport) ) {
+            badprint "Banned port: $banport is opened..";
+            push( @generalrec,
+"Port $banport is opened. Consider stopping the program over this port."
+            );
+        }
+        else {
+            goodprint "$banport is not opened.";
+        }
+    }
+
+    subheaderprint "Filesystem Linux Recommendations";
+    get_fs_info;
+    subheaderprint "Kernel Information Recommendations";
+    get_kernel_info;
+}
+
+sub security_recommendations {
+    subheaderprint "Security Recommendations";
+
+    if ( mysql_version_eq(8) ) {
+        infoprint "Skipped due to unsupported feature for MySQL 8.0+";
+        return;
+    }
+
+    #exit 0;
+    if ( $opt{skippassword} eq 1 ) {
+        infoprint "Skipped due to --skippassword option";
+        return;
+    }
+
+    my $PASS_COLUMN_NAME = 'password';
+
+    # New table schema available since mysql-5.7 and mariadb-10.2
+    # But need to be checked
+    if ( $myvar{'version'} =~ /5\.7|10\.[2-5]\..*MariaDB*/ ) {
+        my $password_column_exists =
+`$mysqlcmd $mysqllogin -Bse "SELECT 1 FROM information_schema.columns WHERE TABLE_SCHEMA = 'mysql' AND TABLE_NAME = 'user' AND COLUMN_NAME = 'password'" 2>>/dev/null`;
+        my $authstring_column_exists =
+`$mysqlcmd $mysqllogin -Bse "SELECT 1 FROM information_schema.columns WHERE TABLE_SCHEMA = 'mysql' AND TABLE_NAME = 'user' AND COLUMN_NAME = 'authentication_string'" 2>>/dev/null`;
+        if ( $password_column_exists && $authstring_column_exists ) {
+            $PASS_COLUMN_NAME =
+"IF(plugin='mysql_native_password', authentication_string, password)";
+        }
+        elsif ($authstring_column_exists) {
+            $PASS_COLUMN_NAME = 'authentication_string';
+        }
+        elsif ( !$password_column_exists ) {
+            infoprint "Skipped due to none of known auth columns exists";
+            return;
+        }
+    }
+    debugprint "Password column = $PASS_COLUMN_NAME";
+
+    # IS THERE A ROLE COLUMN
+    my $is_role_column = select_one
+"select count(*) from information_schema.columns where TABLE_NAME='user' AND TABLE_SCHEMA='mysql' and COLUMN_NAME='IS_ROLE'";
+
+    my $extra_user_condition = "";
+    $extra_user_condition = "IS_ROLE = 'N' AND" if $is_role_column > 0;
+    my @mysqlstatlist;
+    if ( $is_role_column > 0 ) {
+        @mysqlstatlist = select_array
+"SELECT CONCAT(QUOTE(user), '\@', QUOTE(host)) FROM mysql.user WHERE IS_ROLE='Y'";
+        foreach my $line ( sort @mysqlstatlist ) {
+            chomp($line);
+            infoprint "User $line is User Role";
+        }
+    }
+    else {
+        debugprint "No Role user detected";
+        goodprint "No Role user detected";
+    }
+
+    # Looking for Anonymous users
+    @mysqlstatlist = select_array
+"SELECT CONCAT(QUOTE(user), '\@', QUOTE(host)) FROM mysql.user WHERE $extra_user_condition (TRIM(USER) = '' OR USER IS NULL)";
+
+    #debugprint Dumper \@mysqlstatlist;
+
+    #exit 0;
+    if (@mysqlstatlist) {
+        push( @generalrec,
+                "Remove Anonymous User accounts: there are "
+              . scalar(@mysqlstatlist)
+              . " anonymous accounts." );
+        foreach my $line ( sort @mysqlstatlist ) {
+            chomp($line);
+            badprint "User "
+              . $line
+              . " is an anonymous account. Remove with DROP USER "
+              . $line . ";";
+        }
+    }
+    else {
+        goodprint "There are no anonymous accounts for any database users";
+    }
+    if ( mysql_version_le( 5, 1 ) ) {
+        badprint "No more password checks for MySQL version <=5.1";
+        badprint "MySQL version <=5.1 is deprecated and end of support.";
+        return;
+    }
+
+    # Looking for Empty Password
+    if ( mysql_version_ge( 10, 4 ) ) {
+        @mysqlstatlist = select_array
+q{SELECT CONCAT(QUOTE(user), '@', QUOTE(host)) FROM mysql.global_priv WHERE
+    ( user != ''
+    AND JSON_CONTAINS(Priv, '"mysql_native_password"', '$.plugin') AND JSON_CONTAINS(Priv, '""', '$.authentication_string')
+    AND NOT JSON_CONTAINS(Priv, 'true', '$.account_locked')
+    )};
+    }
+    else {
+        @mysqlstatlist = select_array
+"SELECT CONCAT(QUOTE(user), '\@', QUOTE(host)) FROM mysql.user WHERE ($PASS_COLUMN_NAME = '' OR $PASS_COLUMN_NAME IS NULL)
+    AND user != ''
+    /*!50501 AND plugin NOT IN ('auth_socket', 'unix_socket', 'win_socket', 'auth_pam_compat') */
+    /*!80000 AND account_locked = 'N' AND password_expired = 'N' */";
+    }
+    if (@mysqlstatlist) {
+        foreach my $line ( sort @mysqlstatlist ) {
+            chomp($line);
+            badprint "User '" . $line . "' has no password set.";
+            push( @generalrec,
+"Set up a Secure Password for $line user: SET PASSWORD FOR $line = PASSWORD('secure_password');"
+            );
+        }
+    }
+    else {
+        goodprint "All database users have passwords assigned";
+    }
+
+    if ( mysql_version_ge( 5, 7 ) ) {
+        my $valPlugin = select_one(
+"select count(*) from information_schema.plugins where PLUGIN_NAME='validate_password' AND PLUGIN_STATUS='ACTIVE'"
+        );
+        if ( $valPlugin >= 1 ) {
+            infoprint
+"Bug #80860 MySQL 5.7: Avoid testing password when validate_password is activated";
+            return;
+        }
+    }
+
+    # Looking for User with user/ uppercase /capitalise user as password
+    @mysqlstatlist = select_array
+"SELECT CONCAT(QUOTE(user), '\@', QUOTE(host)) FROM mysql.user WHERE user != '' AND (CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(user) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(UPPER(user)) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(CONCAT(UPPER(LEFT(User, 1)), SUBSTRING(User, 2, LENGTH(User)))))";
+    if (@mysqlstatlist) {
+        foreach my $line ( sort @mysqlstatlist ) {
+            chomp($line);
+            badprint "User " . $line . " has user name as password.";
+            push( @generalrec,
+"Set up a Secure Password for $line user: SET PASSWORD FOR $line = PASSWORD('secure_password');"
+            );
+        }
+    }
+
+    @mysqlstatlist = select_array
+      "SELECT CONCAT(QUOTE(user), '\@', host) FROM mysql.user WHERE HOST='%'";
+    if (@mysqlstatlist) {
+        foreach my $line ( sort @mysqlstatlist ) {
+            chomp($line);
+            my $luser = ( split /@/, $line )[0];
+            badprint "User " . $line
+              . " does not specify hostname restrictions.";
+            push( @generalrec,
+"Restrict Host for $luser\@'%' to $luser\@LimitedIPRangeOrLocalhost"
+            );
+            push( @generalrec,
+                    "RENAME USER $luser\@'%' TO "
+                  . $luser
+                  . "\@LimitedIPRangeOrLocalhost;" );
+        }
+    }
+
+    unless ( -f $basic_password_files ) {
+        badprint "There is no basic password file list!";
+        return;
+    }
+
+    my @passwords = get_basic_passwords $basic_password_files;
+    infoprint "There are "
+      . scalar(@passwords)
+      . " basic passwords in the list.";
+    my $nbins = 0;
+    my $passreq;
+    if (@passwords) {
+        my $nbInterPass = 0;
+        foreach my $pass (@passwords) {
+            $nbInterPass++;
+
+            $pass =~ s/\s//g;
+            $pass =~ s/\'/\\\'/g;
+            chomp($pass);
+
+            # Looking for User with user/ uppercase /capitalise weak password
+            @mysqlstatlist =
+              select_array
+"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE $PASS_COLUMN_NAME = PASSWORD('"
+              . $pass
+              . "') OR $PASS_COLUMN_NAME = PASSWORD(UPPER('"
+              . $pass
+              . "')) OR $PASS_COLUMN_NAME = PASSWORD(CONCAT(UPPER(LEFT('"
+              . $pass
+              . "', 1)), SUBSTRING('"
+              . $pass
+              . "', 2, LENGTH('"
+              . $pass . "'))))";
+            debugprint "There are " . scalar(@mysqlstatlist) . " items.";
+            if (@mysqlstatlist) {
+                foreach my $line (@mysqlstatlist) {
+                    chomp($line);
+                    badprint "User '" . $line
+                      . "' is using weak password: $pass in a lower, upper or capitalize derivative version.";
+
+                    push( @generalrec,
+"Set up a Secure Password for $line user: SET PASSWORD FOR '"
+                          . ( split /@/, $line )[0] . "'\@'"
+                          . ( split /@/, $line )[1]
+                          . "' = PASSWORD('secure_password');" );
+                    $nbins++;
+                }
+            }
+            debugprint "$nbInterPass / " . scalar(@passwords)
+              if ( $nbInterPass % 1000 == 0 );
+        }
+    }
+    if ( $nbins > 0 ) {
+        push( @generalrec,
+            $nbins
+              . " user(s) used basic or weak password from basic dictionary." );
+    }
+}
+
+sub get_replication_status {
+    subheaderprint "Replication Metrics";
+    infoprint "Galera Synchronous replication: " . $myvar{'have_galera'};
+    if ( scalar( keys %myslaves ) == 0 ) {
+        infoprint "No replication slave(s) for this server.";
+    }
+    else {
+        infoprint "This server is acting as master for "
+          . scalar( keys %myslaves )
+          . " server(s).";
+    }
+    infoprint "Binlog format: " . $myvar{'binlog_format'};
+    infoprint "XA support enabled: " . $myvar{'innodb_support_xa'};
+
+    infoprint "Semi synchronous replication Master: "
+      . (
+        (
+                 defined( $myvar{'rpl_semi_sync_master_enabled'} )
+              or defined( $myvar{'rpl_semi_sync_source_enabled'} )
+        )
+        ? ( $myvar{'rpl_semi_sync_master_enabled'}
+              // $myvar{'rpl_semi_sync_source_enabled'} )
+        : 'Not Activated'
+      );
+    infoprint "Semi synchronous replication Slave: "
+      . (
+        (
+                 defined( $myvar{'rpl_semi_sync_slave_enabled'} )
+              or defined( $myvar{'rpl_semi_sync_replica_enabled'} )
+        )
+        ? ( $myvar{'rpl_semi_sync_slave_enabled'}
+              // $myvar{'rpl_semi_sync_replica_enabled'} )
+        : 'Not Activated'
+      );
+    if ( scalar( keys %myrepl ) == 0 and scalar( keys %myslaves ) == 0 ) {
+        infoprint "This is a standalone server";
+        return;
+    }
+    if ( scalar( keys %myrepl ) == 0 ) {
+        infoprint
+          "No replication setup for this server or replication not started.";
+        return;
+    }
+
+    $result{'Replication'}{'status'} = \%myrepl;
+    my ($io_running) = $myrepl{'Slave_IO_Running'}
+      // $myrepl{'Replica_IO_Running'};
+    debugprint "IO RUNNING: $io_running ";
+    my ($sql_running) = $myrepl{'Slave_SQL_Running'}
+      // $myrepl{'Replica_SQL_Running'};
+    debugprint "SQL RUNNING: $sql_running ";
+
+    my ($seconds_behind_master) = $myrepl{'Seconds_Behind_Master'}
+      // $myrepl{'Seconds_Behind_Source'};
+    $seconds_behind_master = 1000000 unless defined($seconds_behind_master);
+    debugprint "SECONDS : $seconds_behind_master ";
+
+    if ( defined($io_running)
+        and ( $io_running !~ /yes/i or $sql_running !~ /yes/i ) )
+    {
+        badprint
+          "This replication slave is not running but seems to be configured.";
+    }
+    if (   defined($io_running)
+        && $io_running  =~ /yes/i
+        && $sql_running =~ /yes/i )
+    {
+        if ( $myvar{'read_only'} eq 'OFF' ) {
+            badprint
+"This replication slave is running with the read_only option disabled.";
+        }
+        else {
+            goodprint
+"This replication slave is running with the read_only option enabled.";
+        }
+        if ( $seconds_behind_master > 0 ) {
+            badprint
+"This replication slave is lagging and slave has $seconds_behind_master second(s) behind master host.";
+        }
+        else {
+            goodprint "This replication slave is up to date with master.";
+        }
+    }
+}
+
+# https://endoflife.software/applications/databases/mysql
+# https://endoflife.date/mariadb
+sub validate_mysql_version {
+    ( $mysqlvermajor, $mysqlverminor, $mysqlvermicro ) =
+      $myvar{'version'} =~ /^(\d+)(?:\.(\d+)|)(?:\.(\d+)|)/;
+    $mysqlverminor ||= 0;
+    $mysqlvermicro ||= 0;
+
+    prettyprint " ";
+
+    if (   mysql_version_eq(9)
+        or mysql_version_eq(8, 4)
+				or mysql_version_eq(8, 0)
+        or mysql_version_eq( 10, 5 )
+        or mysql_version_eq( 10, 6 )
+        or mysql_version_eq( 10, 11 )
+        or mysql_version_eq( 11, 4 ) )
+    {
+        goodprint "Currently running supported MySQL version "
+          . $myvar{'version'} . "";
+        return;
+    }
+    else {
+        badprint "Your MySQL version "
+          . $myvar{'version'}
+          . " is EOL software. Upgrade soon!";
+        push( @generalrec,
+            "You are using an unsupported version for production environments"
+        );
+        push( @generalrec,
+            "Upgrade as soon as possible to a supported version !" );
+
+    }
+}
+
+# Checks if MySQL version is equal to (major, minor, micro)
+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) );
+}
+
+# Checks if MySQL version is greater than equal to (major, minor, micro)
+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) );
+}
+
+# Checks if MySQL version is lower than equal to (major, minor, micro)
+sub mysql_version_le {
+    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) );
+}
+
+# Checks for 32-bit boxes with more than 2GB of RAM
+my ($arch);
+
+sub check_architecture {
+    if ( is_remote eq 1 ) {
+        infoprint "Skipping architecture check on remote host";
+        infoprint "Using default $opt{defaultarch} bits as target architecture";
+        $arch = $opt{defaultarch};
+        return;
+    }
+    if ( `uname` =~ /SunOS/ && `isainfo -b` =~ /64/ ) {
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    elsif ( `uname` !~ /SunOS/ && `uname -m` =~ /(64|s390x)/ ) {
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    elsif ( `uname` =~ /AIX/ && `bootinfo -K` =~ /64/ ) {
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    elsif ( `uname` =~ /NetBSD|OpenBSD/ && `sysctl -b hw.machine` =~ /64/ ) {
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    elsif ( `uname` =~ /FreeBSD/ && `sysctl -b hw.machine_arch` =~ /64/ ) {
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    elsif ( `uname` =~ /Darwin/ && `uname -m` =~ /Power Macintosh/ ) {
+
+# Darwin box.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:57:01 PDT 2009; root:xnu1228.15.4~1/RELEASE_PPC Power Macintosh
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    elsif ( `uname` =~ /Darwin/ && `uname -m` =~ /x86_64/ ) {
+
+# Darwin gibas.local 12.6.0 Darwin Kernel Version 12.3.0: Sun Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64
+        $arch = 64;
+        goodprint "Operating on 64-bit architecture";
+    }
+    else {
+        $arch = 32;
+        if ( $physical_memory > 2147483648 ) {
+            badprint
+"Switch to 64-bit OS - MySQL cannot currently use all of your RAM";
+        }
+        else {
+            goodprint "Operating on 32-bit architecture with less than 2GB RAM";
+        }
+    }
+    $result{'OS'}{'Architecture'} = "$arch bits";
+
+}
+
+# Start up a ton of storage engine counts/statistics
+my ( %enginestats, %enginecount, $fragtables );
+
+sub check_storage_engines {
+    subheaderprint "Storage Engine Statistics";
+    if ( $opt{skipsize} eq 1 ) {
+        infoprint "Skipped due to --skipsize option";
+        return;
+    }
+
+    my $engines;
+    if ( mysql_version_ge( 5, 5 ) ) {
+        my @engineresults = select_array
+"SELECT ENGINE,SUPPORT FROM information_schema.ENGINES ORDER BY ENGINE ASC";
+        foreach my $line (@engineresults) {
+            my ( $engine, $engineenabled );
+            ( $engine, $engineenabled ) = $line =~ /([a-zA-Z_]*)\s+([a-zA-Z]+)/;
+            $result{'Engine'}{$engine}{'Enabled'} = $engineenabled;
+            $engines .=
+              ( $engineenabled eq "YES" || $engineenabled eq "DEFAULT" )
+              ? greenwrap "+" . $engine . " "
+              : redwrap "-" . $engine . " ";
+        }
+    }
+    elsif ( mysql_version_ge( 5, 1, 5 ) ) {
+        my @engineresults = select_array
+"SELECT ENGINE, SUPPORT FROM information_schema.ENGINES WHERE ENGINE NOT IN ('MyISAM', 'MERGE', 'MEMORY') ORDER BY ENGINE";
+        foreach my $line (@engineresults) {
+            my ( $engine, $engineenabled );
+            ( $engine, $engineenabled ) = $line =~ /([a-zA-Z_]*)\s+([a-zA-Z]+)/;
+            $result{'Engine'}{$engine}{'Enabled'} = $engineenabled;
+            $engines .=
+              ( $engineenabled eq "YES" || $engineenabled eq "DEFAULT" )
+              ? greenwrap "+" . $engine . " "
+              : redwrap "-" . $engine . " ";
+        }
+    }
+    else {
+        $engines .=
+          ( defined $myvar{'have_archive'} && $myvar{'have_archive'} eq "YES" )
+          ? greenwrap "+Archive "
+          : redwrap "-Archive ";
+        $engines .=
+          ( defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES" )
+          ? greenwrap "+BDB "
+          : redwrap "-BDB ";
+        $engines .=
+          ( defined $myvar{'have_federated_engine'}
+              && $myvar{'have_federated_engine'} eq "YES" )
+          ? greenwrap "+Federated "
+          : redwrap "-Federated ";
+        $engines .=
+          ( defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES" )
+          ? greenwrap "+InnoDB "
+          : redwrap "-InnoDB ";
+        $engines .=
+          ( defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES" )
+          ? greenwrap "+ISAM "
+          : redwrap "-ISAM ";
+        $engines .=
+          ( defined $myvar{'have_ndbcluster'}
+              && $myvar{'have_ndbcluster'} eq "YES" )
+          ? greenwrap "+NDBCluster "
+          : redwrap "-NDBCluster ";
+    }
+
+    my @dblist = grep { $_ ne 'lost+found' } select_array "SHOW DATABASES";
+
+    $result{'Databases'}{'List'} = [@dblist];
+    infoprint "Status: $engines";
+    if ( mysql_version_ge( 5, 1, 5 ) ) {
+
+# MySQL 5+ servers can have table sizes calculated quickly from information schema
+        my @templist = select_array
+"SELECT ENGINE, SUM(DATA_LENGTH+INDEX_LENGTH), COUNT(ENGINE), SUM(DATA_LENGTH), SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;";
+
+        my ( $engine, $size, $count, $dsize, $isize );
+        foreach my $line (@templist) {
+            ( $engine, $size, $count, $dsize, $isize ) =
+              $line =~ /([a-zA-Z_]+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/;
+            debugprint "Engine Found: $engine";
+            next unless ( defined($engine) or trim($engine) eq '' );
+            $size  = 0 unless ( defined($size)  or trim($engine) eq '' );
+            $isize = 0 unless ( defined($isize) or trim($engine) eq '' );
+            $dsize = 0 unless ( defined($dsize) or trim($engine) eq '' );
+            $count = 0 unless ( defined($count) or trim($engine) eq '' );
+            $enginestats{$engine}                      = $size;
+            $enginecount{$engine}                      = $count;
+            $result{'Engine'}{$engine}{'Table Number'} = $count;
+            $result{'Engine'}{$engine}{'Total Size'}   = $size;
+            $result{'Engine'}{$engine}{'Data Size'}    = $dsize;
+            $result{'Engine'}{$engine}{'Index Size'}   = $isize;
+        }
+
+        #print Dumper( \%enginestats ) if $opt{debug};
+        my $not_innodb = '';
+        if ( not defined $result{'Variables'}{'innodb_file_per_table'} ) {
+            $not_innodb = "AND NOT ENGINE='InnoDB'";
+        }
+        elsif ( $result{'Variables'}{'innodb_file_per_table'} eq 'OFF' ) {
+            $not_innodb = "AND NOT ENGINE='InnoDB'";
+        }
+        $result{'Tables'}{'Fragmented tables'} =
+          [ select_array
+"SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, CAST(DATA_FREE AS SIGNED) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND DATA_LENGTH/1024/1024>100 AND cast(DATA_FREE as signed)*100/(DATA_LENGTH+INDEX_LENGTH+cast(DATA_FREE as signed)) > 10 AND NOT ENGINE='MEMORY' $not_innodb"
+          ];
+        $fragtables = scalar @{ $result{'Tables'}{'Fragmented tables'} };
+
+    }
+    else {
+
+        # MySQL < 5 servers take a lot of work to get table sizes
+        my @tblist;
+
+# Now we build a database list, and loop through it to get storage engine stats for tables
+        foreach my $db (@dblist) {
+            chomp($db);
+            if (   $db eq "information_schema"
+                or $db eq "performance_schema"
+                or $db eq "mysql"
+                or $db eq "lost+found" )
+            {
+                next;
+            }
+            my @ixs = ( 1, 6, 9 );
+            if ( !mysql_version_ge( 4, 1 ) ) {
+
+                # MySQL 3.23/4.0 keeps Data_Length in the 5th (0-based) column
+                @ixs = ( 1, 5, 8 );
+            }
+            push( @tblist,
+                map { [ (split)[@ixs] ] }
+                  select_array "SHOW TABLE STATUS FROM \\\`$db\\\`" );
+        }
+
+     # Parse through the table list to generate storage engine counts/statistics
+        $fragtables = 0;
+        foreach my $tbl (@tblist) {
+
+            #debugprint "Data dump " . Dumper(@$tbl) if $opt{debug};
+            my ( $engine, $size, $datafree ) = @$tbl;
+            next if $engine eq 'NULL' or not defined($engine);
+            $size     = 0 if $size eq 'NULL'     or not defined($size);
+            $datafree = 0 if $datafree eq 'NULL' or not defined($datafree);
+            if ( defined $enginestats{$engine} ) {
+                $enginestats{$engine} += $size;
+                $enginecount{$engine} += 1;
+            }
+            else {
+                $enginestats{$engine} = $size;
+                $enginecount{$engine} = 1;
+            }
+            if ( $datafree > 0 ) {
+                $fragtables++;
+            }
+        }
+    }
+    while ( my ( $engine, $size ) = each(%enginestats) ) {
+        infoprint "Data in $engine tables: "
+          . hr_bytes($size)
+          . " (Tables: "
+          . $enginecount{$engine} . ")" . "";
+    }
+
+    # If the storage engine isn't being used, recommend it to be disabled
+    if (  !defined $enginestats{'InnoDB'}
+        && defined $myvar{'have_innodb'}
+        && $myvar{'have_innodb'} eq "YES" )
+    {
+        badprint "InnoDB is enabled, but isn't being used";
+        push( @generalrec,
+            "Add skip-innodb to MySQL configuration to disable InnoDB" );
+    }
+    if (  !defined $enginestats{'BerkeleyDB'}
+        && defined $myvar{'have_bdb'}
+        && $myvar{'have_bdb'} eq "YES" )
+    {
+        badprint "BDB is enabled, but isn't being used";
+        push( @generalrec,
+            "Add skip-bdb to MySQL configuration to disable BDB" );
+    }
+    if (  !defined $enginestats{'ISAM'}
+        && defined $myvar{'have_isam'}
+        && $myvar{'have_isam'} eq "YES" )
+    {
+        badprint "MyISAM is enabled, but isn't being used";
+        push( @generalrec,
+"Add skip-isam to MySQL configuration to disable MyISAM (MySQL > 4.1.0)"
+        );
+    }
+
+    # Fragmented tables
+    if ( $fragtables > 0 ) {
+        badprint "Total fragmented tables: $fragtables";
+        push @generalrec,
+'Run ALTER TABLE ... FORCE or OPTIMIZE TABLE to defragment tables for better performance';
+        my $total_free = 0;
+        foreach my $table_line ( @{ $result{'Tables'}{'Fragmented tables'} } ) {
+            my ( $table_schema, $table_name, $engine, $data_free ) =
+              split /\t/msx, $table_line;
+            $data_free = $data_free / 1024 / 1024;
+            $total_free += $data_free;
+            my $generalrec;
+            if ( $engine eq 'InnoDB' ) {
+                $generalrec =
+                  "  ALTER TABLE `$table_schema`.`$table_name` FORCE;";
+            }
+            else {
+                $generalrec = "  OPTIMIZE TABLE `$table_schema`.`$table_name`;";
+            }
+            $generalrec .= " -- can free $data_free MiB";
+            push @generalrec, $generalrec;
+        }
+        push @generalrec,
+          "Total freed space after defragmentation: $total_free MiB";
+    }
+    else {
+        goodprint "Total fragmented tables: $fragtables";
+    }
+
+    # Auto increments
+    my %tblist;
+
+    # Find the maximum integer
+    my $maxint = select_one "SELECT ~0";
+    $result{'MaxInt'} = $maxint;
+
+# Now we use a database list, and loop through it to get storage engine stats for tables
+    foreach my $db (@dblist) {
+        chomp($db);
+
+        if ( !$tblist{$db} ) {
+            $tblist{$db} = ();
+        }
+
+        if ( $db eq "information_schema" ) { next; }
+        my @ia = ( 0, 10 );
+        if ( !mysql_version_ge( 4, 1 ) ) {
+
+            # MySQL 3.23/4.0 keeps Data_Length in the 5th (0-based) column
+            @ia = ( 0, 9 );
+        }
+        push(
+            @{ $tblist{$db} },
+            map { [ (split)[@ia] ] }
+              select_array "SHOW TABLE STATUS FROM \\\`$db\\\`"
+        );
+    }
+
+    my @dbnames = keys %tblist;
+
+    foreach my $db (@dbnames) {
+        foreach my $tbl ( @{ $tblist{$db} } ) {
+            my ( $name, $autoincrement ) = @$tbl;
+
+            if ( $autoincrement =~ /^\d+?$/ ) {
+                my $percent = percentage( $autoincrement, $maxint );
+                $result{'PctAutoIncrement'}{"$db.$name"} = $percent;
+                if ( $percent >= 75 ) {
+                    badprint
+"Table '$db.$name' has an autoincrement value near max capacity ($percent%)";
+                }
+            }
+        }
+    }
+}
+
+my %mycalc;
+
+sub dump_into_file {
+    my $file    = shift;
+    my $content = shift;
+    if ( -d "$opt{dumpdir}" ) {
+        $file = "$opt{dumpdir}/$file";
+        open( FILE, ">$file" ) or die "Can't open $file: $!";
+        print FILE $content;
+        close FILE;
+        infoprint "Data saved to $file";
+    }
+}
+
+sub calculations {
+    if ( $mystat{'Questions'} < 1 ) {
+        badprint "Your server has not answered any queries: cannot continue...";
+        exit 2;
+    }
+
+    # Per-thread memory
+    $mycalc{'per_thread_buffers'} = 0;
+    $mycalc{'per_thread_buffers'} += $myvar{'read_buffer_size'}
+      if is_int( $myvar{'read_buffer_size'} );
+    $mycalc{'per_thread_buffers'} += $myvar{'read_rnd_buffer_size'}
+      if is_int( $myvar{'read_rnd_buffer_size'} );
+    $mycalc{'per_thread_buffers'} += $myvar{'sort_buffer_size'}
+      if is_int( $myvar{'sort_buffer_size'} );
+    $mycalc{'per_thread_buffers'} += $myvar{'thread_stack'}
+      if is_int( $myvar{'thread_stack'} );
+    $mycalc{'per_thread_buffers'} += $myvar{'join_buffer_size'}
+      if is_int( $myvar{'join_buffer_size'} );
+    $mycalc{'per_thread_buffers'} += $myvar{'binlog_cache_size'}
+      if is_int( $myvar{'binlog_cache_size'} );
+    debugprint "per_thread_buffers: $mycalc{'per_thread_buffers'} ("
+      . human_size( $mycalc{'per_thread_buffers'} ) . " )";
+
+# Error max_allowed_packet is not included in thread buffers size
+#$mycalc{'per_thread_buffers'} += $myvar{'max_allowed_packet'} if is_int($myvar{'max_allowed_packet'});
+
+    # Total per-thread memory
+    $mycalc{'total_per_thread_buffers'} =
+      $mycalc{'per_thread_buffers'} * $myvar{'max_connections'};
+
+    # Max total per-thread memory reached
+    $mycalc{'max_total_per_thread_buffers'} =
+      $mycalc{'per_thread_buffers'} * $mystat{'Max_used_connections'};
+
+    # Server-wide memory
+    $mycalc{'max_tmp_table_size'} =
+      ( $myvar{'tmp_table_size'} > $myvar{'max_heap_table_size'} )
+      ? $myvar{'max_heap_table_size'}
+      : $myvar{'tmp_table_size'};
+    $mycalc{'server_buffers'} =
+      $myvar{'key_buffer_size'} + $mycalc{'max_tmp_table_size'};
+    $mycalc{'server_buffers'} +=
+      ( defined $myvar{'innodb_buffer_pool_size'} )
+      ? $myvar{'innodb_buffer_pool_size'}
+      : 0;
+    $mycalc{'server_buffers'} +=
+      ( defined $myvar{'innodb_additional_mem_pool_size'} )
+      ? $myvar{'innodb_additional_mem_pool_size'}
+      : 0;
+    $mycalc{'server_buffers'} +=
+      ( defined $myvar{'innodb_log_buffer_size'} )
+      ? $myvar{'innodb_log_buffer_size'}
+      : 0;
+    $mycalc{'server_buffers'} +=
+      ( defined $myvar{'query_cache_size'} ) ? $myvar{'query_cache_size'} : 0;
+    $mycalc{'server_buffers'} +=
+      ( defined $myvar{'aria_pagecache_buffer_size'} )
+      ? $myvar{'aria_pagecache_buffer_size'}
+      : 0;
+
+# Global memory
+# Max used memory is memory used by MySQL based on Max_used_connections
+# This is the max memory used theoretically calculated with the max concurrent connection number reached by mysql
+    $mycalc{'max_used_memory'} =
+      $mycalc{'server_buffers'} +
+      $mycalc{"max_total_per_thread_buffers"} +
+      get_pf_memory();
+
+    #   + get_gcache_memory();
+    $mycalc{'pct_max_used_memory'} =
+      percentage( $mycalc{'max_used_memory'}, $physical_memory );
+
+# Total possible memory is memory needed by MySQL based on max_connections
+# This is the max memory MySQL can theoretically used if all connections allowed has opened by mysql
+    $mycalc{'max_peak_memory'} =
+      $mycalc{'server_buffers'} +
+      $mycalc{'total_per_thread_buffers'} +
+      get_pf_memory();
+
+    # +  get_gcache_memory();
+    $mycalc{'pct_max_physical_memory'} =
+      percentage( $mycalc{'max_peak_memory'}, $physical_memory );
+
+    debugprint "Max Used Memory: "
+      . hr_bytes( $mycalc{'max_used_memory'} ) . "";
+    debugprint "Max Used Percentage RAM: "
+      . $mycalc{'pct_max_used_memory'} . "%";
+
+    debugprint "Max Peak Memory: "
+      . hr_bytes( $mycalc{'max_peak_memory'} ) . "";
+    debugprint "Max Peak Percentage RAM: "
+      . $mycalc{'pct_max_physical_memory'} . "%";
+
+    # Slow queries
+    $mycalc{'pct_slow_queries'} =
+      int( ( $mystat{'Slow_queries'} / $mystat{'Questions'} ) * 100 );
+
+    # Connections
+    $mycalc{'pct_connections_used'} = int(
+        ( $mystat{'Max_used_connections'} / $myvar{'max_connections'} ) * 100 );
+    $mycalc{'pct_connections_used'} =
+      ( $mycalc{'pct_connections_used'} > 100 )
+      ? 100
+      : $mycalc{'pct_connections_used'};
+
+    # Aborted Connections
+    $mycalc{'pct_connections_aborted'} =
+      percentage( $mystat{'Aborted_connects'}, $mystat{'Connections'} );
+    debugprint "Aborted_connects: " . $mystat{'Aborted_connects'} . "";
+    debugprint "Connections: " . $mystat{'Connections'} . "";
+    debugprint "pct_connections_aborted: "
+      . $mycalc{'pct_connections_aborted'} . "";
+
+    # Key buffers
+    if ( mysql_version_ge( 4, 1 ) && $myvar{'key_buffer_size'} > 0 ) {
+        $mycalc{'pct_key_buffer_used'} = sprintf(
+            "%.1f",
+            (
+                1 - (
+                    (
+                        $mystat{'Key_blocks_unused'} *
+                          $myvar{'key_cache_block_size'}
+                    ) / $myvar{'key_buffer_size'}
+                )
+            ) * 100
+        );
+    }
+    else {
+        $mycalc{'pct_key_buffer_used'} = 0;
+    }
+
+    if ( $mystat{'Key_read_requests'} > 0 ) {
+        $mycalc{'pct_keys_from_mem'} = sprintf(
+            "%.1f",
+            (
+                100 - (
+                    ( $mystat{'Key_reads'} / $mystat{'Key_read_requests'} ) *
+                      100
+                )
+            )
+        );
+    }
+    else {
+        $mycalc{'pct_keys_from_mem'} = 0;
+    }
+    if ( defined $mystat{'Aria_pagecache_read_requests'}
+        && $mystat{'Aria_pagecache_read_requests'} > 0 )
+    {
+        $mycalc{'pct_aria_keys_from_mem'} = sprintf(
+            "%.1f",
+            (
+                100 - (
+                    (
+                        $mystat{'Aria_pagecache_reads'} /
+                          $mystat{'Aria_pagecache_read_requests'}
+                    ) * 100
+                )
+            )
+        );
+    }
+    else {
+        $mycalc{'pct_aria_keys_from_mem'} = 0;
+    }
+
+    if ( $mystat{'Key_write_requests'} > 0 ) {
+        $mycalc{'pct_wkeys_from_mem'} = sprintf( "%.1f",
+            ( ( $mystat{'Key_writes'} / $mystat{'Key_write_requests'} ) * 100 )
+        );
+    }
+    else {
+        $mycalc{'pct_wkeys_from_mem'} = 0;
+    }
+
+    if ( $doremote eq 0 and !mysql_version_ge(5) ) {
+        my $size = 0;
+        $size += (split)[0]
+          for
+`find "$myvar{'datadir'}" -name "*.MYI" -print0 2>&1 | xargs $xargsflags -0 du -L $duflags 2>&1`;
+        $mycalc{'total_myisam_indexes'} = $size;
+        $size = 0 + (split)[0]
+          for
+`find "$myvar{'datadir'}" -name "*.MAI" -print0 2>&1 | xargs $xargsflags -0 du -L $duflags 2>&1`;
+        $mycalc{'total_aria_indexes'} = $size;
+    }
+    elsif ( mysql_version_ge(5) ) {
+        $mycalc{'total_myisam_indexes'} = select_one
+"SELECT IFNULL(SUM(INDEX_LENGTH), 0) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema') AND ENGINE = 'MyISAM';";
+        $mycalc{'total_aria_indexes'} = select_one
+"SELECT IFNULL(SUM(INDEX_LENGTH), 0) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema') AND ENGINE = 'Aria';";
+    }
+    if ( defined $mycalc{'total_myisam_indexes'} ) {
+        chomp( $mycalc{'total_myisam_indexes'} );
+    }
+    if ( defined $mycalc{'total_aria_indexes'} ) {
+        chomp( $mycalc{'total_aria_indexes'} );
+    }
+
+    # Query cache
+    if ( mysql_version_ge(8) and mysql_version_le(10) ) {
+        $mycalc{'query_cache_efficiency'} = 0;
+    }
+    elsif ( mysql_version_ge(4) ) {
+        $mycalc{'query_cache_efficiency'} = sprintf(
+            "%.1f",
+            (
+                $mystat{'Qcache_hits'} /
+                  ( $mystat{'Com_select'} + $mystat{'Qcache_hits'} )
+            ) * 100
+        );
+        if ( $myvar{'query_cache_size'} ) {
+            $mycalc{'pct_query_cache_used'} = sprintf(
+                "%.1f",
+                100 - (
+                    $mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'}
+                ) * 100
+            );
+        }
+        if ( $mystat{'Qcache_lowmem_prunes'} == 0 ) {
+            $mycalc{'query_cache_prunes_per_day'} = 0;
+        }
+        else {
+            $mycalc{'query_cache_prunes_per_day'} = int(
+                $mystat{'Qcache_lowmem_prunes'} / ( $mystat{'Uptime'} / 86400 )
+            );
+        }
+    }
+
+    # Sorting
+    $mycalc{'total_sorts'} = $mystat{'Sort_scan'} + $mystat{'Sort_range'};
+    if ( $mycalc{'total_sorts'} > 0 ) {
+        $mycalc{'pct_temp_sort_table'} = int(
+            ( $mystat{'Sort_merge_passes'} / $mycalc{'total_sorts'} ) * 100 );
+    }
+
+    # Joins
+    $mycalc{'joins_without_indexes'} =
+      $mystat{'Select_range_check'} + $mystat{'Select_full_join'};
+    $mycalc{'joins_without_indexes_per_day'} =
+      int( $mycalc{'joins_without_indexes'} / ( $mystat{'Uptime'} / 86400 ) );
+
+    # Temporary tables
+    if ( $mystat{'Created_tmp_tables'} > 0 ) {
+        if ( $mystat{'Created_tmp_disk_tables'} > 0 ) {
+            $mycalc{'pct_temp_disk'} = int(
+                (
+                    $mystat{'Created_tmp_disk_tables'} /
+                      $mystat{'Created_tmp_tables'}
+                ) * 100
+            );
+        }
+        else {
+            $mycalc{'pct_temp_disk'} = 0;
+        }
+    }
+
+    # Table cache
+    if ( $mystat{'Opened_tables'} > 0 ) {
+        if ( not defined( $mystat{'Table_open_cache_hits'} ) ) {
+            $mycalc{'table_cache_hit_rate'} =
+              int( $mystat{'Open_tables'} * 100 / $mystat{'Opened_tables'} );
+        }
+        else {
+            $mycalc{'table_cache_hit_rate'} = int(
+                $mystat{'Table_open_cache_hits'} * 100 / (
+                    $mystat{'Table_open_cache_hits'} +
+                      $mystat{'Table_open_cache_misses'}
+                )
+            );
+        }
+    }
+    else {
+        $mycalc{'table_cache_hit_rate'} = 100;
+    }
+
+    # Open files
+    if ( $myvar{'open_files_limit'} > 0 ) {
+        $mycalc{'pct_files_open'} =
+          int( $mystat{'Open_files'} * 100 / $myvar{'open_files_limit'} );
+    }
+
+    # Table locks
+    if ( $mystat{'Table_locks_immediate'} > 0 ) {
+        if ( $mystat{'Table_locks_waited'} == 0 ) {
+            $mycalc{'pct_table_locks_immediate'} = 100;
+        }
+        else {
+            $mycalc{'pct_table_locks_immediate'} = int(
+                $mystat{'Table_locks_immediate'} * 100 / (
+                    $mystat{'Table_locks_waited'} +
+                      $mystat{'Table_locks_immediate'}
+                )
+            );
+        }
+    }
+
+    # Thread cache
+    $mycalc{'thread_cache_hit_rate'} =
+      int( 100 -
+          ( ( $mystat{'Threads_created'} / $mystat{'Connections'} ) * 100 ) );
+
+    # Other
+    if ( $mystat{'Connections'} > 0 ) {
+        $mycalc{'pct_aborted_connections'} =
+          int( ( $mystat{'Aborted_connects'} / $mystat{'Connections'} ) * 100 );
+    }
+    if ( $mystat{'Questions'} > 0 ) {
+        $mycalc{'total_reads'} = $mystat{'Com_select'};
+        $mycalc{'total_writes'} =
+          $mystat{'Com_delete'} +
+          $mystat{'Com_insert'} +
+          $mystat{'Com_update'} +
+          $mystat{'Com_replace'};
+        if ( $mycalc{'total_reads'} == 0 ) {
+            $mycalc{'pct_reads'}  = 0;
+            $mycalc{'pct_writes'} = 100;
+        }
+        else {
+            $mycalc{'pct_reads'} = int(
+                (
+                    $mycalc{'total_reads'} /
+                      ( $mycalc{'total_reads'} + $mycalc{'total_writes'} )
+                ) * 100
+            );
+            $mycalc{'pct_writes'} = 100 - $mycalc{'pct_reads'};
+        }
+    }
+
+    # InnoDB
+    $myvar{'innodb_log_files_in_group'} = 1
+      unless defined( $myvar{'innodb_log_files_in_group'} );
+    $myvar{'innodb_log_files_in_group'} = 1
+      if $myvar{'innodb_log_files_in_group'} == 0;
+
+    $myvar{"innodb_buffer_pool_instances"} = 1
+      unless defined( $myvar{'innodb_buffer_pool_instances'} );
+    if ( $myvar{'have_innodb'} eq "YES" ) {
+        if ( defined $myvar{'innodb_redo_log_capacity'} ) {
+          $mycalc{'innodb_log_size_pct'} =
+            ( $myvar{'innodb_redo_log_capacity'} /
+                $myvar{'innodb_buffer_pool_size'} ) * 100;
+        } else {
+          $mycalc{'innodb_log_size_pct'} =
+            ( $myvar{'innodb_log_file_size'} *
+                $myvar{'innodb_log_files_in_group'} * 100 /
+                $myvar{'innodb_buffer_pool_size'} );
+        }
+    }
+    if ( !defined $myvar{'innodb_buffer_pool_size'} ) {
+        $mycalc{'innodb_log_size_pct'}    = 0;
+        $myvar{'innodb_buffer_pool_size'} = 0;
+    }
+
+    # InnoDB Buffer pool read cache efficiency
+    (
+        $mystat{'Innodb_buffer_pool_read_requests'},
+        $mystat{'Innodb_buffer_pool_reads'}
+      )
+      = ( 1, 1 )
+      unless defined $mystat{'Innodb_buffer_pool_reads'};
+    $mycalc{'pct_read_efficiency'} = percentage(
+        $mystat{'Innodb_buffer_pool_read_requests'},
+        (
+            $mystat{'Innodb_buffer_pool_read_requests'} +
+              $mystat{'Innodb_buffer_pool_reads'}
+        )
+    ) if defined $mystat{'Innodb_buffer_pool_read_requests'};
+    debugprint "pct_read_efficiency: " . $mycalc{'pct_read_efficiency'} . "";
+    debugprint "Innodb_buffer_pool_reads: "
+      . $mystat{'Innodb_buffer_pool_reads'} . "";
+    debugprint "Innodb_buffer_pool_read_requests: "
+      . $mystat{'Innodb_buffer_pool_read_requests'} . "";
+
+    # InnoDB log write cache efficiency
+    ( $mystat{'Innodb_log_write_requests'}, $mystat{'Innodb_log_writes'} ) =
+      ( 1, 1 )
+      unless defined $mystat{'Innodb_log_writes'};
+    $mycalc{'pct_write_efficiency'} = percentage(
+        ( $mystat{'Innodb_log_write_requests'} - $mystat{'Innodb_log_writes'} ),
+        $mystat{'Innodb_log_write_requests'}
+    ) if defined $mystat{'Innodb_log_write_requests'};
+    debugprint "pct_write_efficiency: " . $mycalc{'pct_write_efficiency'} . "";
+    debugprint "Innodb_log_writes: " . $mystat{'Innodb_log_writes'} . "";
+    debugprint "Innodb_log_write_requests: "
+      . $mystat{'Innodb_log_write_requests'} . "";
+    $mycalc{'pct_innodb_buffer_used'} = percentage(
+        (
+            $mystat{'Innodb_buffer_pool_pages_total'} -
+              $mystat{'Innodb_buffer_pool_pages_free'}
+        ),
+        $mystat{'Innodb_buffer_pool_pages_total'}
+    ) if defined $mystat{'Innodb_buffer_pool_pages_total'};
+
+    my $lreq =
+        "select  ROUND( 100* sum(allocated)/ "
+      . $myvar{'innodb_buffer_pool_size'}
+      . ',1) FROM sys.x\$innodb_buffer_stats_by_table;';
+    debugprint("lreq: $lreq");
+    $mycalc{'innodb_buffer_alloc_pct'} = select_one($lreq)
+      if ( $opt{experimental} );
+
+    # Binlog Cache
+    if ( $myvar{'log_bin'} ne 'OFF' ) {
+        $mycalc{'pct_binlog_cache'} = percentage(
+            $mystat{'Binlog_cache_use'} - $mystat{'Binlog_cache_disk_use'},
+            $mystat{'Binlog_cache_use'} );
+    }
+}
+
+sub mysql_stats {
+    subheaderprint "Performance Metrics";
+
+    # Show uptime, queries per second, connections, traffic stats
+    my $qps;
+    if ( $mystat{'Uptime'} > 0 ) {
+        $qps = sprintf( "%.3f", $mystat{'Questions'} / $mystat{'Uptime'} );
+    }
+    push( @generalrec,
+"MySQL was started within the last 24 hours: recommendations may be inaccurate"
+    ) if ( $mystat{'Uptime'} < 86400 );
+    infoprint "Up for: "
+      . pretty_uptime( $mystat{'Uptime'} ) . " ("
+      . hr_num( $mystat{'Questions'} ) . " q ["
+      . hr_num($qps)
+      . " qps], "
+      . hr_num( $mystat{'Connections'} )
+      . " conn," . " TX: "
+      . hr_bytes_rnd( $mystat{'Bytes_sent'} )
+      . ", RX: "
+      . hr_bytes_rnd( $mystat{'Bytes_received'} ) . ")";
+    infoprint "Reads / Writes: "
+      . $mycalc{'pct_reads'} . "% / "
+      . $mycalc{'pct_writes'} . "%";
+
+    # Binlog Cache
+    if ( $myvar{'log_bin'} eq 'OFF' ) {
+        infoprint "Binary logging is disabled";
+    }
+    else {
+        infoprint "Binary logging is enabled (GTID MODE: "
+          . ( defined( $myvar{'gtid_mode'} ) ? $myvar{'gtid_mode'} : "OFF" )
+          . ")";
+    }
+
+    # Memory usage
+    infoprint "Physical Memory     : " . hr_bytes($physical_memory);
+    infoprint "Max MySQL memory    : " . hr_bytes( $mycalc{'max_peak_memory'} );
+    infoprint "Other process memory: " . hr_bytes( get_other_process_memory() );
+
+    infoprint "Total buffers: "
+      . hr_bytes( $mycalc{'server_buffers'} )
+      . " global + "
+      . hr_bytes( $mycalc{'per_thread_buffers'} )
+      . " per thread ($myvar{'max_connections'} max threads)";
+    infoprint "Performance_schema Max memory usage: "
+      . hr_bytes_rnd( get_pf_memory() );
+    $result{'Performance_schema'}{'memory'} = get_pf_memory();
+    $result{'Performance_schema'}{'pretty_memory'} =
+      hr_bytes_rnd( get_pf_memory() );
+    infoprint "Galera GCache Max memory usage: "
+      . hr_bytes_rnd( get_gcache_memory() );
+    $result{'Galera'}{'GCache'}{'memory'} = get_gcache_memory();
+    $result{'Galera'}{'GCache'}{'pretty_memory'} =
+      hr_bytes_rnd( get_gcache_memory() );
+
+    if ( $opt{buffers} ne 0 ) {
+        infoprint "Global Buffers";
+        infoprint " +-- Key Buffer: "
+          . hr_bytes( $myvar{'key_buffer_size'} ) . "";
+        infoprint " +-- Max Tmp Table: "
+          . hr_bytes( $mycalc{'max_tmp_table_size'} ) . "";
+
+        if ( defined $myvar{'query_cache_type'} ) {
+            infoprint "Query Cache Buffers";
+            infoprint " +-- Query Cache: "
+              . $myvar{'query_cache_type'} . " - "
+              . (
+                $myvar{'query_cache_type'} eq 0 |
+                  $myvar{'query_cache_type'} eq 'OFF' ? "DISABLED"
+                : (
+                    $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS"
+                    : "ON DEMAND"
+                )
+              ) . "";
+            infoprint " +-- Query Cache Size: "
+              . hr_bytes( $myvar{'query_cache_size'} ) . "";
+        }
+
+        infoprint "Per Thread Buffers";
+        infoprint " +-- Read Buffer: "
+          . hr_bytes( $myvar{'read_buffer_size'} ) . "";
+        infoprint " +-- Read RND Buffer: "
+          . hr_bytes( $myvar{'read_rnd_buffer_size'} ) . "";
+        infoprint " +-- Sort Buffer: "
+          . hr_bytes( $myvar{'sort_buffer_size'} ) . "";
+        infoprint " +-- Thread stack: "
+          . hr_bytes( $myvar{'thread_stack'} ) . "";
+        infoprint " +-- Join Buffer: "
+          . hr_bytes( $myvar{'join_buffer_size'} ) . "";
+        if ( $myvar{'log_bin'} ne 'OFF' ) {
+            infoprint "Binlog Cache Buffers";
+            infoprint " +-- Binlog Cache: "
+              . hr_bytes( $myvar{'binlog_cache_size'} ) . "";
+        }
+    }
+
+    if (   $arch
+        && $arch == 32
+        && $mycalc{'max_used_memory'} > 2 * 1024 * 1024 * 1024 )
+    {
+        badprint
+          "Allocating > 2GB RAM on 32-bit systems can cause system instability";
+        badprint "Maximum reached memory usage: "
+          . hr_bytes( $mycalc{'max_used_memory'} )
+          . " ($mycalc{'pct_max_used_memory'}% of installed RAM)";
+    }
+    elsif ( $mycalc{'pct_max_used_memory'} > 85 ) {
+        badprint "Maximum reached memory usage: "
+          . hr_bytes( $mycalc{'max_used_memory'} )
+          . " ($mycalc{'pct_max_used_memory'}% of installed RAM)";
+    }
+    else {
+        goodprint "Maximum reached memory usage: "
+          . hr_bytes( $mycalc{'max_used_memory'} )
+          . " ($mycalc{'pct_max_used_memory'}% of installed RAM)";
+    }
+
+    if ( $mycalc{'pct_max_physical_memory'} > 85 ) {
+        badprint "Maximum possible memory usage: "
+          . hr_bytes( $mycalc{'max_peak_memory'} )
+          . " ($mycalc{'pct_max_physical_memory'}% of installed RAM)";
+        push( @generalrec,
+            "Reduce your overall MySQL memory footprint for system stability" );
+    }
+    else {
+        goodprint "Maximum possible memory usage: "
+          . hr_bytes( $mycalc{'max_peak_memory'} )
+          . " ($mycalc{'pct_max_physical_memory'}% of installed RAM)";
+    }
+
+    if ( $physical_memory <
+        ( $mycalc{'max_peak_memory'} + get_other_process_memory() ) )
+    {
+        if ( $opt{nondedicated} ) {
+            infoprint "No warning with --nondedicated option";
+            infoprint
+"Overall possible memory usage with other process exceeded memory";
+        }
+        else {
+            badprint
+"Overall possible memory usage with other process exceeded memory";
+            push( @generalrec,
+                "Dedicate this server to your database for highest performance."
+            );
+        }
+    }
+    else {
+        goodprint
+"Overall possible memory usage with other process is compatible with memory available";
+    }
+
+    # Slow queries
+    if ( $mycalc{'pct_slow_queries'} > 5 ) {
+        badprint "Slow queries: $mycalc{'pct_slow_queries'}% ("
+          . hr_num( $mystat{'Slow_queries'} ) . "/"
+          . hr_num( $mystat{'Questions'} ) . ")";
+    }
+    else {
+        goodprint "Slow queries: $mycalc{'pct_slow_queries'}% ("
+          . hr_num( $mystat{'Slow_queries'} ) . "/"
+          . hr_num( $mystat{'Questions'} ) . ")";
+    }
+    if ( $myvar{'long_query_time'} > 10 ) {
+        push( @adjvars, "long_query_time (<= 10)" );
+    }
+    if ( defined( $myvar{'log_slow_queries'} ) ) {
+        if ( $myvar{'log_slow_queries'} eq "OFF" ) {
+            push( @generalrec,
+                "Enable the slow query log to troubleshoot bad queries" );
+        }
+    }
+
+    # Connections
+    if ( $mycalc{'pct_connections_used'} > 85 ) {
+        badprint
+"Highest connection usage: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})";
+        push( @adjvars,
+            "max_connections (> " . $myvar{'max_connections'} . ")" );
+        push( @adjvars,
+            "wait_timeout (< " . $myvar{'wait_timeout'} . ")",
+            "interactive_timeout (< " . $myvar{'interactive_timeout'} . ")" );
+        push( @generalrec,
+"Reduce or eliminate persistent connections to reduce connection usage"
+        );
+    }
+    else {
+        goodprint
+"Highest usage of available connections: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})";
+    }
+
+    # Aborted Connections
+    if ( $mycalc{'pct_connections_aborted'} > 3 ) {
+        badprint
+"Aborted connections: $mycalc{'pct_connections_aborted'}% ($mystat{'Aborted_connects'}/$mystat{'Connections'})";
+        push( @generalrec,
+            "Reduce or eliminate unclosed connections and network issues" );
+    }
+    else {
+        goodprint
+"Aborted connections: $mycalc{'pct_connections_aborted'}% ($mystat{'Aborted_connects'}/$mystat{'Connections'})";
+    }
+
+    # name resolution
+    debugprint "skip name resolve: $result{'Variables'}{'skip_name_resolve'}"
+      if ( defined( $result{'Variables'}{'skip_name_resolve'} ) );
+    if ( defined( $result{'Variables'}{'skip_networking'} )
+        && $result{'Variables'}{'skip_networking'} eq 'ON' )
+    {
+        infoprint
+"Skipped name resolution test due to skip_networking=ON in system variables.";
+    }
+    elsif ( not defined( $result{'Variables'}{'skip_name_resolve'} ) ) {
+        infoprint
+"Skipped name resolution test due to missing skip_name_resolve in system variables.";
+    }
+
+    #Cpanel and Skip name resolve
+    elsif ( -r "/usr/local/cpanel/cpanel" ) {
+        if ( $result{'Variables'}{'skip_name_resolve'} ne 'OFF' ) {
+            infoprint "CPanel and Flex system skip-name-resolve should be on";
+        }
+        if ( $result{'Variables'}{'skip_name_resolve'} eq 'OFF' ) {
+            badprint "CPanel and Flex system skip-name-resolve should be on";
+            push( @generalrec,
+"name resolution is enabled due to cPanel doesn't support this disabled."
+            );
+            push( @adjvars, "skip-name-resolve=0" );
+        }
+    }
+    elsif ( $result{'Variables'}{'skip_name_resolve'} ne 'ON'
+        and $result{'Variables'}{'skip_name_resolve'} ne '1' )
+    {
+        badprint
+"Name resolution is active: a reverse name resolution is made for each new connection which can reduce performance";
+        push( @generalrec,
+"Configure your accounts with ip or subnets only, then update your configuration with skip-name-resolve=ON"
+        );
+        push( @adjvars, "skip-name-resolve=ON" );
+    }
+
+    # Query cache
+    if ( !mysql_version_ge(4) ) {
+
+        # MySQL versions < 4.01 don't support query caching
+        push( @generalrec,
+            "Upgrade MySQL to version 4+ to utilize query caching" );
+    }
+    elsif ( mysql_version_eq(8) ) {
+        infoprint "Query cache has been removed since MySQL 8.0";
+
+        #return;
+    }
+    elsif ($myvar{'query_cache_size'} < 1
+        or $myvar{'query_cache_type'} eq "OFF" )
+    {
+        goodprint
+"Query cache is disabled by default due to mutex contention on multiprocessor machines.";
+    }
+    elsif ( $mystat{'Com_select'} == 0 ) {
+        badprint
+          "Query cache cannot be analyzed: no SELECT statements executed";
+    }
+    else {
+        if ( $mycalc{'query_cache_efficiency'} < 20 ) {
+            badprint
+              "Query cache efficiency: $mycalc{'query_cache_efficiency'}% ("
+              . hr_num( $mystat{'Qcache_hits'} )
+              . " cached / "
+              . hr_num( $mystat{'Qcache_hits'} + $mystat{'Com_select'} )
+              . " selects)";
+            push( @adjvars,
+                    "query_cache_limit (> "
+                  . hr_bytes_rnd( $myvar{'query_cache_limit'} )
+                  . ", or use smaller result sets)" );
+            badprint
+              "Query cache may be disabled by default due to mutex contention.";
+            push( @adjvars, "query_cache_size (=0)" );
+            push( @adjvars, "query_cache_type (=0)" );
+        }
+        else {
+            goodprint
+              "Query cache efficiency: $mycalc{'query_cache_efficiency'}% ("
+              . hr_num( $mystat{'Qcache_hits'} )
+              . " cached / "
+              . hr_num( $mystat{'Qcache_hits'} + $mystat{'Com_select'} )
+              . " selects)";
+            if ( $mycalc{'query_cache_prunes_per_day'} > 98 ) {
+                badprint
+"Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}";
+                if ( $myvar{'query_cache_size'} >= 128 * 1024 * 1024 ) {
+                    push( @generalrec,
+"Increasing the query_cache size over 128M may reduce performance"
+                    );
+                    push( @adjvars,
+                            "query_cache_size (> "
+                          . hr_bytes_rnd( $myvar{'query_cache_size'} )
+                          . ") [see warning above]" );
+                }
+                else {
+                    push( @adjvars,
+                            "query_cache_size (> "
+                          . hr_bytes_rnd( $myvar{'query_cache_size'} )
+                          . ")" );
+                }
+            }
+            else {
+                goodprint
+"Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}";
+            }
+        }
+
+    }
+
+    # Sorting
+    if ( $mycalc{'total_sorts'} == 0 ) {
+        goodprint "No Sort requiring temporary tables";
+    }
+    elsif ( $mycalc{'pct_temp_sort_table'} > 10 ) {
+        badprint
+          "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% ("
+          . hr_num( $mystat{'Sort_merge_passes'} )
+          . " temp sorts / "
+          . hr_num( $mycalc{'total_sorts'} )
+          . " sorts)";
+        push( @adjvars,
+                "sort_buffer_size (> "
+              . hr_bytes_rnd( $myvar{'sort_buffer_size'} )
+              . ")" );
+        push( @adjvars,
+                "read_rnd_buffer_size (> "
+              . hr_bytes_rnd( $myvar{'read_rnd_buffer_size'} )
+              . ")" );
+    }
+    else {
+        goodprint
+          "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% ("
+          . hr_num( $mystat{'Sort_merge_passes'} )
+          . " temp sorts / "
+          . hr_num( $mycalc{'total_sorts'} )
+          . " sorts)";
+    }
+
+    # Joins
+    if ( $mycalc{'joins_without_indexes_per_day'} > 250 ) {
+        badprint
+          "Joins performed without indexes: $mycalc{'joins_without_indexes'}";
+        push( @adjvars,
+                "join_buffer_size (> "
+              . hr_bytes( $myvar{'join_buffer_size'} )
+              . ", or always use indexes with JOINs)" );
+        push(
+            @generalrec,
+"We will suggest raising the 'join_buffer_size' until JOINs not using indexes are found.
+             See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_join_buffer_size"
+        );
+    }
+    else {
+        goodprint "No joins without indexes";
+
+        # No joins have run without indexes
+    }
+
+    # Temporary tables
+    if ( $mystat{'Created_tmp_tables'} > 0 ) {
+        if (   $mycalc{'pct_temp_disk'} > 25
+            && $mycalc{'max_tmp_table_size'} < 256 * 1024 * 1024 )
+        {
+            badprint
+              "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% ("
+              . hr_num( $mystat{'Created_tmp_disk_tables'} )
+              . " on disk / "
+              . hr_num( $mystat{'Created_tmp_tables'} )
+              . " total)";
+            push( @adjvars,
+                    "tmp_table_size (> "
+                  . hr_bytes_rnd( $myvar{'tmp_table_size'} )
+                  . ")" );
+            push( @adjvars,
+                    "max_heap_table_size (> "
+                  . hr_bytes_rnd( $myvar{'max_heap_table_size'} )
+                  . ")" );
+            push( @generalrec,
+"When making adjustments, make tmp_table_size/max_heap_table_size equal"
+            );
+            push( @generalrec,
+                "Reduce your SELECT DISTINCT queries which have no LIMIT clause"
+            );
+        }
+        elsif ($mycalc{'pct_temp_disk'} > 25
+            && $mycalc{'max_tmp_table_size'} >= 256 * 1024 * 1024 )
+        {
+            badprint
+              "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% ("
+              . hr_num( $mystat{'Created_tmp_disk_tables'} )
+              . " on disk / "
+              . hr_num( $mystat{'Created_tmp_tables'} )
+              . " total)";
+            push( @generalrec,
+                "Temporary table size is already large: reduce result set size"
+            );
+            push( @generalrec,
+                "Reduce your SELECT DISTINCT queries without LIMIT clauses" );
+        }
+        else {
+            goodprint
+              "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% ("
+              . hr_num( $mystat{'Created_tmp_disk_tables'} )
+              . " on disk / "
+              . hr_num( $mystat{'Created_tmp_tables'} )
+              . " total)";
+        }
+    }
+    else {
+        goodprint "No tmp tables created on disk";
+    }
+
+    # Thread cache
+    if ( defined( $myvar{'have_threadpool'} )
+        and $myvar{'have_threadpool'} eq 'YES' )
+    {
+# https://www.percona.com/doc/percona-server/5.7/performance/threadpool.html#status-variables
+# When thread pool is enabled, the value of the thread_cache_size variable
+# is ignored. The Threads_cached status variable contains 0 in this case.
+        infoprint "Thread cache not used with thread pool enabled";
+    }
+    else {
+        if ( $myvar{'thread_cache_size'} eq 0 ) {
+            badprint "Thread cache is disabled";
+            push( @generalrec,
+                "Set thread_cache_size to 4 as a starting value" );
+            push( @adjvars, "thread_cache_size (start at 4)" );
+        }
+        else {
+            if ( $mycalc{'thread_cache_hit_rate'} <= 50 ) {
+                badprint
+                  "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% ("
+                  . hr_num( $mystat{'Threads_created'} )
+                  . " created / "
+                  . hr_num( $mystat{'Connections'} )
+                  . " connections)";
+                push( @adjvars,
+                    "thread_cache_size (> $myvar{'thread_cache_size'})" );
+            }
+            else {
+                goodprint
+                  "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% ("
+                  . hr_num( $mystat{'Threads_created'} )
+                  . " created / "
+                  . hr_num( $mystat{'Connections'} )
+                  . " connections)";
+            }
+        }
+    }
+
+    # Table cache
+    my $table_cache_var = "";
+    if ( $mystat{'Open_tables'} > 0 ) {
+        if ( $mycalc{'table_cache_hit_rate'} < 20 ) {
+
+            unless ( defined( $mystat{'Table_open_cache_hits'} ) ) {
+                badprint
+                  "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% ("
+                  . hr_num( $mystat{'Open_tables'} )
+                  . " hits / "
+                  . hr_num( $mystat{'Opened_tables'} )
+                  . " requests)";
+            }
+            else {
+                badprint
+                  "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% ("
+                  . hr_num( $mystat{'Table_open_cache_hits'} )
+                  . " hits / "
+                  . hr_num( $mystat{'Table_open_cache_hits'} +
+                      $mystat{'Table_open_cache_misses'} )
+                  . " requests)";
+            }
+
+            if ( mysql_version_ge( 5, 1 ) ) {
+                $table_cache_var = "table_open_cache";
+            }
+            else {
+                $table_cache_var = "table_cache";
+            }
+
+            push( @adjvars,
+                $table_cache_var . " (> " . $myvar{$table_cache_var} . ")" );
+            push( @generalrec,
+                    "Increase "
+                  . $table_cache_var
+                  . " gradually to avoid file descriptor limits" );
+            push( @generalrec,
+                    "Read this before increasing "
+                  . $table_cache_var
+                  . " over 64: https://bit.ly/2Fulv7r" );
+            push( @generalrec,
+                    "Read this before increasing for MariaDB"
+                  . " https://mariadb.com/kb/en/library/optimizing-table_open_cache/"
+            );
+            push( @generalrec,
+"This is MyISAM only table_cache scalability problem, InnoDB not affected."
+            );
+            push( @generalrec,
+                "For more details see: https://bugs.mysql.com/bug.php?id=49177"
+            );
+            push( @generalrec,
+"This bug already fixed in MySQL 5.7.9 and newer MySQL versions."
+            );
+            push( @generalrec,
+                    "Beware that open_files_limit ("
+                  . $myvar{'open_files_limit'}
+                  . ") variable " );
+            push( @generalrec,
+                    "should be greater than $table_cache_var ("
+                  . $myvar{$table_cache_var}
+                  . ")" );
+        }
+        else {
+            unless ( defined( $mystat{'Table_open_cache_hits'} ) ) {
+                goodprint
+                  "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% ("
+                  . hr_num( $mystat{'Open_tables'} )
+                  . " hits / "
+                  . hr_num( $mystat{'Opened_tables'} )
+                  . " requests)";
+            }
+            else {
+                goodprint
+                  "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% ("
+                  . hr_num( $mystat{'Table_open_cache_hits'} )
+                  . " hits / "
+                  . hr_num( $mystat{'Table_open_cache_hits'} +
+                      $mystat{'Table_open_cache_misses'} )
+                  . " requests)";
+            }
+        }
+    }
+
+    # Table definition cache
+    my $nbtables = select_one('SELECT COUNT(*) FROM information_schema.tables');
+    $mycalc{'total_tables'} = $nbtables;
+    if ( defined $myvar{'table_definition_cache'} ) {
+        if ( $myvar{'table_definition_cache'} == -1 ) {
+            infoprint( "table_definition_cache ("
+                  . $myvar{'table_definition_cache'}
+                  . ") is in autosizing mode" );
+        }
+        elsif ( $myvar{'table_definition_cache'} < $nbtables ) {
+            badprint "table_definition_cache ("
+              . $myvar{'table_definition_cache'}
+              . ") is less than number of tables ($nbtables) ";
+            push( @adjvars,
+                    "table_definition_cache ("
+                  . $myvar{'table_definition_cache'} . ") > "
+                  . $nbtables
+                  . " or -1 (autosizing if supported)" );
+        }
+        else {
+            goodprint "table_definition_cache ("
+              . $myvar{'table_definition_cache'}
+              . ") is greater than number of tables ($nbtables)";
+        }
+    }
+    else {
+        infoprint "No table_definition_cache variable found.";
+    }
+
+    # Open files
+    if ( defined $mycalc{'pct_files_open'} ) {
+        if ( $mycalc{'pct_files_open'} > 85 ) {
+            badprint "Open file limit used: $mycalc{'pct_files_open'}% ("
+              . hr_num( $mystat{'Open_files'} ) . "/"
+              . hr_num( $myvar{'open_files_limit'} ) . ")";
+            push( @adjvars,
+                "open_files_limit (> " . $myvar{'open_files_limit'} . ")" );
+        }
+        else {
+            goodprint "Open file limit used: $mycalc{'pct_files_open'}% ("
+              . hr_num( $mystat{'Open_files'} ) . "/"
+              . hr_num( $myvar{'open_files_limit'} ) . ")";
+        }
+    }
+
+    # Table locks
+    if ( defined $mycalc{'pct_table_locks_immediate'} ) {
+        if ( $mycalc{'pct_table_locks_immediate'} < 95 ) {
+            badprint
+"Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}%";
+            push( @generalrec,
+                "Optimize queries and/or use InnoDB to reduce lock wait" );
+        }
+        else {
+            goodprint
+"Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}% ("
+              . hr_num( $mystat{'Table_locks_immediate'} )
+              . " immediate / "
+              . hr_num( $mystat{'Table_locks_waited'} +
+                  $mystat{'Table_locks_immediate'} )
+              . " locks)";
+        }
+    }
+
+    # Binlog cache
+    if ( defined $mycalc{'pct_binlog_cache'} ) {
+        if (   $mycalc{'pct_binlog_cache'} < 90
+            && $mystat{'Binlog_cache_use'} > 0 )
+        {
+            badprint "Binlog cache memory access: "
+              . $mycalc{'pct_binlog_cache'} . "% ("
+              . (
+                $mystat{'Binlog_cache_use'} - $mystat{'Binlog_cache_disk_use'} )
+              . " Memory / "
+              . $mystat{'Binlog_cache_use'}
+              . " Total)";
+            push( @generalrec,
+                    "Increase binlog_cache_size (current value: "
+                  . $myvar{'binlog_cache_size'}
+                  . ")" );
+            push( @adjvars,
+                    "binlog_cache_size ("
+                  . hr_bytes( $myvar{'binlog_cache_size'} + 16 * 1024 * 1024 )
+                  . ")" );
+        }
+        else {
+            goodprint "Binlog cache memory access: "
+              . $mycalc{'pct_binlog_cache'} . "% ("
+              . (
+                $mystat{'Binlog_cache_use'} - $mystat{'Binlog_cache_disk_use'} )
+              . " Memory / "
+              . $mystat{'Binlog_cache_use'}
+              . " Total)";
+            debugprint "Not enough data to validate binlog cache size\n"
+              if $mystat{'Binlog_cache_use'} < 10;
+        }
+    }
+
+    # Performance options
+    if ( !mysql_version_ge( 5, 1 ) ) {
+        push( @generalrec, "Upgrade to MySQL 5.5+ to use asynchronous write" );
+    }
+    elsif ( $myvar{'concurrent_insert'} eq "OFF" ) {
+        push( @generalrec, "Enable concurrent_insert by setting it to 'ON'" );
+    }
+    elsif ( $myvar{'concurrent_insert'} eq 0 ) {
+        push( @generalrec, "Enable concurrent_insert by setting it to 1" );
+    }
+}
+
+# Recommendations for MyISAM
+sub mysql_myisam {
+    return 0 unless ( $opt{'myisamstat'} > 0 );
+    subheaderprint "MyISAM Metrics";
+    my $nb_myisam_tables = select_one(
+"SELECT COUNT(*) FROM information_schema.TABLES WHERE ENGINE='MyISAM' and TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema')"
+    );
+    push( @generalrec,
+        "MyISAM engine is deprecated, consider migrating to InnoDB" )
+      if $nb_myisam_tables > 0;
+
+    if ( $nb_myisam_tables > 0 ) {
+        badprint
+          "Consider migrating $nb_myisam_tables following tables to InnoDB:";
+        my $sql_mig = "";
+        for my $myisam_table (
+            select_array(
+"SELECT CONCAT(TABLE_SCHEMA, '.', TABLE_NAME) FROM information_schema.TABLES WHERE ENGINE='MyISAM' and TABLE_SCHEMA NOT IN ('mysql','information_schema','performance_schema')"
+            )
+          )
+        {
+            $sql_mig =
+"${sql_mig}-- InnoDB migration for $myisam_table\nALTER TABLE $myisam_table ENGINE=InnoDB;\n\n";
+            infoprint
+"* InnoDB migration request for $myisam_table Table: ALTER TABLE $myisam_table ENGINE=InnoDB;";
+        }
+        dump_into_file( "migrate_myisam_to_innodb.sql", $sql_mig );
+    }
+    infoprint("General MyIsam metrics:");
+    infoprint " +-- Total MyISAM Tables  : $nb_myisam_tables";
+    infoprint " +-- Total MyISAM indexes : "
+      . hr_bytes( $mycalc{'total_myisam_indexes'} )
+      if defined( $mycalc{'total_myisam_indexes'} );
+    infoprint " +-- KB Size :" . hr_bytes( $myvar{'key_buffer_size'} );
+    infoprint " +-- KB Used Size :"
+      . hr_bytes( $myvar{'key_buffer_size'} -
+          $mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'} );
+    infoprint " +-- KB used :" . $mycalc{'pct_key_buffer_used'} . "%";
+    infoprint " +-- Read KB hit rate: $mycalc{'pct_keys_from_mem'}% ("
+      . hr_num( $mystat{'Key_read_requests'} )
+      . " cached / "
+      . hr_num( $mystat{'Key_reads'} )
+      . " reads)";
+    infoprint " +-- Write KB hit rate: $mycalc{'pct_wkeys_from_mem'}% ("
+      . hr_num( $mystat{'Key_write_requests'} )
+      . " cached / "
+      . hr_num( $mystat{'Key_writes'} )
+      . " writes)";
+
+    if ( $nb_myisam_tables == 0 ) {
+        infoprint "No MyISAM table(s) detected ....";
+        return;
+    }
+    if ( mysql_version_ge(8) and mysql_version_le(10) ) {
+        infoprint "MyISAM Metrics are disabled since MySQL 8.0.";
+        if ( $myvar{'key_buffer_size'} > 0 ) {
+            push( @adjvars, "key_buffer_size=0" );
+            push( @generalrec,
+                "Buffer Key MyISAM set to 0, no MyISAM table detected" );
+        }
+        return;
+    }
+
+    if ( !defined( $mycalc{'total_myisam_indexes'} ) ) {
+        badprint
+          "Unable to calculate MyISAM index size on MySQL server < 5.0.0";
+        push( @generalrec,
+            "Unable to calculate MyISAM index size on MySQL server < 5.0.0" );
+        return;
+    }
+    if ( $mycalc{'pct_key_buffer_used'} == 0 ) {
+
+        # No queries have run that would use keys
+        infoprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% ("
+          . hr_bytes( $myvar{'key_buffer_size'} -
+              $mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'} )
+          . " used / "
+          . hr_bytes( $myvar{'key_buffer_size'} )
+          . " cache)";
+        infoprint "No SQL statement based on MyISAM table(s) detected ....";
+        return;
+    }
+
+    # Key buffer usage
+    if ( $mycalc{'pct_key_buffer_used'} < 90 ) {
+        badprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% ("
+          . hr_bytes( $myvar{'key_buffer_size'} -
+              $mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'} )
+          . " used / "
+          . hr_bytes( $myvar{'key_buffer_size'} )
+          . " cache)";
+
+        push(
+            @adjvars,
+            "key_buffer_size (\~ "
+              . hr_num(
+                $myvar{'key_buffer_size'} *
+                  $mycalc{'pct_key_buffer_used'} / 100
+              )
+              . ")"
+        );
+    }
+    else {
+        goodprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% ("
+          . hr_bytes( $myvar{'key_buffer_size'} -
+              $mystat{'Key_blocks_unused'} * $myvar{'key_cache_block_size'} )
+          . " used / "
+          . hr_bytes( $myvar{'key_buffer_size'} )
+          . " cache)";
+    }
+
+    # Key buffer size / total MyISAM indexes
+    if (   $myvar{'key_buffer_size'} < $mycalc{'total_myisam_indexes'}
+        && $mycalc{'pct_keys_from_mem'} < 95 )
+    {
+        badprint "Key buffer size / total MyISAM indexes: "
+          . hr_bytes( $myvar{'key_buffer_size'} ) . "/"
+          . hr_bytes( $mycalc{'total_myisam_indexes'} ) . "";
+        push( @adjvars,
+                "key_buffer_size (> "
+              . hr_bytes( $mycalc{'total_myisam_indexes'} )
+              . ")" );
+    }
+    else {
+        goodprint "Key buffer size / total MyISAM indexes: "
+          . hr_bytes( $myvar{'key_buffer_size'} ) . "/"
+          . hr_bytes( $mycalc{'total_myisam_indexes'} ) . "";
+    }
+    if ( $mystat{'Key_read_requests'} > 0 ) {
+        if ( $mycalc{'pct_keys_from_mem'} < 95 ) {
+            badprint
+              "Read Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% ("
+              . hr_num( $mystat{'Key_read_requests'} )
+              . " cached / "
+              . hr_num( $mystat{'Key_reads'} )
+              . " reads)";
+        }
+        else {
+            goodprint
+              "Read Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% ("
+              . hr_num( $mystat{'Key_read_requests'} )
+              . " cached / "
+              . hr_num( $mystat{'Key_reads'} )
+              . " reads)";
+        }
+    }
+
+    # No queries have run that would use keys
+    debugprint "Key buffer size / total MyISAM indexes: "
+      . hr_bytes( $myvar{'key_buffer_size'} ) . "/"
+      . hr_bytes( $mycalc{'total_myisam_indexes'} ) . "";
+    if ( $mystat{'Key_write_requests'} > 0 ) {
+        if ( $mycalc{'pct_wkeys_from_mem'} < 95 ) {
+            badprint
+              "Write Key buffer hit rate: $mycalc{'pct_wkeys_from_mem'}% ("
+              . hr_num( $mystat{'Key_write_requests'} )
+              . " cached / "
+              . hr_num( $mystat{'Key_writes'} )
+              . " writes)";
+        }
+        else {
+            goodprint
+              "Write Key buffer hit rate: $mycalc{'pct_wkeys_from_mem'}% ("
+              . hr_num( $mystat{'Key_write_requests'} )
+              . " cached / "
+              . hr_num( $mystat{'Key_writes'} )
+              . " writes)";
+        }
+    }
+    else {
+        # No queries have run that would use keys
+        debugprint
+          "Write Key buffer hit rate: $mycalc{'pct_wkeys_from_mem'}% ("
+          . hr_num( $mystat{'Key_write_requests'} )
+          . " cached / "
+          . hr_num( $mystat{'Key_writes'} )
+          . " writes)";
+    }
+}
+
+# Recommendations for ThreadPool
+sub mariadb_threadpool {
+    subheaderprint "ThreadPool Metrics";
+
+    # MariaDB
+    unless ( defined $myvar{'have_threadpool'}
+        && $myvar{'have_threadpool'} eq "YES" )
+    {
+        infoprint "ThreadPool stat is disabled.";
+        return;
+    }
+    infoprint "ThreadPool stat is enabled.";
+    infoprint "Thread Pool Size: " . $myvar{'thread_pool_size'} . " thread(s).";
+
+    if (   $myvar{'version'} =~ /percona/i
+        or $myvar{'version_comment'} =~ /percona/i )
+    {
+        my $np = cpu_cores;
+        if (    $myvar{'thread_pool_size'} >= $np
+            and $myvar{'thread_pool_size'} < ( $np * 1.5 ) )
+        {
+            goodprint
+"thread_pool_size for Percona between 1 and 1.5 times number of CPUs ("
+              . $np . " and "
+              . ( $np * 1.5 ) . ")";
+        }
+        else {
+            badprint
+"thread_pool_size for Percona between 1 and 1.5 times number of CPUs ("
+              . $np . " and "
+              . ( $np * 1.5 ) . ")";
+            push( @adjvars,
+                    "thread_pool_size between "
+                  . $np . " and "
+                  . ( $np * 1.5 )
+                  . " for InnoDB usage" );
+        }
+        return;
+    }
+
+    if ( $myvar{'version'} =~ /mariadb/i ) {
+        infoprint "Using default value is good enough for your version ("
+          . $myvar{'version'} . ")";
+        return;
+    }
+
+    if ( $myvar{'have_innodb'} eq 'YES' ) {
+        if (   $myvar{'thread_pool_size'} < 16
+            or $myvar{'thread_pool_size'} > 36 )
+        {
+            badprint
+"thread_pool_size between 16 and 36 when using InnoDB storage engine.";
+            push( @generalrec,
+                    "Thread pool size for InnoDB usage ("
+                  . $myvar{'thread_pool_size'}
+                  . ")" );
+            push( @adjvars,
+                "thread_pool_size between 16 and 36 for InnoDB usage" );
+        }
+        else {
+            goodprint
+"thread_pool_size between 16 and 36 when using InnoDB storage engine.";
+        }
+        return;
+    }
+    if ( $myvar{'have_isam'} eq 'YES' ) {
+        if ( $myvar{'thread_pool_size'} < 4 or $myvar{'thread_pool_size'} > 8 )
+        {
+            badprint
+"thread_pool_size between 4 and 8 when using MyISAM storage engine.";
+            push( @generalrec,
+                    "Thread pool size for MyISAM usage ("
+                  . $myvar{'thread_pool_size'}
+                  . ")" );
+            push( @adjvars,
+                "thread_pool_size between 4 and 8 for MyISAM usage" );
+        }
+        else {
+            goodprint
+"thread_pool_size between 4 and 8 when using MyISAM storage engine.";
+        }
+    }
+}
+
+sub get_pf_memory {
+
+    # Performance Schema
+    return 0 unless defined $myvar{'performance_schema'};
+    return 0 if $myvar{'performance_schema'} eq 'OFF';
+
+    my @infoPFSMemory = grep { /\tperformance_schema[.]memory\t/msx }
+      select_array("SHOW ENGINE PERFORMANCE_SCHEMA STATUS");
+    @infoPFSMemory == 1 || return 0;
+    $infoPFSMemory[0] =~ s/.*\s+(\d+)$/$1/g;
+    return $infoPFSMemory[0];
+}
+
+# Recommendations for Performance Schema
+sub mysql_pfs {
+    subheaderprint "Performance schema";
+
+    # Performance Schema
+    debugprint "Performance schema is " . $myvar{'performance_schema'};
+    $myvar{'performance_schema'} = 'OFF'
+      unless defined( $myvar{'performance_schema'} );
+    if ( $myvar{'performance_schema'} eq 'OFF' ) {
+        badprint "Performance_schema should be activated.";
+        push( @adjvars, "performance_schema=ON" );
+        push( @generalrec,
+            "Performance schema should be activated for better diagnostics" );
+    }
+    if ( $myvar{'performance_schema'} eq 'ON' ) {
+        infoprint "Performance_schema is activated.";
+        debugprint "Performance schema is " . $myvar{'performance_schema'};
+        infoprint "Memory used by Performance_schema: "
+          . hr_bytes( get_pf_memory() );
+    }
+
+    unless ( grep /^sys$/, select_array("SHOW DATABASES") ) {
+        infoprint "Sys schema is not installed.";
+        push( @generalrec,
+            mysql_version_ge( 10, 0 )
+            ? "Consider installing Sys schema from https://github.com/FromDual/mariadb-sys for MariaDB"
+            : "Consider installing Sys schema from https://github.com/mysql/mysql-sys for MySQL"
+        ) unless ( mysql_version_le( 5, 6 ) );
+
+        return;
+    }
+    infoprint "Sys schema is installed.";
+    return if ( $opt{pfstat} == 0 or $myvar{'performance_schema'} ne 'ON' );
+
+    infoprint "Sys schema Version: "
+      . select_one("select sys_version from sys.version");
+
+    # Store all sys schema in dumpdir if defined
+    if ( defined $opt{dumpdir} and -d "$opt{dumpdir}" ) {
+        for my $sys_view ( select_array('use sys;show tables;') ) {
+            infoprint "Dumping $sys_view into $opt{dumpdir}";
+            my $sys_view_table = $sys_view;
+            $sys_view_table =~ s/\$/\\\$/g;
+            select_csv_file( "$opt{dumpdir}/sys_$sys_view.csv",
+                'select * from sys.\`' . $sys_view_table . '\`' );
+        }
+        return;
+
+        #exit 0 if ( $opt{stop} == 1 );
+    }
+
+    # Top user per connection
+    subheaderprint "Performance schema: Top 5 user per connection";
+    my $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, total_connections from sys.user_summary order by total_connections desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery conn(s)";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per statement
+    subheaderprint "Performance schema: Top 5 user per statement";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, statements from sys.user_summary order by statements desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery stmt(s)";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per statement latency
+    subheaderprint "Performance schema: Top 5 user per statement latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, statement_avg_latency from sys.x\\$user_summary order by statement_avg_latency desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per lock latency
+    subheaderprint "Performance schema: Top 5 user per lock latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, lock_latency from sys.x\\$user_summary_by_statement_latency order by lock_latency desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per full scans
+    subheaderprint "Performance schema: Top 5 user per nb full scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, full_scans from sys.x\\$user_summary_by_statement_latency order by full_scans desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per row_sent
+    subheaderprint "Performance schema: Top 5 user per rows sent";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, rows_sent from sys.x\\$user_summary_by_statement_latency order by rows_sent desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per row modified
+    subheaderprint "Performance schema: Top 5 user per rows modified";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, rows_affected from sys.x\\$user_summary_by_statement_latency order by rows_affected desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per io
+    subheaderprint "Performance schema: Top 5 user per IO";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, file_ios from sys.x\\$user_summary order by file_ios desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top user per io latency
+    subheaderprint "Performance schema: Top 5 user per IO latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, file_io_latency from sys.x\\$user_summary order by file_io_latency desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per connection
+    subheaderprint "Performance schema: Top 5 host per connection";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, total_connections from sys.x\\$host_summary order by total_connections desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery conn(s)";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per statement
+    subheaderprint "Performance schema: Top 5 host per statement";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, statements from sys.x\\$host_summary order by statements desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery stmt(s)";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per statement latency
+    subheaderprint "Performance schema: Top 5 host per statement latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, statement_avg_latency from sys.x\\$host_summary order by statement_avg_latency desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per lock latency
+    subheaderprint "Performance schema: Top 5 host per lock latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, lock_latency from sys.x\\$host_summary_by_statement_latency order by lock_latency desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per full scans
+    subheaderprint "Performance schema: Top 5 host per nb full scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, full_scans from sys.x\\$host_summary_by_statement_latency order by full_scans desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per rows sent
+    subheaderprint "Performance schema: Top 5 host per rows sent";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, rows_sent from sys.x\\$host_summary_by_statement_latency order by rows_sent desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per rows modified
+    subheaderprint "Performance schema: Top 5 host per rows modified";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, rows_affected from sys.x\\$host_summary_by_statement_latency order by rows_affected desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per io
+    subheaderprint "Performance schema: Top 5 host per io";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, file_ios from sys.x\\$host_summary order by file_ios desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top 5 host per io latency
+    subheaderprint "Performance schema: Top 5 host per io latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, file_io_latency from sys.x\\$host_summary order by file_io_latency desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top IO type order by total io
+    subheaderprint "Performance schema: Top IO type order by total io";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select substring(event_name,14), SUM(total)AS total from sys.x\\$host_summary_by_file_io_type GROUP BY substring(event_name,14) ORDER BY total DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery i/o";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top IO type order by total latency
+    subheaderprint "Performance schema: Top IO type order by total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select substring(event_name,14), ROUND(SUM(total_latency),1) AS total_latency from sys.x\\$host_summary_by_file_io_type GROUP BY substring(event_name,14) ORDER BY total_latency DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top IO type order by max latency
+    subheaderprint "Performance schema: Top IO type order by max latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select substring(event_name,14), MAX(max_latency) as max_latency from sys.x\\$host_summary_by_file_io_type GROUP BY substring(event_name,14) ORDER BY max_latency DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top Stages order by total io
+    subheaderprint "Performance schema: Top Stages order by total io";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select substring(event_name,7), SUM(total)AS total from sys.x\\$host_summary_by_stages GROUP BY substring(event_name,7) ORDER BY total DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery i/o";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top Stages order by total latency
+    subheaderprint "Performance schema: Top Stages order by total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select substring(event_name,7), ROUND(SUM(total_latency),1) AS total_latency from sys.x\\$host_summary_by_stages GROUP BY substring(event_name,7) ORDER BY total_latency DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top Stages order by avg latency
+    subheaderprint "Performance schema: Top Stages order by avg latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select substring(event_name,7), MAX(avg_latency) as avg_latency from sys.x\\$host_summary_by_stages GROUP BY substring(event_name,7) ORDER BY avg_latency DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top host per table scans
+    subheaderprint "Performance schema: Top 5 host per table scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select host, table_scans from sys.x\\$host_summary order by table_scans desc LIMIT 5'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # InnoDB Buffer Pool by schema
+    subheaderprint "Performance schema: InnoDB Buffer Pool by schema";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select object_schema, allocated, data, pages from sys.x\\$innodb_buffer_stats_by_schema ORDER BY pages DESC'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery page(s)";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # InnoDB Buffer Pool by table
+    subheaderprint "Performance schema: 40 InnoDB Buffer Pool by table";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select object_schema,  object_name, allocated,data, pages from sys.x\\$innodb_buffer_stats_by_table ORDER BY pages DESC LIMIT 40'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery page(s)";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Process per allocated memory
+    subheaderprint "Performance schema: Process per time";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, Command AS PROC, time from sys.x\\$processlist ORDER BY time DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # InnoDB Lock Waits
+    subheaderprint "Performance schema: InnoDB Lock Waits";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select wait_age_secs, locked_table, locked_type, waiting_query from sys.x\\$innodb_lock_waits order by wait_age_secs DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Threads IO Latency
+    subheaderprint "Performance schema: Thread IO Latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select user, total_latency, max_latency from sys.x\\$io_by_thread_by_latency order by total_latency DESC;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # High Cost SQL statements
+    subheaderprint "Performance schema: Top 15 Most latency statements";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select LEFT(query, 120), avg_latency from sys.x\\$statement_analysis order by avg_latency desc LIMIT 15'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top 5% slower queries
+    subheaderprint "Performance schema: Top 15 slower queries";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select LEFT(query, 120), exec_count from sys.x\\$statements_with_runtimes_in_95th_percentile order by exec_count desc LIMIT 15'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery s";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top 10 nb statement type
+    subheaderprint "Performance schema: Top 15 nb statement type";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select statement, sum(total) as total from sys.x\\$host_summary_by_statement_type group by statement order by total desc LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top statement by total latency
+    subheaderprint "Performance schema: Top 15 statement by total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select statement, sum(total_latency) as total from sys.x\\$host_summary_by_statement_type group by statement order by total desc LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top statement by lock latency
+    subheaderprint "Performance schema: Top 15 statement by lock latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select statement, sum(lock_latency) as total from sys.x\\$host_summary_by_statement_type group by statement order by total desc LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top statement by full scans
+    subheaderprint "Performance schema: Top 15 statement by full scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select statement, sum(full_scans) as total from sys.x\\$host_summary_by_statement_type group by statement order by total desc LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top statement by rows sent
+    subheaderprint "Performance schema: Top 15 statement by rows sent";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select statement, sum(rows_sent) as total from sys.x\\$host_summary_by_statement_type group by statement order by total desc LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Top statement by rows modified
+    subheaderprint "Performance schema: Top 15 statement by rows modified";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select statement, sum(rows_affected) as total from sys.x\\$host_summary_by_statement_type group by statement order by total desc LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Use temporary tables
+    subheaderprint "Performance schema: 15 sample queries using temp table";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select left(query, 120) from sys.x\\$statements_with_temp_tables LIMIT 15'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Unused Indexes
+    subheaderprint "Performance schema: Unused indexes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+"select \* from sys.schema_unused_indexes where object_schema not in ('performance_schema')"
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Full table scans
+    subheaderprint "Performance schema: Tables with full table scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select * from sys.x\\$schema_tables_with_full_table_scans order by rows_full_scanned DESC'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Latest file IO by latency
+    subheaderprint "Performance schema: Latest File IO by latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select thread, file, latency, operation from sys.x\\$latest_file_io ORDER BY latency LIMIT 10;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # FILE by IO read bytes
+    subheaderprint "Performance schema: File by IO read bytes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select file, total_read from sys.x\\$io_global_by_file_by_bytes order by total_read DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # FILE by IO written bytes
+    subheaderprint "Performance schema: File by IO written bytes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select file, total_written from sys.x\\$io_global_by_file_by_bytes order by total_written DESC LIMIT 15'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # file per IO total latency
+    subheaderprint "Performance schema: File per IO total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select file, total_latency from sys.x\\$io_global_by_file_by_latency ORDER BY total_latency DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # file per IO read latency
+    subheaderprint "Performance schema: file per IO read latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select file, read_latency from sys.x\\$io_global_by_file_by_latency ORDER BY read_latency DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # file per IO write latency
+    subheaderprint "Performance schema: file per IO write latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select file, write_latency from sys.x\\$io_global_by_file_by_latency ORDER BY write_latency DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Event Wait by read bytes
+    subheaderprint "Performance schema: Event Wait by read bytes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select event_name, total_read from sys.x\\$io_global_by_wait_by_bytes order by total_read DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Event Wait by write bytes
+    subheaderprint "Performance schema: Event Wait written bytes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select event_name, total_written from sys.x\\$io_global_by_wait_by_bytes order by total_written DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # event per wait total latency
+    subheaderprint "Performance schema: event per wait total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select event_name, total_latency from sys.x\\$io_global_by_wait_by_latency ORDER BY total_latency DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # event per wait read latency
+    subheaderprint "Performance schema: event per wait read latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select event_name, read_latency from sys.x\\$io_global_by_wait_by_latency ORDER BY read_latency DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # event per wait write latency
+    subheaderprint "Performance schema: event per wait write latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select event_name, write_latency from sys.x\\$io_global_by_wait_by_latency ORDER BY write_latency DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    #schema_index_statistics
+    # TOP 15 most read index
+    subheaderprint "Performance schema: Top 15 most read indexes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name,index_name, rows_selected from sys.x\\$schema_index_statistics ORDER BY ROWs_selected DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 most used index
+    subheaderprint "Performance schema: Top 15 most modified indexes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name,index_name, rows_inserted+rows_updated+rows_deleted AS changes from sys.x\\$schema_index_statistics ORDER BY rows_inserted+rows_updated+rows_deleted DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high read latency index
+    subheaderprint "Performance schema: Top 15 high read latency index";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name,index_name, select_latency from sys.x\\$schema_index_statistics ORDER BY select_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high insert latency index
+    subheaderprint "Performance schema: Top 15 most modified indexes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name,index_name, insert_latency from sys.x\\$schema_index_statistics ORDER BY insert_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high update latency index
+    subheaderprint "Performance schema: Top 15 high update latency index";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name,index_name, update_latency from sys.x\\$schema_index_statistics ORDER BY update_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high delete latency index
+    subheaderprint "Performance schema: Top 15 high delete latency index";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name,index_name, delete_latency from sys.x\\$schema_index_statistics ORDER BY delete_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 most read tables
+    subheaderprint "Performance schema: Top 15 most read tables";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name, rows_fetched from sys.x\\$schema_table_statistics ORDER BY ROWs_fetched DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 most used tables
+    subheaderprint "Performance schema: Top 15 most modified tables";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name, rows_inserted+rows_updated+rows_deleted AS changes from sys.x\\$schema_table_statistics ORDER BY rows_inserted+rows_updated+rows_deleted DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high read latency tables
+    subheaderprint "Performance schema: Top 15 high read latency tables";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name, fetch_latency from sys.x\\$schema_table_statistics ORDER BY fetch_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high insert latency tables
+    subheaderprint "Performance schema: Top 15 high insert latency tables";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name, insert_latency from sys.x\\$schema_table_statistics ORDER BY insert_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high update latency tables
+    subheaderprint "Performance schema: Top 15 high update latency tables";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name, update_latency from sys.x\\$schema_table_statistics ORDER BY update_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # TOP 15 high delete latency tables
+    subheaderprint "Performance schema: Top 15 high delete latency tables";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select table_schema, table_name, delete_latency from sys.x\\$schema_table_statistics ORDER BY delete_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    # Redundant indexes
+    subheaderprint "Performance schema: Redundant indexes";
+    $nbL = 1;
+    for my $lQuery (
+        select_array('use sys;select * from schema_redundant_indexes;') )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Table not using InnoDB buffer";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+' Select table_schema, table_name from sys.x\\$schema_table_statistics_with_buffer where innodb_buffer_allocated IS NULL;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 Tables using InnoDB buffer";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select table_schema,table_name,innodb_buffer_allocated from sys.x\\$schema_table_statistics_with_buffer where innodb_buffer_allocated IS NOT NULL ORDER BY innodb_buffer_allocated DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 Tables with InnoDB buffer free";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select table_schema,table_name,innodb_buffer_free from sys.x\\$schema_table_statistics_with_buffer where innodb_buffer_allocated IS NOT NULL ORDER BY innodb_buffer_free DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 Most executed queries";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), exec_count from sys.x\\$statement_analysis order by exec_count DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Latest SQL queries in errors or warnings";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select LEFT(query, 120), last_seen from sys.x\\$statements_with_errors_or_warnings ORDER BY last_seen LIMIT 40;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 20 queries with full table scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), exec_count from sys.x\\$statements_with_full_table_scans order BY exec_count DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Last 50 queries with full table scans";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), last_seen from sys.x\\$statements_with_full_table_scans order BY last_seen DESC LIMIT 50;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 reader queries (95% percentile)";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), rows_sent from sys.x\\$statements_with_runtimes_in_95th_percentile ORDER BY ROWs_sent DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Top 15 most row look queries (95% percentile)";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), rows_examined AS search from sys.x\\$statements_with_runtimes_in_95th_percentile ORDER BY rows_examined DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Top 15 total latency queries (95% percentile)";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), total_latency AS search from sys.x\\$statements_with_runtimes_in_95th_percentile ORDER BY total_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Top 15 max latency queries (95% percentile)";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), max_latency AS search from sys.x\\$statements_with_runtimes_in_95th_percentile ORDER BY max_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Top 15 average latency queries (95% percentile)";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), avg_latency AS search from sys.x\\$statements_with_runtimes_in_95th_percentile ORDER BY avg_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 20 queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), exec_count from sys.x\\$statements_with_sorting order BY exec_count DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Last 50 queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), last_seen from sys.x\\$statements_with_sorting order BY last_seen DESC LIMIT 50;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 row sorting queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), rows_sorted from sys.x\\$statements_with_sorting ORDER BY ROWs_sorted DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 total latency queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), total_latency AS search from sys.x\\$statements_with_sorting ORDER BY total_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 merge queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), sort_merge_passes AS search from sys.x\\$statements_with_sorting ORDER BY sort_merge_passes DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Top 15 average sort merges queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), avg_sort_merges AS search from sys.x\\$statements_with_sorting ORDER BY avg_sort_merges DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 scans queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), sorts_using_scans AS search from sys.x\\$statements_with_sorting ORDER BY sorts_using_scans DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 range queries with sort";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), sort_using_range AS search from sys.x\\$statements_with_sorting ORDER BY sort_using_range DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+##################################################################################
+
+    #statements_with_temp_tables
+
+#mysql> desc statements_with_temp_tables;
+#+--------------------------+---------------------+------+-----+---------------------+-------+
+#| Field                    | Type                | Null | Key | Default             | Extra |
+#+--------------------------+---------------------+------+-----+---------------------+-------+
+#| query                    | longtext            | YES  |     | NULL                |       |
+#| db                       | varchar(64)         | YES  |     | NULL                |       |
+#| exec_count               | bigint(20) unsigned | NO   |     | NULL                |       |
+#| total_latency            | text                | YES  |     | NULL                |       |
+#| memory_tmp_tables        | bigint(20) unsigned | NO   |     | NULL                |       |
+#| disk_tmp_tables          | bigint(20) unsigned | NO   |     | NULL                |       |
+#| avg_tmp_tables_per_query | decimal(21,0)       | NO   |     | 0                   |       |
+#| tmp_tables_to_disk_pct   | decimal(24,0)       | NO   |     | 0                   |       |
+#| first_seen               | timestamp           | NO   |     | 0000-00-00 00:00:00 |       |
+#| last_seen                | timestamp           | NO   |     | 0000-00-00 00:00:00 |       |
+#| digest                   | varchar(32)         | YES  |     | NULL                |       |
+#+--------------------------+---------------------+------+-----+---------------------+-------+
+#11 rows in set (0,01 sec)#
+#
+    subheaderprint "Performance schema: Top 20 queries with temp table";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), exec_count from sys.x\\$statements_with_temp_tables order BY exec_count DESC LIMIT 20;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Last 50 queries with temp table";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), last_seen from sys.x\\$statements_with_temp_tables order BY last_seen DESC LIMIT 50;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint
+      "Performance schema: Top 15 total latency queries with temp table";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select db, LEFT(query, 120), total_latency AS search from sys.x\\$statements_with_temp_tables ORDER BY total_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 queries with temp table to disk";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select db, LEFT(query, 120), disk_tmp_tables from sys.x\\$statements_with_temp_tables ORDER BY disk_tmp_tables DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+##################################################################################
+    #wait_classes_global_by_latency
+
+#mysql> select * from wait_classes_global_by_latency;
+#-----------------+-------+---------------+-------------+-------------+-------------+
+# event_class     | total | total_latency | min_latency | avg_latency | max_latency |
+#-----------------+-------+---------------+-------------+-------------+-------------+
+# wait/io/file    | 15381 | 1.23 s        | 0 ps        | 80.12 us    | 230.64 ms   |
+# wait/io/table   |    59 | 7.57 ms       | 5.45 us     | 128.24 us   | 3.95 ms     |
+# wait/lock/table |    69 | 3.22 ms       | 658.84 ns   | 46.64 us    | 1.10 ms     |
+#-----------------+-------+---------------+-------------+-------------+-------------+
+# rows in set (0,00 sec)
+
+    subheaderprint "Performance schema: Top 15 class events by number";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select event_class, total from sys.x\\$wait_classes_global_by_latency ORDER BY total DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 30 events by number";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select events, total from sys.x\\$waits_global_by_latency ORDER BY total DESC LIMIT 30;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 class events by total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select event_class, total_latency from sys.x\\$wait_classes_global_by_latency ORDER BY total_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 30 events by total latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'use sys;select events, total_latency from sys.x\\$waits_global_by_latency ORDER BY total_latency DESC LIMIT 30;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 15 class events by max latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select event_class, max_latency from sys.x\\$wait_classes_global_by_latency ORDER BY max_latency DESC LIMIT 15;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+    subheaderprint "Performance schema: Top 30 events by max latency";
+    $nbL = 1;
+    for my $lQuery (
+        select_array(
+'select events, max_latency from sys.x\\$waits_global_by_latency ORDER BY max_latency DESC LIMIT 30;'
+        )
+      )
+    {
+        infoprint " +-- $nbL: $lQuery";
+        $nbL++;
+    }
+    infoprint "No information found or indicators deactivated."
+      if ( $nbL == 1 );
+
+}
+
+# Recommendations for Aria Engine
+sub mariadb_aria {
+    subheaderprint "Aria Metrics";
+
+    # Aria
+    if ( !defined $myvar{'have_aria'} ) {
+        infoprint "Aria Storage Engine not available.";
+        return;
+    }
+    if ( $myvar{'have_aria'} ne "YES" ) {
+        infoprint "Aria Storage Engine is disabled.";
+        return;
+    }
+    infoprint "Aria Storage Engine is enabled.";
+
+    # Aria pagecache
+    if ( !defined( $mycalc{'total_aria_indexes'} ) ) {
+        push( @generalrec,
+            "Unable to calculate Aria index size on MySQL server" );
+    }
+    else {
+        if (
+            $myvar{'aria_pagecache_buffer_size'} < $mycalc{'total_aria_indexes'}
+            && $mycalc{'pct_aria_keys_from_mem'} < 95 )
+        {
+            badprint "Aria pagecache size / total Aria indexes: "
+              . hr_bytes( $myvar{'aria_pagecache_buffer_size'} ) . "/"
+              . hr_bytes( $mycalc{'total_aria_indexes'} ) . "";
+            push( @adjvars,
+                    "aria_pagecache_buffer_size (> "
+                  . hr_bytes( $mycalc{'total_aria_indexes'} )
+                  . ")" );
+        }
+        else {
+            goodprint "Aria pagecache size / total Aria indexes: "
+              . hr_bytes( $myvar{'aria_pagecache_buffer_size'} ) . "/"
+              . hr_bytes( $mycalc{'total_aria_indexes'} ) . "";
+        }
+        if ( $mystat{'Aria_pagecache_read_requests'} > 0 ) {
+            if ( $mycalc{'pct_aria_keys_from_mem'} < 95 ) {
+                badprint
+"Aria pagecache hit rate: $mycalc{'pct_aria_keys_from_mem'}% ("
+                  . hr_num( $mystat{'Aria_pagecache_read_requests'} )
+                  . " cached / "
+                  . hr_num( $mystat{'Aria_pagecache_reads'} )
+                  . " reads)";
+            }
+            else {
+                goodprint
+"Aria pagecache hit rate: $mycalc{'pct_aria_keys_from_mem'}% ("
+                  . hr_num( $mystat{'Aria_pagecache_read_requests'} )
+                  . " cached / "
+                  . hr_num( $mystat{'Aria_pagecache_reads'} )
+                  . " reads)";
+            }
+        }
+        else {
+
+            # No queries have run that would use keys
+        }
+    }
+}
+
+# Recommendations for TokuDB
+sub mariadb_tokudb {
+    subheaderprint "TokuDB Metrics";
+
+    # AriaDB
+    unless ( defined $myvar{'have_tokudb'}
+        && $myvar{'have_tokudb'} eq "YES" )
+    {
+        infoprint "TokuDB is disabled.";
+        return;
+    }
+    infoprint "TokuDB is enabled.";
+
+    # Not implemented
+}
+
+# Recommendations for XtraDB
+sub mariadb_xtradb {
+    subheaderprint "XtraDB Metrics";
+
+    # XtraDB
+    unless ( defined $myvar{'have_xtradb'}
+        && $myvar{'have_xtradb'} eq "YES" )
+    {
+        infoprint "XtraDB is disabled.";
+        return;
+    }
+    infoprint "XtraDB is enabled.";
+    infoprint "Note that MariaDB 10.2 makes use of InnoDB, not XtraDB."
+
+      # Not implemented
+}
+
+# Recommendations for RocksDB
+sub mariadb_rockdb {
+    subheaderprint "RocksDB Metrics";
+
+    # RocksDB
+    unless ( defined $myvar{'have_rocksdb'}
+        && $myvar{'have_rocksdb'} eq "YES" )
+    {
+        infoprint "RocksDB is disabled.";
+        return;
+    }
+    infoprint "RocksDB is enabled.";
+
+    # Not implemented
+}
+
+# Recommendations for Spider
+sub mariadb_spider {
+    subheaderprint "Spider Metrics";
+
+    # Spider
+    unless ( defined $myvar{'have_spider'}
+        && $myvar{'have_spider'} eq "YES" )
+    {
+        infoprint "Spider is disabled.";
+        return;
+    }
+    infoprint "Spider is enabled.";
+
+    # Not implemented
+}
+
+# Recommendations for Connect
+sub mariadb_connect {
+    subheaderprint "Connect Metrics";
+
+    # Connect
+    unless ( defined $myvar{'have_connect'}
+        && $myvar{'have_connect'} eq "YES" )
+    {
+        infoprint "Connect is disabled.";
+        return;
+    }
+    infoprint "Connect is enabled.";
+
+    # Not implemented
+}
+
+# Perl trim function to remove whitespace from the start and end of the string
+sub trim {
+    my $string = shift;
+    return "" unless defined($string);
+    $string =~ s/^\s+//;
+    $string =~ s/\s+$//;
+    return $string;
+}
+
+sub get_wsrep_options {
+    return () unless defined $myvar{'wsrep_provider_options'};
+
+    my @galera_options      = split /;/, $myvar{'wsrep_provider_options'};
+    my $wsrep_slave_threads = $myvar{'wsrep_slave_threads'};
+    push @galera_options, ' wsrep_slave_threads = ' . $wsrep_slave_threads;
+    @galera_options = remove_cr @galera_options;
+    @galera_options = remove_empty @galera_options;
+
+    #debugprint Dumper( \@galera_options ) if $opt{debug};
+    return @galera_options;
+}
+
+sub get_gcache_memory {
+    my $gCacheMem = hr_raw( get_wsrep_option('gcache.size') );
+
+    return 0 unless defined $gCacheMem and $gCacheMem ne '';
+    return $gCacheMem;
+}
+
+sub get_wsrep_option {
+    my $key = shift;
+    return '' unless defined $myvar{'wsrep_provider_options'};
+    my @galera_options = get_wsrep_options;
+    return '' unless scalar(@galera_options) > 0;
+    my @memValues = grep /\s*$key =/, @galera_options;
+    my $memValue  = $memValues[0];
+    return 0 unless defined $memValue;
+    $memValue =~ s/.*=\s*(.+)$/$1/g;
+    return $memValue;
+}
+
+# REcommendations for Tables
+sub mysql_table_structures {
+    return 0 unless ( $opt{structstat} > 0 );
+    subheaderprint "Table structures analysis";
+
+    my @primaryKeysNbTables = select_array(
+        "Select CONCAT(c.table_schema, ',' , c.table_name)
+from information_schema.columns c
+join information_schema.tables t using (TABLE_SCHEMA, TABLE_NAME)
+where c.table_schema not in ('sys', 'mysql', 'information_schema', 'performance_schema')
+  and t.table_type = 'BASE TABLE'
+group by c.table_schema,c.table_name
+having sum(if(c.column_key in ('PRI', 'UNI'), 1, 0)) = 0"
+    );
+
+    my $tmpContent = 'Schema,Table';
+    if ( scalar(@primaryKeysNbTables) > 0 ) {
+        badprint "Following table(s) don't have primary key:";
+        foreach my $badtable (@primaryKeysNbTables) {
+            badprint "\t$badtable";
+            push @{ $result{'Tables without PK'} }, $badtable;
+            $tmpContent .= "\n$badtable";
+        }
+        push @generalrec,
+"Ensure that all table(s) get an explicit primary keys for performance, maintenance and also for replication";
+
+    }
+    else {
+        goodprint "All tables get a primary key";
+    }
+    dump_into_file( "tables_without_primary_keys.csv", $tmpContent );
+
+    my @nonInnoDBTables = select_array(
+        "select CONCAT(table_schema, ',', table_name, ',', ENGINE) 
+FROM information_schema.tables t
+WHERE ENGINE <> 'InnoDB' 
+and t.table_type = 'BASE TABLE'
+and table_schema not in 
+('sys', 'mysql', 'performance_schema', 'information_schema')"
+    );
+    $tmpContent = 'Schema,Table,Engine';
+    if ( scalar(@nonInnoDBTables) > 0 ) {
+        badprint "Following table(s) are not InnoDB table:";
+        push @generalrec,
+"Ensure that all table(s) are InnoDB tables for performance and also for replication";
+        foreach my $badtable (@nonInnoDBTables) {
+            if ( $badtable =~ /Memory/i ) {
+                badprint
+"Table $badtable is a MEMORY table. It's suggested to use only InnoDB tables in production";
+            }
+            else {
+                badprint "\t$badtable";
+            }
+            $tmpContent .= "\n$badtable";
+        }
+    }
+    else {
+        goodprint "All tables are InnoDB tables";
+    }
+    dump_into_file( "tables_non_innodb.csv", $tmpContent );
+
+    my @nonutf8columns = select_array(
+"SELECT CONCAT(table_schema, ',', table_name, ',', column_name, ',', CHARacter_set_name, ',', COLLATION_name, ',', data_type, ',', CHARACTER_MAXIMUM_LENGTH)
+from information_schema.columns
+WHERE table_schema not in ('sys', 'mysql', 'performance_schema', 'information_schema')
+and (CHARacter_set_name  NOT LIKE 'utf8%'
+or COLLATION_name NOT LIKE 'utf8%');"
+    );
+    $tmpContent =
+      'Schema,Table,Column, Charset, Collation, Data Type, Max Length';
+    if ( scalar(@nonutf8columns) > 0 ) {
+        badprint "Following character columns(s) are not utf8 compliant:";
+        push @generalrec,
+"Ensure that all text colums(s) are UTF-8 compliant for encoding support and performance";
+        foreach my $badtable (@nonutf8columns) {
+            badprint "\t$badtable";
+            $tmpContent .= "\n$badtable";
+        }
+    }
+    else {
+        goodprint "All columns are UTF-8 compliant";
+    }
+    dump_into_file( "columns_non_utf8.csv", $tmpContent );
+
+    my @utf8columns = select_array(
+"SELECT CONCAT(table_schema, ',', table_name, ',', column_name, ',', CHARacter_set_name, ',', COLLATION_name, ',', data_type, ',', CHARACTER_MAXIMUM_LENGTH)
+from information_schema.columns
+WHERE table_schema not in ('sys', 'mysql', 'performance_schema', 'information_schema')
+and (CHARacter_set_name  LIKE 'utf8%'
+or COLLATION_name LIKE 'utf8%');"
+    );
+    $tmpContent =
+      'Schema,Table,Column, Charset, Collation, Data Type, Max Length';
+    foreach my $badtable (@utf8columns) {
+        $tmpContent .= "\n$badtable";
+    }
+    dump_into_file( "columns_utf8.csv", $tmpContent );
+
+    my @ftcolumns = select_array(
+"SELECT CONCAT(table_schema, ',', table_name, ',', column_name, ',', data_type)
+from information_schema.columns
+WHERE table_schema not in ('sys', 'mysql', 'performance_schema', 'information_schema')
+AND data_type='FULLTEXT';"
+    );
+    $tmpContent = 'Schema,Table,Column, Data Type';
+    foreach my $ctable (@ftcolumns) {
+        $tmpContent .= "\n$ctable";
+    }
+    dump_into_file( "fulltext_columns.csv", $tmpContent );
+
+}
+
+# Recommendations for Galera
+sub mariadb_galera {
+    subheaderprint "Galera Metrics";
+
+    # Galera Cluster
+    unless ( defined $myvar{'have_galera'}
+        && $myvar{'have_galera'} eq "YES" )
+    {
+        infoprint "Galera is disabled.";
+        return;
+    }
+    infoprint "Galera is enabled.";
+    debugprint "Galera variables:";
+    foreach my $gvar ( keys %myvar ) {
+        next unless $gvar =~ /^wsrep.*/;
+        next if $gvar eq 'wsrep_provider_options';
+        debugprint "\t" . trim($gvar) . " = " . $myvar{$gvar};
+        $result{'Galera'}{'variables'}{$gvar} = $myvar{$gvar};
+    }
+    if ( not defined( $myvar{'wsrep_on'} ) or $myvar{'wsrep_on'} ne "ON" ) {
+        infoprint "Galera is disabled.";
+        return;
+    }
+    debugprint "Galera wsrep provider Options:";
+    my @galera_options = get_wsrep_options;
+    $result{'Galera'}{'wsrep options'} = get_wsrep_options();
+    foreach my $gparam (@galera_options) {
+        debugprint "\t" . trim($gparam);
+    }
+    debugprint "Galera status:";
+    foreach my $gstatus ( keys %mystat ) {
+        next unless $gstatus =~ /^wsrep.*/;
+        debugprint "\t" . trim($gstatus) . " = " . $mystat{$gstatus};
+        $result{'Galera'}{'status'}{$gstatus} = $myvar{$gstatus};
+    }
+    infoprint "GCache is using "
+      . hr_bytes_rnd( get_wsrep_option('gcache.mem_size') );
+
+    infoprint "CPU cores detected : " . (cpu_cores);
+    infoprint "wsrep_slave_threads: " . get_wsrep_option('wsrep_slave_threads');
+
+    if (   get_wsrep_option('wsrep_slave_threads') > ( (cpu_cores) * 4 )
+        or get_wsrep_option('wsrep_slave_threads') < ( (cpu_cores) * 2 ) )
+    {
+        badprint
+"wsrep_slave_threads is not equal to 2, 3 or 4 times the number of CPU(s)";
+        push @adjvars, "wsrep_slave_threads = " . ( (cpu_cores) * 4 );
+    }
+    else {
+        goodprint
+"wsrep_slave_threads is equal to 2, 3 or 4 times the number of CPU(s)";
+    }
+
+    if ( get_wsrep_option('wsrep_slave_threads') > 1 ) {
+        infoprint
+          "wsrep parallel slave can cause frequent inconsistency crash.";
+        push @adjvars,
+"Set wsrep_slave_threads to 1 in case of HA_ERR_FOUND_DUPP_KEY crash on slave";
+
+        # check options for parallel slave
+        if ( get_wsrep_option('wsrep_slave_FK_checks') eq "OFF" ) {
+            badprint "wsrep_slave_FK_checks is off with parallel slave";
+            push @adjvars,
+              "wsrep_slave_FK_checks should be ON when using parallel slave";
+        }
+
+        # wsrep_slave_UK_checks seems useless in MySQL source code
+        if ( $myvar{'innodb_autoinc_lock_mode'} != 2 ) {
+            badprint
+              "innodb_autoinc_lock_mode is incorrect with parallel slave";
+            push @adjvars,
+              "innodb_autoinc_lock_mode should be 2 when using parallel slave";
+        }
+    }
+
+    if ( get_wsrep_option('gcs.fc_limit') != $myvar{'wsrep_slave_threads'} * 5 )
+    {
+        badprint "gcs.fc_limit should be equal to 5 * wsrep_slave_threads (="
+          . ( $myvar{'wsrep_slave_threads'} * 5 ) . ")";
+        push @adjvars, "gcs.fc_limit= wsrep_slave_threads * 5 (="
+          . ( $myvar{'wsrep_slave_threads'} * 5 ) . ")";
+    }
+    else {
+        goodprint "gcs.fc_limit is equal to 5 * wsrep_slave_threads ( ="
+          . get_wsrep_option('gcs.fc_limit') . ")";
+    }
+
+    if ( get_wsrep_option('gcs.fc_factor') != 0.8 ) {
+        badprint "gcs.fc_factor should be equal to 0.8 (="
+          . get_wsrep_option('gcs.fc_factor') . ")";
+        push @adjvars, "gcs.fc_factor=0.8";
+    }
+    else {
+        goodprint "gcs.fc_factor is equal to 0.8";
+    }
+    if ( get_wsrep_option('wsrep_flow_control_paused') > 0.02 ) {
+        badprint "Fraction of time node pause flow control > 0.02";
+    }
+    else {
+        goodprint
+"Flow control fraction seems to be OK (wsrep_flow_control_paused <= 0.02)";
+    }
+
+    if ( $myvar{'binlog_format'} ne 'ROW' ) {
+        badprint "Binlog format should be in ROW mode.";
+        push @adjvars, "binlog_format = ROW";
+    }
+    else {
+        goodprint "Binlog format is in ROW mode.";
+    }
+    if ( $myvar{'innodb_flush_log_at_trx_commit'} != 0 ) {
+        badprint "InnoDB flush log at each commit should be disabled.";
+        push @adjvars, "innodb_flush_log_at_trx_commit = 0";
+    }
+    else {
+        goodprint "InnoDB flush log at each commit is disabled for Galera.";
+    }
+
+    infoprint "Read consistency mode :" . $myvar{'wsrep_causal_reads'};
+
+    if ( defined( $myvar{'wsrep_cluster_name'} )
+        and $myvar{'wsrep_on'} eq "ON" )
+    {
+        goodprint "Galera WsREP is enabled.";
+        if ( defined( $myvar{'wsrep_cluster_address'} )
+            and trim("$myvar{'wsrep_cluster_address'}") ne "" )
+        {
+            goodprint "Galera Cluster address is defined: "
+              . $myvar{'wsrep_cluster_address'};
+            my @NodesTmp = split /,/, $myvar{'wsrep_cluster_address'};
+            my $nbNodes  = @NodesTmp;
+            infoprint "There are $nbNodes nodes in wsrep_cluster_address";
+            my $nbNodesSize = trim( $mystat{'wsrep_cluster_size'} );
+            if ( $nbNodesSize == 3 or $nbNodesSize == 5 ) {
+                goodprint "There are $nbNodesSize nodes in wsrep_cluster_size.";
+            }
+            else {
+                badprint
+"There are $nbNodesSize nodes in wsrep_cluster_size. Prefer 3 or 5 nodes architecture.";
+                push @generalrec, "Prefer 3 or 5 nodes architecture.";
+            }
+
+            # wsrep_cluster_address doesn't include garbd nodes
+            if ( $nbNodes > $nbNodesSize ) {
+                badprint
+"All cluster nodes are not detected. wsrep_cluster_size less than node count in wsrep_cluster_address";
+            }
+            else {
+                goodprint "All cluster nodes detected.";
+            }
+        }
+        else {
+            badprint "Galera Cluster address is undefined";
+            push @adjvars,
+              "set up wsrep_cluster_address variable for Galera replication";
+        }
+        if ( defined( $myvar{'wsrep_cluster_name'} )
+            and trim( $myvar{'wsrep_cluster_name'} ) ne "" )
+        {
+            goodprint "Galera Cluster name is defined: "
+              . $myvar{'wsrep_cluster_name'};
+        }
+        else {
+            badprint "Galera Cluster name is undefined";
+            push @adjvars,
+              "set up wsrep_cluster_name variable for Galera replication";
+        }
+        if ( defined( $myvar{'wsrep_node_name'} )
+            and trim( $myvar{'wsrep_node_name'} ) ne "" )
+        {
+            goodprint "Galera Node name is defined: "
+              . $myvar{'wsrep_node_name'};
+        }
+        else {
+            badprint "Galera node name is undefined";
+            push @adjvars,
+              "set up wsrep_node_name variable for Galera replication";
+        }
+        if ( trim( $myvar{'wsrep_notify_cmd'} ) ne "" ) {
+            goodprint "Galera Notify command is defined.";
+        }
+        else {
+            badprint "Galera Notify command is not defined.";
+            push( @adjvars,
+                "set up parameter wsrep_notify_cmd to be notified" );
+        }
+        if (    trim( $myvar{'wsrep_sst_method'} ) !~ "^xtrabackup.*"
+            and trim( $myvar{'wsrep_sst_method'} ) !~ "^mariabackup" )
+        {
+            badprint "Galera SST method is not xtrabackup based.";
+            push( @adjvars,
+"set up parameter wsrep_sst_method to xtrabackup based parameter"
+            );
+        }
+        else {
+            goodprint "SST Method is based on xtrabackup.";
+        }
+        if (
+            (
+                defined( $myvar{'wsrep_OSU_method'} )
+                && trim( $myvar{'wsrep_OSU_method'} ) eq "TOI"
+            )
+            || ( defined( $myvar{'wsrep_osu_method'} )
+                && trim( $myvar{'wsrep_osu_method'} ) eq "TOI" )
+          )
+        {
+            goodprint "TOI is default mode for upgrade.";
+        }
+        else {
+            badprint "Schema upgrade are not replicated automatically";
+            push( @adjvars, "set up parameter wsrep_OSU_method to TOI" );
+        }
+        infoprint "Max WsRep message : "
+          . hr_bytes( $myvar{'wsrep_max_ws_size'} );
+    }
+    else {
+        badprint "Galera WsREP is disabled";
+    }
+
+    if ( defined( $mystat{'wsrep_connected'} )
+        and $mystat{'wsrep_connected'} eq "ON" )
+    {
+        goodprint "Node is connected";
+    }
+    else {
+        badprint "Node is disconnected";
+    }
+    if ( defined( $mystat{'wsrep_ready'} ) and $mystat{'wsrep_ready'} eq "ON" )
+    {
+        goodprint "Node is ready";
+    }
+    else {
+        badprint "Node is not ready";
+    }
+    infoprint "Cluster status :" . $mystat{'wsrep_cluster_status'};
+    if ( defined( $mystat{'wsrep_cluster_status'} )
+        and $mystat{'wsrep_cluster_status'} eq "Primary" )
+    {
+        goodprint "Galera cluster is consistent and ready for operations";
+    }
+    else {
+        badprint "Cluster is not consistent and ready";
+    }
+    if ( $mystat{'wsrep_local_state_uuid'} eq
+        $mystat{'wsrep_cluster_state_uuid'} )
+    {
+        goodprint "Node and whole cluster at the same level: "
+          . $mystat{'wsrep_cluster_state_uuid'};
+    }
+    else {
+        badprint "Node and whole cluster not the same level";
+        infoprint "Node    state uuid: " . $mystat{'wsrep_local_state_uuid'};
+        infoprint "Cluster state uuid: " . $mystat{'wsrep_cluster_state_uuid'};
+    }
+    if ( $mystat{'wsrep_local_state_comment'} eq 'Synced' ) {
+        goodprint "Node is synced with whole cluster.";
+    }
+    else {
+        badprint "Node is not synced";
+        infoprint "Node State : " . $mystat{'wsrep_local_state_comment'};
+    }
+    if ( $mystat{'wsrep_local_cert_failures'} == 0 ) {
+        goodprint "There is no certification failures detected.";
+    }
+    else {
+        badprint "There is "
+          . $mystat{'wsrep_local_cert_failures'}
+          . " certification failure(s)detected.";
+    }
+
+    for my $key ( keys %mystat ) {
+        if ( $key =~ /wsrep_|galera/i ) {
+            debugprint "WSREP: $key = $mystat{$key}";
+        }
+    }
+
+    #debugprint Dumper get_wsrep_options() if $opt{debug};
+}
+
+# Recommendations for InnoDB
+sub mysql_innodb {
+    subheaderprint "InnoDB Metrics";
+
+    # InnoDB
+    unless ( defined $myvar{'have_innodb'}
+        && $myvar{'have_innodb'} eq "YES" )
+    {
+        infoprint "InnoDB is disabled.";
+        if ( mysql_version_ge( 5, 5 ) ) {
+            my $defengine = 'InnoDB';
+            $defengine = $myvar{'default_storage_engine'}
+              if defined( $myvar{'default_storage_engine'} );
+            badprint
+"InnoDB Storage engine is disabled. $defengine is the default storage engine"
+              if $defengine eq 'InnoDB';
+            infoprint
+"InnoDB Storage engine is disabled. $defengine is the default storage engine"
+              if $defengine ne 'InnoDB';
+        }
+        return;
+    }
+    infoprint "InnoDB is enabled.";
+    if ( !defined $enginestats{'InnoDB'} ) {
+        if ( $opt{skipsize} eq 1 ) {
+            infoprint "Skipped due to --skipsize option";
+            return;
+        }
+        badprint "No tables are Innodb";
+        $enginestats{'InnoDB'} = 0;
+    }
+
+    if ( $opt{buffers} ne 0 ) {
+        infoprint "InnoDB Buffers";
+        if ( defined $myvar{'innodb_buffer_pool_size'} ) {
+            infoprint " +-- InnoDB Buffer Pool: "
+              . hr_bytes( $myvar{'innodb_buffer_pool_size'} ) . "";
+        }
+        if ( defined $myvar{'innodb_buffer_pool_instances'} ) {
+            infoprint " +-- InnoDB Buffer Pool Instances: "
+              . $myvar{'innodb_buffer_pool_instances'} . "";
+        }
+
+        if ( defined $myvar{'innodb_buffer_pool_chunk_size'} ) {
+            infoprint " +-- InnoDB Buffer Pool Chunk Size: "
+              . hr_bytes( $myvar{'innodb_buffer_pool_chunk_size'} ) . "";
+        }
+        if ( defined $myvar{'innodb_additional_mem_pool_size'} ) {
+            infoprint " +-- InnoDB Additional Mem Pool: "
+              . hr_bytes( $myvar{'innodb_additional_mem_pool_size'} ) . "";
+        }
+        if ( defined $myvar{'innodb_redo_log_capacity'} ) {
+            infoprint " +-- InnoDB Redo Log Capacity: "
+              . hr_bytes( $myvar{'innodb_redo_log_capacity'} );
+        }
+        else {
+            if ( defined $myvar{'innodb_log_file_size'} ) {
+                infoprint " +-- InnoDB Log File Size: "
+                  . hr_bytes( $myvar{'innodb_log_file_size'} );
+            }
+            if ( defined $myvar{'innodb_log_files_in_group'} ) {
+                infoprint " +-- InnoDB Log File In Group: "
+                  . $myvar{'innodb_log_files_in_group'};
+                infoprint " +-- InnoDB Total Log File Size: "
+                  . hr_bytes( $myvar{'innodb_log_files_in_group'} *
+                      $myvar{'innodb_log_file_size'} )
+                  . "("
+                  . $mycalc{'innodb_log_size_pct'}
+                  . " % of buffer pool)";
+            }
+            else {
+                infoprint " +-- InnoDB Total Log File Size: "
+                  . hr_bytes( $myvar{'innodb_log_file_size'} ) . "("
+                  . $mycalc{'innodb_log_size_pct'}
+                  . " % of buffer pool)";
+            }
+        }
+        if ( defined $myvar{'innodb_log_buffer_size'} ) {
+            infoprint " +-- InnoDB Log Buffer: "
+              . hr_bytes( $myvar{'innodb_log_buffer_size'} );
+        }
+        if ( defined $mystat{'Innodb_buffer_pool_pages_free'} ) {
+            infoprint " +-- InnoDB Buffer Free: "
+              . hr_bytes( $mystat{'Innodb_buffer_pool_pages_free'} ) . "";
+        }
+        if ( defined $mystat{'Innodb_buffer_pool_pages_total'} ) {
+            infoprint " +-- InnoDB Buffer Used: "
+              . hr_bytes( $mystat{'Innodb_buffer_pool_pages_total'} ) . "";
+        }
+    }
+
+    if ( defined $myvar{'innodb_thread_concurrency'} ) {
+        infoprint "InnoDB Thread Concurrency: "
+          . $myvar{'innodb_thread_concurrency'};
+    }
+
+    # InnoDB Buffer Pool Size
+    if ( $myvar{'innodb_file_per_table'} eq "ON" ) {
+        goodprint "InnoDB File per table is activated";
+    }
+    else {
+        badprint "InnoDB File per table is not activated";
+        push( @adjvars, "innodb_file_per_table=ON" );
+    }
+
+    # InnoDB Buffer Pool Size
+    if ( $arch == 32 && $myvar{'innodb_buffer_pool_size'} > 4294967295 ) {
+        badprint
+          "InnoDB Buffer Pool size limit reached for 32 bits architecture: ("
+          . hr_bytes(4294967295) . " )";
+        push( @adjvars,
+                "limit innodb_buffer_pool_size under "
+              . hr_bytes(4294967295)
+              . " for 32 bits architecture" );
+    }
+    if ( $arch == 32 && $myvar{'innodb_buffer_pool_size'} < 4294967295 ) {
+        goodprint "InnoDB Buffer Pool size ( "
+          . hr_bytes( $myvar{'innodb_buffer_pool_size'} )
+          . " ) under limit for 32 bits architecture: ("
+          . hr_bytes(4294967295) . ")";
+    }
+    if (   $arch == 64
+        && $myvar{'innodb_buffer_pool_size'} > 18446744073709551615 )
+    {
+        badprint "InnoDB Buffer Pool size limit("
+          . hr_bytes(18446744073709551615)
+          . ") reached for 64 bits architecture";
+        push( @adjvars,
+                "limit innodb_buffer_pool_size under "
+              . hr_bytes(18446744073709551615)
+              . " for 64 bits architecture" );
+    }
+
+    if (   $arch == 64
+        && $myvar{'innodb_buffer_pool_size'} < 18446744073709551615 )
+    {
+        goodprint "InnoDB Buffer Pool size ( "
+          . hr_bytes( $myvar{'innodb_buffer_pool_size'} )
+          . " ) under limit for 64 bits architecture: ("
+          . hr_bytes(18446744073709551615) . " )";
+    }
+    if ( $myvar{'innodb_buffer_pool_size'} > $enginestats{'InnoDB'} ) {
+        goodprint "InnoDB buffer pool / data size: "
+          . hr_bytes( $myvar{'innodb_buffer_pool_size'} ) . " / "
+          . hr_bytes( $enginestats{'InnoDB'} ) . "";
+    }
+    else {
+        badprint "InnoDB buffer pool / data size: "
+          . hr_bytes( $myvar{'innodb_buffer_pool_size'} ) . " / "
+          . hr_bytes( $enginestats{'InnoDB'} ) . "";
+        push( @adjvars,
+                "innodb_buffer_pool_size (>= "
+              . hr_bytes( $enginestats{'InnoDB'} )
+              . ") if possible." );
+    }
+
+  # select  round( 100* sum(allocated)/( select VARIABLE_VALUE
+  #                                  FROM information_schema.global_variables
+  #                              where VARIABLE_NAME='innodb_buffer_pool_size' )
+  # ,2) as "PCT ALLOC/BUFFER POOL"
+  #from sys.x$innodb_buffer_stats_by_table;
+
+    if ( $opt{experimental} ) {
+        debugprint( 'innodb_buffer_alloc_pct: "'
+              . $mycalc{innodb_buffer_alloc_pct}
+              . '"' );
+        if ( defined $mycalc{innodb_buffer_alloc_pct}
+            and $mycalc{innodb_buffer_alloc_pct} ne '' )
+        {
+            if ( $mycalc{innodb_buffer_alloc_pct} < 80 ) {
+                badprint "Ratio Buffer Pool allocated / Buffer Pool Size: "
+                  . $mycalc{'innodb_buffer_alloc_pct'} . '%';
+            }
+            else {
+                goodprint "Ratio Buffer Pool allocated / Buffer Pool Size: "
+                  . $mycalc{'innodb_buffer_alloc_pct'} . '%';
+            }
+        }
+    }
+    if (   $mycalc{'innodb_log_size_pct'} < 20
+        or $mycalc{'innodb_log_size_pct'} > 30 )
+    {
+        if ( defined $myvar{'innodb_redo_log_capacity'} ) {
+            badprint
+              "Ratio InnoDB redo log capacity / InnoDB Buffer pool size ("
+              . $mycalc{'innodb_log_size_pct'} . "%): "
+              . hr_bytes( $myvar{'innodb_redo_log_capacity'} ) . " / "
+              . hr_bytes( $myvar{'innodb_buffer_pool_size'} )
+              . " should be equal to 25%";
+            push( @adjvars,
+                    "innodb_redo_log_capacity should be (="
+                  . hr_bytes_rnd( $myvar{'innodb_buffer_pool_size'} / 4 )
+                  . ") if possible, so InnoDB Redo log Capacity equals 25% of buffer pool size."
+            );
+            push( @generalrec,
+"Be careful, increasing innodb_redo_log_capacity means higher crash recovery mean time"
+            );
+        }
+        else {
+            badprint "Ratio InnoDB log file size / InnoDB Buffer pool size ("
+              . $mycalc{'innodb_log_size_pct'} . "%): "
+              . hr_bytes( $myvar{'innodb_log_file_size'} ) . " * "
+              . $myvar{'innodb_log_files_in_group'} . " / "
+              . hr_bytes( $myvar{'innodb_buffer_pool_size'} )
+              . " should be equal to 25%";
+            push(
+                @adjvars,
+                "innodb_log_file_size should be (="
+                  . hr_bytes_rnd(
+                    $myvar{'innodb_buffer_pool_size'} /
+                      $myvar{'innodb_log_files_in_group'} / 4
+                  )
+                  . ") if possible, so InnoDB total log file size equals 25% of buffer pool size."
+            );
+            push( @generalrec,
+"Be careful, increasing innodb_log_file_size / innodb_log_files_in_group means higher crash recovery mean time"
+            );
+        }
+        if ( mysql_version_le( 5, 6, 2 ) ) {
+            push( @generalrec,
+"For MySQL 5.6.2 and lower, total innodb_log_file_size should have a ceiling of (4096MB / log files in group) - 1MB."
+            );
+        }
+
+    }
+    else {
+        if ( defined $myvar{'innodb_redo_log_capacity'} ) {
+            goodprint
+              "Ratio InnoDB Redo Log Capacity / InnoDB Buffer pool size: "
+              . hr_bytes( $myvar{'innodb_redo_log_capacity'} ) . "/"
+              . hr_bytes( $myvar{'innodb_buffer_pool_size'} )
+              . " should be equal to 25%";
+        }
+        else {
+            push( @generalrec,
+"Before changing innodb_log_file_size and/or innodb_log_files_in_group read this: https://bit.ly/2TcGgtU"
+            );
+            goodprint "Ratio InnoDB log file size / InnoDB Buffer pool size: "
+              . hr_bytes( $myvar{'innodb_log_file_size'} ) . " * "
+              . $myvar{'innodb_log_files_in_group'} . "/"
+              . hr_bytes( $myvar{'innodb_buffer_pool_size'} )
+              . " should be equal to 25%";
+        }
+    }
+
+    # InnoDB Buffer Pool Instances (MySQL 5.6.6+)
+    if ( not mysql_version_ge( 10, 4 )
+        and defined( $myvar{'innodb_buffer_pool_instances'} ) )
+    {
+
+        # Bad Value if > 64
+        if ( $myvar{'innodb_buffer_pool_instances'} > 64 ) {
+            badprint "InnoDB buffer pool instances: "
+              . $myvar{'innodb_buffer_pool_instances'} . "";
+            push( @adjvars, "innodb_buffer_pool_instances (<= 64)" );
+        }
+
+        # InnoDB Buffer Pool Size > 1Go
+        if ( $myvar{'innodb_buffer_pool_size'} > 1024 * 1024 * 1024 ) {
+
+# InnoDB Buffer Pool Size / 1Go = InnoDB Buffer Pool Instances limited to 64 max.
+
+            #  InnoDB Buffer Pool Size > 64Go
+            my $max_innodb_buffer_pool_instances =
+              int( $myvar{'innodb_buffer_pool_size'} / ( 1024 * 1024 * 1024 ) );
+            $max_innodb_buffer_pool_instances = 64
+              if ( $max_innodb_buffer_pool_instances > 64 );
+
+            if ( $myvar{'innodb_buffer_pool_instances'} !=
+                $max_innodb_buffer_pool_instances )
+            {
+                badprint "InnoDB buffer pool instances: "
+                  . $myvar{'innodb_buffer_pool_instances'} . "";
+                push( @adjvars,
+                        "innodb_buffer_pool_instances(="
+                      . $max_innodb_buffer_pool_instances
+                      . ")" );
+            }
+            else {
+                goodprint "InnoDB buffer pool instances: "
+                  . $myvar{'innodb_buffer_pool_instances'} . "";
+            }
+
+            # InnoDB Buffer Pool Size < 1Go
+        }
+        else {
+            if ( $myvar{'innodb_buffer_pool_instances'} != 1 ) {
+                badprint
+"InnoDB buffer pool <= 1G and Innodb_buffer_pool_instances(!=1).";
+                push( @adjvars, "innodb_buffer_pool_instances (=1)" );
+            }
+            else {
+                goodprint "InnoDB buffer pool instances: "
+                  . $myvar{'innodb_buffer_pool_instances'} . "";
+            }
+        }
+    }
+
+    # InnoDB Used Buffer Pool Size vs CHUNK size
+    if ( !defined( $myvar{'innodb_buffer_pool_chunk_size'} ) ) {
+        infoprint
+          "InnoDB Buffer Pool Chunk Size not used or defined in your version";
+    }
+    else {
+        infoprint "Number of InnoDB Buffer Pool Chunk: "
+          . int( $myvar{'innodb_buffer_pool_size'} ) /
+          int( $myvar{'innodb_buffer_pool_chunk_size'} ) . " for "
+          . $myvar{'innodb_buffer_pool_instances'}
+          . " Buffer Pool Instance(s)";
+
+        if (
+            int( $myvar{'innodb_buffer_pool_size'} ) % (
+                int( $myvar{'innodb_buffer_pool_chunk_size'} ) *
+                  int( $myvar{'innodb_buffer_pool_instances'} )
+            ) eq 0
+          )
+        {
+            goodprint
+"Innodb_buffer_pool_size aligned with Innodb_buffer_pool_chunk_size & Innodb_buffer_pool_instances";
+        }
+        else {
+            badprint
+"Innodb_buffer_pool_size aligned with Innodb_buffer_pool_chunk_size & Innodb_buffer_pool_instances";
+
+#push( @adjvars, "Adjust innodb_buffer_pool_instances, innodb_buffer_pool_chunk_size with innodb_buffer_pool_size" );
+            push( @adjvars,
+"innodb_buffer_pool_size must always be equal to or a multiple of innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances"
+            );
+        }
+    }
+
+    # InnoDB Read efficiency
+    if ( defined $mycalc{'pct_read_efficiency'}
+        && $mycalc{'pct_read_efficiency'} < 90 )
+    {
+        badprint "InnoDB Read buffer efficiency: "
+          . $mycalc{'pct_read_efficiency'} . "% ("
+          . $mystat{'Innodb_buffer_pool_read_requests'}
+          . " hits / "
+          . ( $mystat{'Innodb_buffer_pool_reads'} +
+              $mystat{'Innodb_buffer_pool_read_requests'} )
+          . " total)";
+    }
+    else {
+        goodprint "InnoDB Read buffer efficiency: "
+          . $mycalc{'pct_read_efficiency'} . "% ("
+          . $mystat{'Innodb_buffer_pool_read_requests'}
+          . " hits / "
+          . ( $mystat{'Innodb_buffer_pool_reads'} +
+              $mystat{'Innodb_buffer_pool_read_requests'} )
+          . " total)";
+    }
+
+    # InnoDB Write efficiency
+    if ( defined $mycalc{'pct_write_efficiency'}
+        && $mycalc{'pct_write_efficiency'} < 90 )
+    {
+        badprint "InnoDB Write Log efficiency: "
+          . abs( $mycalc{'pct_write_efficiency'} ) . "% ("
+          . abs( $mystat{'Innodb_log_write_requests'} -
+              $mystat{'Innodb_log_writes'} )
+          . " hits / "
+          . $mystat{'Innodb_log_write_requests'}
+          . " total)";
+        push( @adjvars,
+                "innodb_log_buffer_size (> "
+              . hr_bytes_rnd( $myvar{'innodb_log_buffer_size'} )
+              . ")" );
+    }
+    else {
+        goodprint "InnoDB Write Log efficiency: "
+          . $mycalc{'pct_write_efficiency'} . "% ("
+          . ( $mystat{'Innodb_log_write_requests'} -
+              $mystat{'Innodb_log_writes'} )
+          . " hits / "
+          . $mystat{'Innodb_log_write_requests'}
+          . " total)";
+    }
+
+    # InnoDB Log Waits
+    $mystat{'Innodb_log_waits_computed'} = 0;
+
+    if (    defined( $mystat{'Innodb_log_waits'} )
+        and defined( $mystat{'Innodb_log_writes'} )
+        and $mystat{'Innodb_log_writes'} > 0.000001 )
+    {
+        $mystat{'Innodb_log_waits_computed'} =
+          $mystat{'Innodb_log_waits'} / $mystat{'Innodb_log_writes'};
+    }
+    else {
+        undef $mystat{'Innodb_log_waits_computed'};
+    }
+
+    if ( defined $mystat{'Innodb_log_waits_computed'}
+        && $mystat{'Innodb_log_waits_computed'} > 0.000001 )
+    {
+        badprint "InnoDB log waits: "
+          . percentage( $mystat{'Innodb_log_waits'},
+            $mystat{'Innodb_log_writes'} )
+          . "% ("
+          . $mystat{'Innodb_log_waits'}
+          . " waits / "
+          . $mystat{'Innodb_log_writes'}
+          . " writes)";
+        push( @adjvars,
+                "innodb_log_buffer_size (> "
+              . hr_bytes_rnd( $myvar{'innodb_log_buffer_size'} )
+              . ")" );
+    }
+    else {
+        goodprint "InnoDB log waits: "
+          . percentage( $mystat{'Innodb_log_waits'},
+            $mystat{'Innodb_log_writes'} )
+          . "% ("
+          . $mystat{'Innodb_log_waits'}
+          . " waits / "
+          . $mystat{'Innodb_log_writes'}
+          . " writes)";
+    }
+    $result{'Calculations'} = {%mycalc};
+}
+
+sub check_metadata_perf {
+    subheaderprint "Analysis Performance Metrics";
+    if ( defined $myvar{'innodb_stats_on_metadata'} ) {
+        infoprint "innodb_stats_on_metadata: "
+          . $myvar{'innodb_stats_on_metadata'};
+        if ( $myvar{'innodb_stats_on_metadata'} eq 'ON' ) {
+            badprint "Stat are updated during querying INFORMATION_SCHEMA.";
+            push @adjvars, "SET innodb_stats_on_metadata = OFF";
+
+            #Disabling innodb_stats_on_metadata
+            select_one("SET GLOBAL innodb_stats_on_metadata = OFF;");
+            return 1;
+        }
+    }
+    goodprint "No stat updates during querying INFORMATION_SCHEMA.";
+    return 0;
+}
+
+# Recommendations for Database metrics
+sub mysql_databases {
+    return if ( $opt{dbstat} == 0 );
+
+    subheaderprint "Database Metrics";
+    unless ( mysql_version_ge( 5, 5 ) ) {
+        infoprint
+"Database metrics from information schema are missing in this version. Skipping...";
+        return;
+    }
+
+    @dblist = select_array(
+"SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME NOT IN ( 'mysql', 'performance_schema', 'information_schema', 'sys' );"
+    );
+    infoprint "There is " . scalar(@dblist) . " Database(s).";
+    my @totaldbinfo = split /\s/,
+      select_one(
+"SELECT SUM(TABLE_ROWS), SUM(DATA_LENGTH), SUM(INDEX_LENGTH), SUM(DATA_LENGTH+INDEX_LENGTH), COUNT(TABLE_NAME), COUNT(DISTINCT(TABLE_COLLATION)), COUNT(DISTINCT(ENGINE)) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys');"
+      );
+    infoprint "All User Databases:";
+    infoprint " +-- TABLE : "
+      . select_one(
+"SELECT count(*) from information_schema.TABLES WHERE TABLE_TYPE ='BASE TABLE' AND TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')"
+      ) . "";
+    infoprint " +-- VIEW  : "
+      . select_one(
+"SELECT count(*) from information_schema.TABLES WHERE TABLE_TYPE ='VIEW' AND TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')"
+      ) . "";
+    infoprint " +-- INDEX : "
+      . select_one(
+"SELECT count(distinct(concat(TABLE_NAME, TABLE_SCHEMA, INDEX_NAME))) from information_schema.STATISTICS WHERE TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys')"
+      ) . "";
+
+    infoprint " +-- CHARS : "
+      . ( $totaldbinfo[5] eq 'NULL' ? 0 : $totaldbinfo[5] ) . " ("
+      . (
+        join ", ",
+        select_array(
+"select distinct(CHARACTER_SET_NAME) from information_schema.columns WHERE CHARACTER_SET_NAME IS NOT NULL AND TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys');"
+        )
+      ) . ")";
+    infoprint " +-- COLLA : "
+      . ( $totaldbinfo[5] eq 'NULL' ? 0 : $totaldbinfo[5] ) . " ("
+      . (
+        join ", ",
+        select_array(
+"SELECT DISTINCT(TABLE_COLLATION) FROM information_schema.TABLES WHERE TABLE_COLLATION IS NOT NULL AND TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys');"
+        )
+      ) . ")";
+    infoprint " +-- ROWS  : "
+      . ( $totaldbinfo[0] eq 'NULL' ? 0 : $totaldbinfo[0] ) . "";
+    infoprint " +-- DATA  : "
+      . hr_bytes( $totaldbinfo[1] ) . "("
+      . percentage( $totaldbinfo[1], $totaldbinfo[3] ) . "%)";
+    infoprint " +-- INDEX : "
+      . hr_bytes( $totaldbinfo[2] ) . "("
+      . percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%)";
+    infoprint " +-- SIZE  : " . hr_bytes( $totaldbinfo[3] ) . "";
+    infoprint " +-- ENGINE: "
+      . ( $totaldbinfo[6] eq 'NULL' ? 0 : $totaldbinfo[6] ) . " ("
+      . (
+        join ", ",
+        select_array(
+"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE ENGINE IS NOT NULL AND TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema', 'sys');"
+        )
+      ) . ")";
+
+    $result{'Databases'}{'All databases'}{'Rows'} =
+      ( $totaldbinfo[0] eq 'NULL' ? 0 : $totaldbinfo[0] );
+    $result{'Databases'}{'All databases'}{'Data Size'} = $totaldbinfo[1];
+    $result{'Databases'}{'All databases'}{'Data Pct'} =
+      percentage( $totaldbinfo[1], $totaldbinfo[3] ) . "%";
+    $result{'Databases'}{'All databases'}{'Index Size'} = $totaldbinfo[2];
+    $result{'Databases'}{'All databases'}{'Index Pct'} =
+      percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%";
+    $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3];
+    print "\n" unless ( $opt{'silent'} or $opt{'json'} );
+    my $nbViews  = 0;
+    my $nbTables = 0;
+
+    foreach (@dblist) {
+        my @dbinfo = split /\s/,
+          select_one(
+"SELECT TABLE_SCHEMA, SUM(TABLE_ROWS), SUM(DATA_LENGTH), SUM(INDEX_LENGTH), SUM(DATA_LENGTH+INDEX_LENGTH), COUNT(DISTINCT ENGINE), COUNT(TABLE_NAME), COUNT(DISTINCT(TABLE_COLLATION)), COUNT(DISTINCT(ENGINE)) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_' GROUP BY TABLE_SCHEMA ORDER BY TABLE_SCHEMA"
+          );
+        next unless defined $dbinfo[0];
+
+        infoprint "Database: " . $dbinfo[0] . "";
+        $nbTables = select_one(
+"SELECT count(*) from information_schema.TABLES WHERE TABLE_TYPE ='BASE TABLE' AND TABLE_SCHEMA='$_'"
+        );
+        infoprint " +-- TABLE : $nbTables";
+        infoprint " +-- VIEW  : "
+          . select_one(
+"SELECT count(*) from information_schema.TABLES WHERE TABLE_TYPE ='VIEW' AND TABLE_SCHEMA='$_'"
+          ) . "";
+        infoprint " +-- INDEX : "
+          . select_one(
+"SELECT count(distinct(concat(TABLE_NAME, TABLE_SCHEMA, INDEX_NAME))) from information_schema.STATISTICS WHERE TABLE_SCHEMA='$_'"
+          ) . "";
+        infoprint " +-- CHARS : "
+          . ( $totaldbinfo[5] eq 'NULL' ? 0 : $totaldbinfo[5] ) . " ("
+          . (
+            join ", ",
+            select_array(
+"select distinct(CHARACTER_SET_NAME) from information_schema.columns WHERE CHARACTER_SET_NAME IS NOT NULL AND TABLE_SCHEMA='$_';"
+            )
+          ) . ")";
+        infoprint " +-- COLLA : "
+          . ( $dbinfo[7] eq 'NULL' ? 0 : $dbinfo[7] ) . " ("
+          . (
+            join ", ",
+            select_array(
+"SELECT DISTINCT(TABLE_COLLATION) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_' AND TABLE_COLLATION IS NOT NULL;"
+            )
+          ) . ")";
+        infoprint " +-- ROWS  : "
+          . ( !defined( $dbinfo[1] ) or $dbinfo[1] eq 'NULL' ? 0 : $dbinfo[1] )
+          . "";
+        infoprint " +-- DATA  : "
+          . hr_bytes( $dbinfo[2] ) . "("
+          . percentage( $dbinfo[2], $dbinfo[4] ) . "%)";
+        infoprint " +-- INDEX : "
+          . hr_bytes( $dbinfo[3] ) . "("
+          . percentage( $dbinfo[3], $dbinfo[4] ) . "%)";
+        infoprint " +-- TOTAL : " . hr_bytes( $dbinfo[4] ) . "";
+        infoprint " +-- ENGINE: "
+          . ( $dbinfo[8] eq 'NULL' ? 0 : $dbinfo[8] ) . " ("
+          . (
+            join ", ",
+            select_array(
+"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_' AND ENGINE IS NOT NULL"
+            )
+          ) . ")";
+
+        foreach my $eng (
+            select_array(
+"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_' AND ENGINE IS NOT NULL"
+            )
+          )
+        {
+            infoprint " +-- ENGINE $eng : "
+              . select_one(
+"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$dbinfo[0]' AND ENGINE='$eng'"
+              ) . " TABLE(s)";
+        }
+
+        if ( $nbTables == 0 ) {
+            badprint " No table in $dbinfo[0] database";
+            next;
+        }
+        badprint "Index size is larger than data size for $dbinfo[0] \n"
+          if ( $dbinfo[2] ne 'NULL' )
+          and ( $dbinfo[3] ne 'NULL' )
+          and ( $dbinfo[2] < $dbinfo[3] );
+        if ( $dbinfo[5] > 1 and $nbTables > 0 ) {
+            badprint "There are "
+              . $dbinfo[5]
+              . " storage engines. Be careful. \n";
+            push @generalrec,
+"Select one storage engine (InnoDB is a good choice) for all tables in $dbinfo[0] database ($dbinfo[5] engines detected)";
+        }
+        $result{'Databases'}{ $dbinfo[0] }{'Rows'}       = $dbinfo[1];
+        $result{'Databases'}{ $dbinfo[0] }{'Tables'}     = $dbinfo[6];
+        $result{'Databases'}{ $dbinfo[0] }{'Collations'} = $dbinfo[7];
+        $result{'Databases'}{ $dbinfo[0] }{'Data Size'}  = $dbinfo[2];
+        $result{'Databases'}{ $dbinfo[0] }{'Data Pct'} =
+          percentage( $dbinfo[2], $dbinfo[4] ) . "%";
+        $result{'Databases'}{ $dbinfo[0] }{'Index Size'} = $dbinfo[3];
+        $result{'Databases'}{ $dbinfo[0] }{'Index Pct'} =
+          percentage( $dbinfo[3], $dbinfo[4] ) . "%";
+        $result{'Databases'}{ $dbinfo[0] }{'Total Size'} = $dbinfo[4];
+
+        if ( $dbinfo[7] > 1 ) {
+            badprint $dbinfo[7]
+              . " different collations for database "
+              . $dbinfo[0];
+            push( @generalrec,
+                "Check all table collations are identical for all tables in "
+                  . $dbinfo[0]
+                  . " database." );
+        }
+        else {
+            goodprint $dbinfo[7]
+              . " collation for "
+              . $dbinfo[0]
+              . " database.";
+        }
+        if ( $dbinfo[8] > 1 ) {
+            badprint $dbinfo[8]
+              . " different engines for database "
+              . $dbinfo[0];
+            push( @generalrec,
+                    "Check all table engines are identical for all tables in "
+                  . $dbinfo[0]
+                  . " database." );
+        }
+        else {
+            goodprint $dbinfo[8] . " engine for " . $dbinfo[0] . " database.";
+        }
+
+        my @distinct_column_charset = select_array(
+"select DISTINCT(CHARACTER_SET_NAME) from information_schema.COLUMNS where CHARACTER_SET_NAME IS NOT NULL AND TABLE_SCHEMA ='$_' AND CHARACTER_SET_NAME IS NOT NULL"
+        );
+        infoprint "Charsets for $dbinfo[0] database table column: "
+          . join( ', ', @distinct_column_charset );
+        if ( scalar(@distinct_column_charset) > 1 ) {
+            badprint $dbinfo[0]
+              . " table column(s) has several charsets defined for all text like column(s).";
+            push( @generalrec,
+                    "Limit charset for column to one charset if possible for "
+                  . $dbinfo[0]
+                  . " database." );
+        }
+        else {
+            goodprint $dbinfo[0]
+              . " table column(s) has same charset defined for all text like column(s).";
+        }
+
+        my @distinct_column_collation = select_array(
+"select DISTINCT(COLLATION_NAME) from information_schema.COLUMNS where COLLATION_NAME IS NOT NULL AND TABLE_SCHEMA ='$_' AND COLLATION_NAME IS NOT NULL"
+        );
+        infoprint "Collations for $dbinfo[0] database table column: "
+          . join( ', ', @distinct_column_collation );
+        if ( scalar(@distinct_column_collation) > 1 ) {
+            badprint $dbinfo[0]
+              . " table column(s) has several collations defined for all text like column(s).";
+            push( @generalrec,
+                "Limit collations for column to one collation if possible for "
+                  . $dbinfo[0]
+                  . " database." );
+        }
+        else {
+            goodprint $dbinfo[0]
+              . " table column(s) has same collation defined for all text like column(s).";
+        }
+    }
+}
+
+# Recommendations for database columns
+sub mysql_tables {
+    return if ( $opt{tbstat} == 0 );
+
+    subheaderprint "Table Column Metrics";
+    unless ( mysql_version_ge( 5, 5 ) ) {
+        infoprint
+"Table column metrics from information schema are missing in this version. Skipping...";
+        return;
+    }
+    if ( mysql_version_ge(8) and not mysql_version_eq(10) ) {
+        infoprint
+"MySQL and Percona version 8.0 and greater have removed PROCEDURE ANALYSE feature";
+        $opt{colstat} = 0;
+        infoprint "Disabling colstat parameter";
+
+    }
+
+    infoprint("Dumpdir: $opt{dumpdir}");
+
+    # Store all information schema in dumpdir if defined
+    if ( defined $opt{dumpdir} and -d "$opt{dumpdir}" ) {
+        for my $info_s_table (
+            select_array('use information_schema;show tables;') )
+        {
+            infoprint "Dumping $info_s_table into $opt{dumpdir}";
+            select_csv_file(
+                "$opt{dumpdir}/ifs_${info_s_table}.csv",
+                "select * from information_schema.$info_s_table"
+            );
+        }
+
+        #exit 0 if ( $opt{stop} == 1 );
+    }
+    foreach ( select_user_dbs() ) {
+        my $dbname = $_;
+        next unless defined $_;
+        infoprint "Database: " . $_ . "";
+        my @dbtable = select_array(
+"SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='$dbname' AND TABLE_TYPE='BASE TABLE' ORDER BY TABLE_NAME"
+        );
+        foreach (@dbtable) {
+            my $tbname = $_;
+            infoprint " +-- TABLE: $tbname";
+            infoprint "     +-- TYPE: "
+              . select_one(
+"SELECT ENGINE FROM information_schema.tables where TABLE_schema='$dbname' AND TABLE_NAME='$tbname'"
+              );
+
+            my $selIdxReq = <<"ENDSQL";
+      SELECT  index_name AS idxname, 
+              GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols, 
+              INDEX_TYPE as type
+              FROM information_schema.statistics
+              WHERE INDEX_SCHEMA='$dbname'
+              AND TABLE_NAME='$tbname'
+              GROUP BY idxname, type
+ENDSQL
+            my @tbidx = select_array($selIdxReq);
+            my $found = 0;
+            foreach my $idx (@tbidx) {
+                my @info = split /\s/, $idx;
+                next if $info[0] eq 'NULL';
+                infoprint
+                  "     +-- Index $info[0] - Cols: $info[1] - Type: $info[2]";
+                $found++;
+            }
+            if ( $found == 0 ) {
+                badprint("Table $dbname.$tbname has no index defined");
+                push @generalrec,
+                  "Add at least a primary key on table $dbname.$tbname";
+            }
+            my @tbcol = select_array(
+"SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname'"
+            );
+            foreach (@tbcol) {
+                my $ctype = select_one(
+"SELECT COLUMN_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname' AND COLUMN_NAME='$_' "
+                );
+                my $isnull = select_one(
+"SELECT IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='$dbname' AND TABLE_NAME='$tbname' AND COLUMN_NAME='$_' "
+                );
+
+                my $current_type =
+                  uc($ctype) . ( $isnull eq 'NO' ? " NOT NULL" : " NULL" );
+                my $optimal_type = '';
+                infoprint "     +-- Column $tbname.$_: $current_type";
+                if ( $opt{colstat} == 1 ) {
+                    $optimal_type = select_str_g( "Optimal_fieldtype",
+"SELECT \\`$_\\` FROM \\`$dbname\\`.\\`$tbname\\` PROCEDURE ANALYSE(100000)"
+                      )
+                      unless ( mysql_version_ge(8)
+                        and not mysql_version_eq(10) );
+                }
+                if ( $optimal_type eq '' ) {
+
+                    #infoprint "     +-- Current Fieldtype: $current_type";
+
+                    #infoprint "      Optimal Fieldtype: Not available";
+                }
+                elsif ( $current_type ne $optimal_type
+                    and $current_type !~ /.*DATETIME.*/
+                    and $current_type !~ /.*TIMESTAMP.*/ )
+                {
+                    infoprint "     +-- Current Fieldtype: $current_type";
+                    if ( $optimal_type =~ /.*ENUM\(.*/ ) {
+                        $optimal_type = "ENUM( ... )";
+                    }
+                    infoprint "     +-- Optimal Fieldtype: $optimal_type ";
+                    if ( $optimal_type !~ /.*ENUM\(.*/ ) {
+                        badprint
+"Consider changing type for column $_ in table $dbname.$tbname";
+                        push( @generalrec,
+"ALTER TABLE \`$dbname\`.\`$tbname\` MODIFY \`$_\` $optimal_type;"
+                        );
+                    }
+                }
+                else {
+                    goodprint "$dbname.$tbname ($_) type: $current_type";
+                }
+            }
+        }
+    }
+}
+
+# Recommendations for Indexes metrics
+sub mysql_indexes {
+    return if ( $opt{idxstat} == 0 );
+
+    subheaderprint "Indexes Metrics";
+    unless ( mysql_version_ge( 5, 5 ) ) {
+        infoprint
+"Index metrics from information schema are missing in this version. Skipping...";
+        return;
+    }
+
+#    unless ( mysql_version_ge( 5, 6 ) ) {
+#        infoprint
+#"Skip Index metrics from information schema due to erroneous information provided in this version";
+#        return;
+#    }
+    my $selIdxReq = <<'ENDSQL';
+SELECT
+  CONCAT(t.TABLE_SCHEMA, '.', t.TABLE_NAME) AS 'table', 
+  CONCAT(s.INDEX_NAME, '(', s.COLUMN_NAME, ')') AS 'index'
+ , s.SEQ_IN_INDEX AS 'seq'
+ , s2.max_columns AS 'maxcol'
+ , s.CARDINALITY  AS 'card'
+ , t.TABLE_ROWS   AS 'est_rows'
+ , INDEX_TYPE as type
+ , ROUND(((s.CARDINALITY / IFNULL(t.TABLE_ROWS, 0.01)) * 100), 2) AS 'sel'
+FROM INFORMATION_SCHEMA.STATISTICS s
+ INNER JOIN INFORMATION_SCHEMA.TABLES t
+  ON s.TABLE_SCHEMA = t.TABLE_SCHEMA
+  AND s.TABLE_NAME = t.TABLE_NAME
+ INNER JOIN (
+  SELECT
+     TABLE_SCHEMA
+   , TABLE_NAME
+   , INDEX_NAME
+   , MAX(SEQ_IN_INDEX) AS max_columns
+  FROM INFORMATION_SCHEMA.STATISTICS
+  WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema')
+  AND INDEX_TYPE <> 'FULLTEXT'
+  GROUP BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME
+ ) AS s2
+ ON s.TABLE_SCHEMA = s2.TABLE_SCHEMA
+ AND s.TABLE_NAME = s2.TABLE_NAME
+ AND s.INDEX_NAME = s2.INDEX_NAME
+WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema')
+AND t.TABLE_ROWS > 10
+AND s.CARDINALITY IS NOT NULL
+AND (s.CARDINALITY / IFNULL(t.TABLE_ROWS, 0.01)) < 8.00
+ORDER BY sel
+LIMIT 10;
+ENDSQL
+    my @idxinfo = select_array($selIdxReq);
+    infoprint "Worst selectivity indexes:";
+    foreach (@idxinfo) {
+        debugprint "$_";
+        my @info = split /\s/;
+        infoprint "Index: " . $info[1] . "";
+
+        infoprint " +-- COLUMN      : " . $info[0] . "";
+        infoprint " +-- NB SEQS     : " . $info[2] . " sequence(s)";
+        infoprint " +-- NB COLS     : " . $info[3] . " column(s)";
+        infoprint " +-- CARDINALITY : " . $info[4] . " distinct values";
+        infoprint " +-- NB ROWS     : " . $info[5] . " rows";
+        infoprint " +-- TYPE        : " . $info[6];
+        infoprint " +-- SELECTIVITY : " . $info[7] . "%";
+
+        $result{'Indexes'}{ $info[1] }{'Column'}           = $info[0];
+        $result{'Indexes'}{ $info[1] }{'Sequence number'}  = $info[2];
+        $result{'Indexes'}{ $info[1] }{'Number of column'} = $info[3];
+        $result{'Indexes'}{ $info[1] }{'Cardinality'}      = $info[4];
+        $result{'Indexes'}{ $info[1] }{'Row number'}       = $info[5];
+        $result{'Indexes'}{ $info[1] }{'Index Type'}       = $info[6];
+        $result{'Indexes'}{ $info[1] }{'Selectivity'}      = $info[7];
+        if ( $info[7] < 25 ) {
+            badprint "$info[1] has a low selectivity";
+        }
+    }
+    infoprint "Indexes per database:";
+    foreach my $dbname ( select_user_dbs() ) {
+        infoprint "Database: " . $dbname . "";
+        $selIdxReq = <<"ENDSQL";
+        SELECT  concat(table_name, '.', index_name) AS idxname,
+                GROUP_CONCAT(column_name ORDER BY seq_in_index) AS cols,
+                SUM(CARDINALITY) as card,
+                INDEX_TYPE as type
+        FROM information_schema.statistics
+        WHERE INDEX_SCHEMA='$dbname'
+        AND index_name IS NOT NULL
+        GROUP BY table_name, idxname, type
+ENDSQL
+        my $found = 0;
+        foreach my $idxinfo ( select_array($selIdxReq) ) {
+            my @info = split /\s/, $idxinfo;
+            next if $info[0] eq 'NULL';
+            infoprint " +-- INDEX      : " . $info[0];
+            infoprint " +-- COLUMNS    : " . $info[1];
+            infoprint " +-- CARDINALITY: " . $info[2];
+            infoprint " +-- TYPE        : " . $info[4] if defined $info[4];
+            infoprint " +-- COMMENT     : " . $info[5] if defined $info[5];
+            $found++;
+        }
+        my $nbTables = select_one(
+"SELECT count(*) from information_schema.TABLES WHERE TABLE_TYPE ='BASE TABLE' AND TABLE_SCHEMA='$dbname'"
+        );
+        badprint "No index found for $dbname database"
+          if $found == 0 and $nbTables > 1;
+        push @generalrec, "Add indexes on tables from $dbname database"
+          if $found == 0 and $nbTables > 1;
+    }
+    return
+      unless ( defined( $myvar{'performance_schema'} )
+        and $myvar{'performance_schema'} eq 'ON' );
+
+    $selIdxReq = <<'ENDSQL';
+SELECT CONCAT(object_schema, '.', object_name) AS 'table', index_name
+FROM performance_schema.table_io_waits_summary_by_index_usage
+WHERE index_name IS NOT NULL
+AND count_star = 0
+AND index_name <> 'PRIMARY'
+AND object_schema NOT IN ('mysql', 'performance_schema', 'information_schema')
+ORDER BY count_star, object_schema, object_name;
+ENDSQL
+    @idxinfo = select_array($selIdxReq);
+    infoprint "Unused indexes:";
+    push( @generalrec, "Remove unused indexes." ) if ( scalar(@idxinfo) > 0 );
+    foreach (@idxinfo) {
+        debugprint "$_";
+        my @info = split /\s/;
+        badprint "Index: $info[1] on $info[0] is not used.";
+        push @{ $result{'Indexes'}{'Unused Indexes'} },
+          $info[0] . "." . $info[1];
+    }
+}
+
+sub mysql_views {
+    subheaderprint "Views Metrics";
+    unless ( mysql_version_ge( 5, 5 ) ) {
+        infoprint
+"Views metrics from information schema are missing in this version. Skipping...";
+        return;
+    }
+}
+
+sub mysql_routines {
+    subheaderprint "Routines Metrics";
+    unless ( mysql_version_ge( 5, 5 ) ) {
+        infoprint
+"Routines metrics from information schema are missing in this version. Skipping...";
+        return;
+    }
+}
+
+sub mysql_triggers {
+    subheaderprint "Triggers Metrics";
+    unless ( mysql_version_ge( 5, 5 ) ) {
+        infoprint
+"Trigger metrics from information schema are missing in this version. Skipping...";
+        return;
+    }
+}
+
+# Take the two recommendation arrays and display them at the end of the output
+sub make_recommendations {
+    $result{'Recommendations'} = \@generalrec;
+    $result{'AdjustVariables'} = \@adjvars;
+    subheaderprint "Recommendations";
+    if ( @generalrec > 0 ) {
+        prettyprint "General recommendations:";
+        foreach (@generalrec) { prettyprint "    " . $_ . ""; }
+    }
+    if ( @adjvars > 0 ) {
+        prettyprint "Variables to adjust:";
+        if ( $mycalc{'pct_max_physical_memory'} > 90 ) {
+            prettyprint
+              "  *** MySQL's maximum memory usage is dangerously high ***\n"
+              . "  *** Add RAM before increasing MySQL buffer variables ***";
+        }
+        foreach (@adjvars) { prettyprint "    " . $_ . ""; }
+    }
+    if ( @generalrec == 0 && @adjvars == 0 ) {
+        prettyprint "No additional performance recommendations are available.";
+    }
+}
+
+sub close_outputfile {
+    close($fh) if defined($fh);
+}
+
+sub headerprint {
+    prettyprint " >>  MySQLTuner $tunerversion\n"
+      . "\t * Jean-Marie Renouard <jmrenouard\@gmail.com>\n"
+      . "\t * Major Hayden <major\@mhtx.net>\n"
+      . " >>  Bug reports, feature requests, and downloads at http://mysqltuner.pl/\n"
+      . " >>  Run with '--help' for additional options and output filtering";
+    debugprint( "Debug: " . $opt{debug} );
+    debugprint( "Experimental: " . $opt{experimental} );
+}
+
+sub string2file {
+    my $filename = shift;
+    my $content  = shift;
+    open my $fh, q(>), $filename
+      or die
+"Unable to open $filename in write mode. Please check permissions for this file or directory";
+    print $fh $content if defined($content);
+    close $fh;
+    debugprint $content;
+}
+
+sub file2array {
+    my $filename = shift;
+    debugprint "* reading $filename";
+    my $fh;
+    open( $fh, q(<), "$filename" )
+      or die "Couldn't open $filename for reading: $!\n";
+    my @lines = <$fh>;
+    close($fh);
+    return @lines;
+}
+
+sub file2string {
+    return join( '', file2array(@_) );
+}
+
+my $templateModel;
+if ( $opt{'template'} ne 0 ) {
+    $templateModel = file2string( $opt{'template'} );
+}
+else {
+    # DEFAULT REPORT TEMPLATE
+    $templateModel = <<'END_TEMPLATE';
+<!DOCTYPE html>
+<html>
+<head>
+  <title>MySQLTuner Report</title>
+  <meta charset="UTF-8">
+</head>
+<body>
+
+<h1>Result output</h1>
+<pre>
+{$data}
+</pre>
+
+</body>
+</html>
+END_TEMPLATE
+}
+
+sub dump_result {
+
+    #debugprint Dumper( \%result ) if ( $opt{'debug'} );
+    debugprint "HTML REPORT: $opt{'reportfile'}";
+
+    if ( $opt{'reportfile'} ne 0 ) {
+        eval { require Text::Template };
+        eval { require JSON };
+        if ($@) {
+            badprint "Text::Template Module is needed.";
+            die "Text::Template Module is needed.";
+        }
+
+        my $json      = JSON->new->allow_nonref;
+        my $json_text = $json->pretty->encode( \%result );
+        my %vars      = (
+            'data'  => \%result,
+            'debug' => $json_text,
+        );
+        my $template;
+        {
+            no warnings 'once';
+            $template = Text::Template->new(
+                TYPE       => 'STRING',
+                PREPEND    => q{;},
+                SOURCE     => $templateModel,
+                DELIMITERS => [ '[%', '%]' ]
+            ) or die "Couldn't construct template: $Text::Template::ERROR";
+        }
+
+        open my $fh, q(>), $opt{'reportfile'}
+          or die
+"Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory";
+        $template->fill_in( HASH => \%vars, OUTPUT => $fh );
+        close $fh;
+    }
+
+    if ( $opt{'json'} ne 0 ) {
+        eval { require JSON };
+        if ($@) {
+            print "$bad JSON Module is needed.\n";
+            return 1;
+        }
+
+        my $json = JSON->new->allow_nonref;
+        print $json->utf8(1)->pretty( ( $opt{'prettyjson'} ? 1 : 0 ) )
+          ->encode( \%result );
+
+        if ( $opt{'outputfile'} ne 0 ) {
+            unlink $opt{'outputfile'} if ( -e $opt{'outputfile'} );
+            open my $fh, q(>), $opt{'outputfile'}
+              or die
+"Unable to open $opt{'outputfile'} in write mode. please check permissions for this file or directory";
+            print $fh $json->utf8(1)->pretty( ( $opt{'prettyjson'} ? 1 : 0 ) )
+              ->encode( \%result );
+            close $fh;
+        }
+    }
+}
+
+sub which {
+    my $prog_name   = shift;
+    my $path_string = shift;
+    my @path_array  = split /:/, $ENV{'PATH'};
+
+    for my $path (@path_array) {
+        return "$path/$prog_name" if ( -x "$path/$prog_name" );
+    }
+
+    return 0;
+}
+
+# ---------------------------------------------------------------------------
+# BEGIN 'MAIN'
+# ---------------------------------------------------------------------------
+headerprint;    # Header Print
+
+validate_tuner_version;    # Check latest version
+mysql_setup;               # Gotta login first
+debugprint "MySQL FINAL Client : $mysqlcmd $mysqllogin";
+debugprint "MySQL Admin FINAL Client : $mysqladmincmd $mysqllogin";
+
+#exit(0);
+os_setup;                  # Set up some OS variables
+get_all_vars;              # Toss variables/status into hashes
+get_tuning_info;           # Get information about the tuning connection
+calculations;              # Calculate everything we need
+check_architecture;        # Suggest 64-bit upgrade
+check_storage_engines;     # Show enabled storage engines
+if ( $opt{'feature'} ne '' ) {
+    subheaderprint "See FEATURES.md for more information";
+    no strict 'refs';
+    for my $feature ( split /,/, $opt{'feature'} ) {
+        subheaderprint "Running feature: $opt{'feature'}";
+        $feature->();
+    }
+    make_recommendations;
+    exit(0);
+}
+validate_mysql_version;    # Check current MySQL version
+
+system_recommendations;    # Avoid too many services on the same host
+log_file_recommendations;  # check log file content
+
+check_metadata_perf;      # Show parameter impacting performance during analysis
+mysql_databases;          # Show information about databases
+mysql_tables;             # Show information about table column
+mysql_table_structures;   # Show information about table structures
+
+mysql_indexes;            # Show information about indexes
+mysql_views;              # Show information about views
+mysql_triggers;           # Show information about triggers
+mysql_routines;           # Show information about routines
+security_recommendations; # Display some security recommendations
+cve_recommendations;      # Display related CVE
+
+mysql_stats;              # Print the server stats
+mysql_pfs;                # Print Performance schema info
+
+mariadb_threadpool;       # Print MariaDB ThreadPool stats
+mysql_myisam;             # Print MyISAM stats
+mysql_innodb;             # Print InnoDB stats
+mariadb_aria;             # Print MariaDB Aria stats
+mariadb_tokudb;           # Print MariaDB Tokudb stats
+mariadb_xtradb;           # Print MariaDB XtraDB stats
+
+#mariadb_rockdb;           # Print MariaDB RockDB stats
+#mariadb_spider;           # Print MariaDB Spider stats
+#mariadb_connect;          # Print MariaDB Connect stats
+mariadb_galera;            # Print MariaDB Galera Cluster stats
+get_replication_status;    # Print replication info
+make_recommendations;      # Make recommendations based on stats
+dump_result;               # Dump result if debug is on
+close_outputfile;          # Close reportfile if needed
+
+# ---------------------------------------------------------------------------
+# END 'MAIN'
+# ---------------------------------------------------------------------------
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+ MySQLTuner 2.6.1 - MySQL High Performance Tuning Script
+
+=head1 IMPORTANT USAGE GUIDELINES
+
+To run the script with the default options, run the script without arguments
+Allow MySQL server to run for at least 24-48 hours before trusting suggestions
+Some routines may require root level privileges (script will provide warnings)
+You must provide the remote server's total memory when connecting to other servers
+
+=head1 CONNECTION AND AUTHENTICATION
+
+ --host <hostname>           Connect to a remote host to perform tests (default: localhost)
+ --socket <socket>           Use a different socket for a local connection
+ --port <port>               Port to use for connection (default: 3306)
+ --protocol tcp              Force TCP connection instead of socket
+ --user <username>           Username to use for authentication
+ --userenv <envvar>          Name of env variable which contains username to use for authentication
+ --pass <password>           Password to use for authentication
+ --passenv <envvar>          Name of env variable which contains password to use for authentication
+ --ssl-ca <path>             Path to public key
+ --mysqladmin <path>         Path to a custom mysqladmin executable
+ --mysqlcmd <path>           Path to a custom mysql executable
+ --defaults-file <path>      Path to a custom .my.cnf
+ --defaults-extra-file <path>      Path to an extra custom config file
+ --server-log <path>         Path to explicit log file (error_log)
+
+=head1 PERFORMANCE AND REPORTING OPTIONS
+
+ --skipsize                  Don't enumerate tables and their types/sizes (default: on)
+                             (Recommended for servers with many tables)
+ --json                      Print result as JSON string
+ --prettyjson                Print result as JSON formatted string
+ --skippassword              Don't perform checks on user passwords (default: off)
+ --checkversion              Check for updates to MySQLTuner (default: don't check)
+ --updateversion             Check for updates to MySQLTuner and update when newer version is available (default: don't check)
+ --forcemem <size>           Amount of RAM installed in megabytes
+ --forceswap <size>          Amount of swap memory configured in megabytes
+ --passwordfile <path>       Path to a password file list (one password by line)
+ --cvefile <path>            CVE File for vulnerability checks
+ --outputfile <path>         Path to a output txt file
+ --reportfile <path>         Path to a report txt file
+ --template   <path>         Path to a template file
+ --dumpdir <path>            Path to a directory where to dump information files
+ --feature <feature>         Run a specific feature (see FEATURES section)
+ --dumpdir <path>            information_schema tables and sys views are dumped in CSV in this path
+
+=head1 OUTPUT OPTIONS
+
+ --silent                    Don't output anything on screen
+ --verbose                   Print out all options (default: no verbose, dbstat, idxstat, sysstat, tbstat, pfstat)
+ --color                     Print output in color
+ --nocolor                   Don't print output in color
+ --noprettyicon              Print output with legacy tag [OK], [!!], [--], [CMD], ...
+ --nogood                    Remove OK responses
+ --nobad                     Remove negative/suggestion responses
+ --noinfo                    Remove informational responses
+ --debug                     Print debug information
+ --experimental              Print experimental analysis (may fail)
+ --nondedicated              Consider server is not dedicated to Db server usage only
+ --noprocess                 Consider no other process is running
+ --dbstat                    Print database information
+ --nodbstat                  Don't print database information
+ --tbstat                    Print table information
+ --notbstat                  Don't print table information
+ --colstat                   Print column information
+ --nocolstat                 Don't print column information
+ --idxstat                   Print index information
+ --noidxstat                 Don't print index information
+ --nomyisamstat              Don't print MyIsam information
+ --sysstat                   Print system information
+ --nosysstat                 Don't print system information
+ --nostructstat              Don't print table structures information
+ --pfstat                    Print Performance schema
+ --nopfstat                  Don't print Performance schema
+ --bannedports               Ports banned separated by comma (,)
+ --server-log                Define specific error_log to analyze
+ --maxportallowed            Number of open ports allowable on this host
+ --buffers                   Print global and per-thread buffer values
+
+=head1 PERLDOC
+
+You can find documentation for this module with the perldoc command.
+
+  perldoc mysqltuner
+
+=head2 INTERNALS
+
+L<https://github.com/major/MySQLTuner-perl/blob/master/INTERNALS.md>
+
+ Internal documentation
+
+=head1 AUTHORS
+
+Major Hayden - major@mhtx.net
+Jean-Marie Renouard - jmrenouard@gmail.com
+
+=head1 CONTRIBUTORS
+
+=over 4
+
+=item *
+
+Matthew Montgomery
+
+=item *
+
+Paul Kehrer
+
+=item *
+
+Dave Burgess
+
+=item *
+
+Jonathan Hinds
+
+=item *
+
+Mike Jackson
+
+=item *
+
+Nils Breunese
+
+=item *
+
+Shawn Ashlee
+
+=item *
+
+Luuk Vosslamber
+
+=item *
+
+Ville Skytta
+
+=item *
+
+Trent Hornibrook
+
+=item *
+
+Jason Gill
+
+=item *
+
+Mark Imbriaco
+
+=item *
+
+Greg Eden
+
+=item *
+
+Aubin Galinotti
+
+=item *
+
+Giovanni Bechis
+
+=item *
+
+Bill Bradford
+
+=item *
+
+Ryan Novosielski
+
+=item *
+
+Michael Scheidell
+
+=item *
+
+Blair Christensen
+
+=item *
+
+Hans du Plooy
+
+=item *
+
+Victor Trac
+
+=item *
+
+Everett Barnes
+
+=item *
+
+Tom Krouper
+
+=item *
+
+Gary Barrueto
+
+=item *
+
+Simon Greenaway
+
+=item *
+
+Adam Stein
+
+=item *
+
+Isart Montane
+
+=item *
+
+Baptiste M.
+
+=item *
+
+Cole Turner
+
+=item *
+
+Major Hayden
+
+=item *
+
+Joe Ashcraft
+
+=item *
+
+Jean-Marie Renouard
+
+=item *
+
+Stephan GroBberndt
+
+=item *
+
+Christian Loos
+
+=item *
+
+Long Radix
+
+=back
+
+=head1 SUPPORT
+
+
+Bug reports, feature requests, and downloads at http://mysqltuner.pl/
+
+Bug tracker can be found at https://github.com/major/MySQLTuner-perl/issues
+
+Maintained by Jean-Marie Renouard (jmrenouard\@gmail.com) - Licensed under GPL
+
+=head1 SOURCE CODE
+
+L<https://github.com/major/MySQLTuner-perl>
+
+ git clone https://github.com/major/MySQLTuner-perl.git
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2006-2023 Major Hayden - major@mhtx.net
+# Copyright (C) 2015-2023 Jean-Marie Renouard - jmrenouard@gmail.com
+
+For the latest updates, please visit http://mysqltuner.pl/
+
+Git repository available at https://github.com/major/MySQLTuner-perl
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+=cut
+
+# Local variables:
+# indent-tabs-mode: t
+# cperl-indent-level: 8
+# perl-indent-level: 8
+# End:
\ No newline at end of file
diff --git a/roles/services/files/scripts/promote-master.sh b/roles/services/files/scripts/promote-master.sh
new file mode 100755
index 0000000..d4b5b9f
--- /dev/null
+++ b/roles/services/files/scripts/promote-master.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+mysql -e "SET GLOBAL event_scheduler = OFF"
+
+echo "SELECT db, name FROM mysql.event WHERE status = 'SLAVESIDE_DISABLED'" | mysql --raw --silent | \
+awk '{
+	gsub("`", "``", $1);
+	gsub("`", "``", $2);
+	print "`"$1"`.`"$2"`";
+}' | \
+while read event; do
+	mysql -e "ALTER EVENT $event ENABLE"
+done
+
+mysql -e "SET GLOBAL event_scheduler = ON"
diff --git a/roles/services/files/scripts/promote-slave.sh b/roles/services/files/scripts/promote-slave.sh
new file mode 100755
index 0000000..4856f4f
--- /dev/null
+++ b/roles/services/files/scripts/promote-slave.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+mysql -e "SET GLOBAL event_scheduler = OFF"
+
+echo "SELECT db, name FROM mysql.event WHERE status = 'ENABLED'" | mysql --raw --silent | \
+awk '{
+	gsub("`", "``", $1);
+	gsub("`", "``", $2);
+	print "`"$1"`.`"$2"`";
+}' | \
+while read event; do
+	mysql -e "ALTER EVENT $event DISABLE ON SLAVE"
+done
+
+mysql -e "SET GLOBAL event_scheduler = ON"
+
diff --git a/roles/services/files/scripts/scheduler-log.sh b/roles/services/files/scripts/scheduler-log.sh
new file mode 100755
index 0000000..9cb0b37
--- /dev/null
+++ b/roles/services/files/scripts/scheduler-log.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+set -e
+
+logFile="/var/log/mysql/error.log"
+dateFile="/tmp/mysql_scheduler_log-lastdate"
+logTable="util.eventLog"
+purgeDays=30
+
+quote() {
+	local str=${1//\'/\'\'/}
+	local str=${str//\\/\\\\}
+	echo "'$str'"
+}
+
+mysql -e "SELECT TRUE" > /dev/null 2>&1
+if [ "$?" -ne "0" ]; then
+	exit
+fi
+
+if [ -f "$dateFile" ]; then
+	fromDate=$(cat "$dateFile")
+else
+	fromDate=0
+fi
+
+lastDate=$(tail -n1 "$logFile" | awk '{print $1" "$2}')
+toDate=$(date +%s -d "$lastDate")
+
+awk -v fromDate="$fromDate" -v toDate="$toDate" '{
+	split($1, date, "-");
+	split($2, time, ":");
+	timestamp = mktime(date[1]" "date[2]" "date[3]" "time[1]" "time[2]" "time[3])
+	if (timestamp >= fromDate && timestamp < toDate && $4" "$5" "$6 == "[ERROR] Event Scheduler:") {
+		printf $1" "$2" "$7;
+		for (i=8; i<=NF; i++) printf FS $i ;
+		print "";
+	}
+}' "$logFile" | \
+\
+while read line; do
+	date="$(echo "$line" | cut -d' ' -f1,2)"
+	event="$(echo "$line" | cut -d' ' -f3)"
+	error="$(echo "$line" | cut -d' ' -f4-)"
+	echo "INSERT INTO $logTable (date, event, error)" \
+		"VALUES ($(quote "$date"), $(quote "$event"), $(quote "$error"))" | mysql
+done
+
+echo -n "$toDate" > "$dateFile"
+echo "DELETE FROM $logTable WHERE date < TIMESTAMPADD(DAY, -$purgeDays, NOW())" | mysql
diff --git a/roles/services/files/scripts/sync-conf.sh b/roles/services/files/scripts/sync-conf.sh
new file mode 100755
index 0000000..3597234
--- /dev/null
+++ b/roles/services/files/scripts/sync-conf.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+partner=root@db2.static.verdnatura.es
+confDir=/etc/mysql/mariadb.conf.d
+files=(
+	z90-vn.cnf
+	z95-production.cnf
+)
+
+#echo "Reloading service."
+#service mariadb reload
+
+if [ $? -eq "0" ]; then
+	echo "Synchronizing partner configuration."
+	for file in "${files[@]}"; do 
+        	scp "$confDir/$file" $partner:$confDir
+	done
+
+	#echo "Reloading partner service."
+	#ssh $partner service mariadb reload
+fi
diff --git a/roles/services/files/z90-vn.cnf b/roles/services/files/z90-vn.cnf
new file mode 100644
index 0000000..0f6a716
--- /dev/null
+++ b/roles/services/files/z90-vn.cnf
@@ -0,0 +1,119 @@
+[mysqld]
+# Docs: https://mariadb.com/kb/en/server-system-variables
+
+lc_messages                                 = es_ES
+lc_time_names                               = es_ES
+character-set-server                        = utf8
+collation-server                            = utf8_unicode_ci
+explicit_defaults_for_timestamp             = ON
+datadir                                     = /var/lib/mysql
+tmpdir                                      = /mnt/mysqltmp
+log_bin_trust_function_creators             = 1
+sql_mode                                    = NO_ENGINE_SUBSTITUTION
+bind-address                                = 0.0.0.0
+max_password_errors                         = 50
+
+#++++++++++++++++++++++++++++++++++++++++ Threads
+
+thread_stack                                = 512K
+join_buffer_size                            = 2M
+sort_buffer_size                            = 4M
+net_buffer_length                           = 256K
+max_allowed_packet                          = 16M
+read_buffer_size                            = 1M
+read_rnd_buffer_size                        = 512K
+
+#++++++++++++++++++++++++++++++++++++++++ Performance
+
+thread_cache_size                           = 450
+interactive_timeout                         = 1800
+wait_timeout                                = 1800
+open_files_limit                            = 20000
+low_priority_updates                        = 1
+table_open_cache                            = 40000
+table_definition_cache                      = 10000
+table_open_cache_instances                  = 1
+key_buffer_size                             = 256K
+max_heap_table_size                         = 128M
+tmp_table_size                              = 128M
+concurrent_insert                           = ALWAYS
+group_concat_max_len                        = 10000
+max_connect_errors                          = 50
+
+#++++++++++++++++++++++++++++++++++++++++ Binary log
+
+log-bin                                     = /mnt/mysqlbin/binlog/bin.log
+max_binlog_size                             = 1GB
+binlog_cache_size                           = 16M
+binlog_stmt_cache_size                      = 16M
+binlog_row_image                            = noblob
+binlog_format                               = row
+relay_log                                   = /mnt/mysqlbin/binlog/relay.log
+
+binlog-ignore-db                            = tmp
+binlog-ignore-db                            = PERCONA_SCHEMA
+
+#++++++++++++++++++++++++++++++++++++++++ Replication
+
+event-scheduler                             = ON
+slave_exec_mode                             = STRICT
+
+replicate-ignore-db                         = tmp
+replicate-ignore-table                      = util.eventLog
+replicate-ignore-table                      = cache.cache_calc
+replicate-ignore-table                      = cache.available
+replicate-ignore-table                      = cache.availableNoRaids
+replicate-ignore-table                      = cache.cache_valid
+replicate-ignore-table                      = cache.stock
+replicate-ignore-table                      = cache.visible
+
+#++++++++++++++++++++++++++++++++++++++++ InnoDB
+
+transaction-isolation                       = READ-COMMITTED
+idle_transaction_timeout                    = 60
+innodb_io_capacity                          = 100
+innodb_io_capacity_max                      = 100
+innodb_monitor_enable                       = all
+innodb_read_io_threads                      = 16
+innodb_write_io_threads                     = 16
+innodb_checksum_algorithm                   = crc32
+innodb_adaptive_hash_index                  = 0
+innodb_flush_method                         = O_DIRECT
+innodb_log_buffer_size                      = 32M
+innodb_log_file_size                        = 8G
+innodb_purge_threads                        = 4
+innodb_buffer_pool_dump_at_shutdown         = ON
+innodb_buffer_pool_load_at_startup          = ON
+
+#++++++++++++++++++++++++++++++++++++++++ Logging
+
+log_error                                   = /var/log/mysql/error.log
+log_warnings                                = 1
+log_output                                  = TABLE
+general_log                                 = OFF
+slow_query_log                              = ON
+long_query_time                             = 2
+min_examined_row_limit                      = 0
+log_slow_admin_statements                   = ON
+log_queries_not_using_indexes               = OFF
+max_error_count                             = 100
+
+#++++++++++++++++++++++++++++++++++++++++ SSL
+
+ssl-ca                                      = /etc/mysql/ca.pem
+ssl-cert                                    = /etc/mysql/cert.pem
+ssl-key                                     = /etc/mysql/key.pem
+
+#++++++++++++++++++++++++++++++++++++++++ Query cache
+
+query_cache_limit                           = 0
+query_cache_type                            = OFF
+query_cache_size                            = 0
+
+#++++++++++++++++++++++++++++++++++++++++ Performance Schema
+
+performance_schema                                         = ON
+performance_schema_digests_size                            = 20000
+performance-schema-consumer-events-statements-history      = ON
+performance_schema_consumer_events_statements_history_long = ON
+userstat                                                   = ON
diff --git a/roles/services/files/z95-production.cnf b/roles/services/files/z95-production.cnf
new file mode 100644
index 0000000..17f9732
--- /dev/null
+++ b/roles/services/files/z95-production.cnf
@@ -0,0 +1,6 @@
+[mysqld]
+
+port                                        = 3306
+max_connections                             = 1000
+expire_logs_days                            = 7
+innodb_buffer_pool_size                     = 64G
diff --git a/roles/services/files/z99-local.cnf b/roles/services/files/z99-local.cnf
new file mode 100644
index 0000000..58249ee
--- /dev/null
+++ b/roles/services/files/z99-local.cnf
@@ -0,0 +1,7 @@
+[mysqld]
+
+server-id                                   = 1
+#bind-address                               = 127.0.0.1
+#event-scheduler                            = OFF
+#skip-log-bin
+#skip-slave-start
diff --git a/roles/services/handlers/main.yml b/roles/services/handlers/main.yml
new file mode 100644
index 0000000..77780d8
--- /dev/null
+++ b/roles/services/handlers/main.yml
@@ -0,0 +1,13 @@
+- name: restart-chrony 
+  systemd:
+    name: chrony
+    state: restarted
+- name: reload systemd
+  command:
+    cmd: systemctl daemon-reload
+- name: restart-mariadb
+  systemd:
+    name: mariadb
+    state: restarted
+
+
diff --git a/roles/services/tasks/adsamba.yml b/roles/services/tasks/adsamba.yml
new file mode 100644
index 0000000..fe8b880
--- /dev/null
+++ b/roles/services/tasks/adsamba.yml
@@ -0,0 +1,49 @@
+- name: Install adSamba packages
+  package:
+    name: "{{ dcsamba_base_packages }}"
+    state: present
+    install_recommends: no
+
+- name: Add adsamba host to hosts file
+  blockinfile:
+    path: /etc/hosts
+    marker: "# {mark} ANSIBLE-MANAGED SAMBA DC ENTRY"
+    block: |
+      {{ dc1 }} dc1-test.samba-test.{{ resolv_domain }}
+
+- name: Disable Samba client services and mask them
+  systemd:
+    name: "{{ item }}"
+    state: stopped
+    enabled: no
+    masked: yes
+  loop: "{{ samba_client_services }}"
+
+- name: Check if server is already joined to domain
+  command:
+    cmd: samba-tool domain info localhost
+  register: domain_info
+  failed_when: domain_info.rc != 0 and 'Cannot contact' not in domain_info.stderr
+  changed_when: false
+
+- name: Join domain as DC if not already joined
+  command:
+    cmd: samba-tool domain join samba."{{ resolv_domain }}" DC -U"SAMBA\\administrator" --option='idmap_ldb:use rfc2307 = yes'
+  when: "'Cannot contact' in domain_info.stderr"
+  register: domain_join
+  changed_when: "'Joined domain' in domain_join.stdout"
+
+- name: Copy Kerberos configuration
+  copy:
+    src: krb5.conf
+    dest: /etc/krb5.conf
+    remote_src: true
+    owner: root
+    group: root
+    mode: '0644'
+
+- name: Enable and start Samba AD DC service
+  systemd:
+    name: samba-ad-dc
+    state: started
+    enabled: yes
diff --git a/roles/services/tasks/main.yml b/roles/services/tasks/main.yml
new file mode 100644
index 0000000..abb6d16
--- /dev/null
+++ b/roles/services/tasks/main.yml
@@ -0,0 +1,6 @@
+- import_tasks: timeserver.yml
+  tags: timeserver
+- import_tasks: mariadb.yml
+  tags: mariadb
+- import_tasks: adsamba.yml
+  tags: adsamba
diff --git a/roles/services/tasks/mariadb.yml b/roles/services/tasks/mariadb.yml
new file mode 100644
index 0000000..f9ec533
--- /dev/null
+++ b/roles/services/tasks/mariadb.yml
@@ -0,0 +1,109 @@
+# Review /root/scripts/check-memory.sh --> It's not optimal to do what this program does
+# Also review the cron task /root/scripts/scheduler-log.sh
+
+- name: Ensure Install requirements for MariaDB repository setup script
+  apt:
+    name: "{{ mariadb_requeriments }}"
+    state: present
+    install_recommends: no
+
+- name: Download required setup files
+  get_url:
+    url: "{{ item.url }}"
+    dest: "{{ item.dest }}"
+    mode: "{{ item.mode }}"
+  loop: "{{ downloads }}"
+
+- name: Run MariaDB repository setup script
+  command:
+    cmd: "/bin/bash /tmp/mariadb_repo_setup --mariadb-server-version=10.11.10"
+    creates: "/etc/apt/sources.list.d/mariadb.list"
+
+- name: Install Percona repository package
+  apt:
+    deb: "/tmp/percona-release_latest.generic_all.deb"
+    state: present
+    install_recommends: no
+
+- name: Install MariaDB packages
+  apt:
+    name: "{{ mariadb_base_packages }}"
+    state: present
+    install_recommends: no
+
+- name: Ensure required directories exist
+  file:
+    path: "{{ item.path }}"
+    state: directory
+    owner: "{{ item.owner }}"
+    group: "{{ item.group }}"
+    mode: "{{ item.mode }}"
+  loop: "{{ required_directories }}"
+
+- name: Ensure required custom and Mariabackup files are copied to their destinations and root scripts
+  copy:
+    src: "{{ item.src }}"
+    dest: "{{ item.dest }}"
+    owner: root
+    group: root
+    mode: "{{ item.mode }}"
+  loop: "{{ required_files_and_mariabackup_files_and_root_scripts }}"
+  notify: reload systemd
+
+- name: Add tmpfs in /etc/fstab
+  blockinfile:
+    path: /etc/fstab
+    marker: "# {mark} ANSIBLE-MANAGED TMPFS ENTRY"
+    block: |
+      tmpfs /mnt/mysqltmp         tmpfs rw,size=6144M         0 0
+  register: fstab
+
+- name: Mount all filesystems from /etc/fstab
+  command: mount -a
+  when: fstab.changed
+
+- name: Set MariaDB Cron to /etc/cron.d
+  template:
+    src: templates/cron_mariadb
+    dest: /etc/cron.d/vn
+    owner: root
+    group: root
+    mode: u=rw,g=r,o=r
+
+- name: Insert MySQL certificates
+  copy:
+    content: "{{ item.content }}"
+    dest: "{{ item.dest }}"
+    owner: mysql
+    group: mysql
+    mode: "{{ item.mode }}"
+  loop: "{{ certificates }}"
+  notify: restart-mariadb
+
+- name: Configure MySQL master cert
+  copy:
+    content: "{{ lookup(passbolt, 'private_mysql', folder_parent_id=passbolt_folder).description }}"
+    dest: /etc/mysql/key.pem
+    owner: mysql
+    group: mysql
+    mode: u=rw,g=,o=
+
+- name: Set MariaDB custom configuration
+  copy:
+    src: "{{ item }}"
+    dest: /etc/mysql/mariadb.conf.d/
+    owner: root
+    group: root
+    mode: u=rw,g=r,o=r
+  with_fileglob:
+      - "files/z9*.cnf"
+  notify: restart-mariadb
+
+- name: Reminder to check mount points
+  debug:
+    msg: |
+      Remember to check the following mount points:
+      - /var/lib/mysql
+      - /mnt/mysqlbin
+      - /mnt/local-backup
+      Make sure they are correctly configured and accessible.
diff --git a/roles/services/tasks/timeserver.yml b/roles/services/tasks/timeserver.yml
new file mode 100644
index 0000000..5f3d850
--- /dev/null
+++ b/roles/services/tasks/timeserver.yml
@@ -0,0 +1,15 @@
+- name: Install CHRONY packages
+  apt:
+    name: chrony
+    state: present
+    install_recommends: no
+- name: Set CHRONY generic configuration
+  template:
+    src: timeserver_custom.conf
+    dest: /etc/chrony/conf.d/custom.conf
+    owner: root
+    group: root
+    mode: u=rw,g=r,o=r
+  notify: restart-chrony
+
+
diff --git a/roles/services/templates/cron_mariadb b/roles/services/templates/cron_mariadb
new file mode 100644
index 0000000..bc281bd
--- /dev/null
+++ b/roles/services/templates/cron_mariadb
@@ -0,0 +1,4 @@
+MAILTO="{{ sysadmin_mail }}"
+
+*/15 *  * * *  root  /root/scripts/check-memory.sh
+*/30 *  * * *  root  /root/scripts/scheduler-log.sh
diff --git a/roles/services/templates/timeserver_custom.conf b/roles/services/templates/timeserver_custom.conf
new file mode 100644
index 0000000..8bcd532
--- /dev/null
+++ b/roles/services/templates/timeserver_custom.conf
@@ -0,0 +1,17 @@
+# vendor zone
+{{ timeserver_vendor_zone }}
+
+# Logging
+log tracking statistics
+
+# will serve as local NTP server
+local stratum 10 
+
+# restrict clients from your subnets
+allow {{ timeserver_restrict_clients_zone }}
+
+# in case this server lost INTERNET connection
+local stratum 10
+
+# in case you wanna to broadcat time in your subnet
+{{ timeserver_broadcat_subnet }}
diff --git a/scripts/backup_pve.sh b/scripts/backup_pve.sh
new file mode 100644
index 0000000..326058a
--- /dev/null
+++ b/scripts/backup_pve.sh
@@ -0,0 +1,148 @@
+#!/bin/bash
+# Script to automate Proxmox PVE node backups to a PBS machine.
+#
+# Author: Xavi Lleó
+# Copyright (c) 2025 Verdnatura S.L. All rights reserved.
+# Version: 1.0.3
+# ¿Juan Wants add GPL License?
+#
+# A configuration file is required in the user's home directory who runs this command.
+# The file should be sourced using CONFIG_FILE before execution.
+#
+# Example of a configuration file:
+# 
+# Default values
+# USER_API="root@pam!api"
+# USER="root@pam"
+# IP_PBS="192.168.1.250"
+# POOL="backup-pool"
+# BACKUP_ITEMS="etc-pve.pxar:/etc/pve,interfaces.pxar:/etc/network"
+# LOG_FILE="/var/log/proxmox-backup-node-pve.log"
+# KEY_FILE="mykeyfile.key" #In case you want encrypted backups
+# PBS_PASSWORD='mypass or api token'
+# PBS_FINGERPRINT='b0:69:24:75:f0:92:a2:72:37:7c:c1:cb:0d:ba:8e:14:EE:XX:AA:MM:PP:LL:EE:e4:2b:07:02:18:86:9a:df:45'
+#
+# If you prefer to use switches in a one-liner, refer to the help section (--help) for available options.
+# Remember to add the port after the IP address when using an API user for authentication.
+# Example: IP_PBS="192.168.1.250:8007"
+
+
+CONFIG_FILE="$HOME/.backup_config.conf"
+
+# https://pbs.proxmox.com/docs/backup-client.html#environment-variables
+export PBS_PASSWORD
+export PBS_FINGERPRINT
+
+if [ -f "$CONFIG_FILE" ]; then
+    source "$CONFIG_FILE"
+else
+    echo "Error: Configuration file not found at $CONFIG_FILE"
+    exit 1
+fi
+
+show_help() {
+    echo "Usage: $0 [options]"
+    echo
+    echo "Options:"
+    echo "  --standard       Perform a standard backup."
+    echo "  --encrypt        Perform an encrypted backup (requires a key file)."
+    echo "  --ip             Repository IP address (overrides configuration)."
+    echo "  --pool           Name of the backup pool (overrides configuration)."
+    echo "  --items          List of backup items in 'name1:source1,name2:source2' format."
+    echo "  --user-api       Specify user API credentials for backup."
+    echo "  --user           Specify user credentials for backup."
+    echo "  --help           Show this help."
+    exit 0
+}
+
+exit_from_repo() {
+    proxmox-backup-client logout --repository "$REPOSITORY" 2>>"$LOG_FILE" && echo "$(date '+%Y-%m-%d %H:%M:%S') - Logged out from repository $REPOSITORY" | tee -a "$LOG_FILE"
+}
+
+# Check if PBS_PASSWORD and PBS_FINGERPRINT are set
+if [ -z "$PBS_PASSWORD" ] || [ -z "$PBS_FINGERPRINT" ]; then
+    echo "Error: PBS_PASSWORD or PBS_FINGERPRINT is not set."
+    exit 1
+fi
+
+while [[ $# -gt 0 ]]; do
+    case "$1" in
+        --standard)
+            MODE="standard"
+            ;;
+        --encrypt)
+            MODE="encrypt"
+            ;;
+        --ip)
+            IP_PBS="$2"
+            shift
+            ;;
+        --pool)
+            POOL="$2"
+            shift
+            ;;
+        --items)
+            BACKUP_ITEMS="$2"
+            shift
+            ;;
+        --user-api)
+            USER_API="$2"
+            shift
+            ;;
+        --user)
+            USER="$2"
+            shift
+            ;;
+        --help)
+            show_help
+            ;;
+        *)
+            echo "Error: Unrecognized option '$1'"
+            show_help
+            ;;
+    esac
+    shift
+done
+
+if [ -z "$MODE" ]; then
+    echo "You must specify --standard or --encrypt."
+    show_help
+fi
+
+if [ -n "$USER_API" ]; then
+    REPOSITORY="$USER_API@$IP_PBS:$POOL"
+else
+    REPOSITORY="$USER@$IP_PBS:$POOL"
+fi
+
+echo "$(date '+%Y-%m-%d %H:%M:%S') - Starting backup to repository $REPOSITORY" | tee -a "$LOG_FILE"
+for item in $(echo "$BACKUP_ITEMS" | tr ',' '\n'); do
+    BACKUP_NAME=$(echo "$item" | cut -d':' -f1)
+    TARGET_DIR=$(echo "$item" | cut -d':' -f2)
+    
+    if [ "$MODE" == "encrypt" ]; then
+        if [ ! -f "$KEY_FILE" ]; then
+            echo "The key file $KEY_FILE does not exist." | tee -a "$LOG_FILE"
+            exit 1
+        fi
+        
+        proxmox-backup-client backup "$BACKUP_NAME:$TARGET_DIR" --repository "$REPOSITORY" --crypt-mode encrypt --keyfile "$KEY_FILE" --backup-type 'host' 2>>"$LOG_FILE"
+    else
+        proxmox-backup-client backup "$BACKUP_NAME:$TARGET_DIR" --repository "$REPOSITORY" --backup-type 'host' 2>>"$LOG_FILE"
+    fi
+
+    if [ $? -ne 0 ]; then
+        echo "Backup failed for $BACKUP_NAME" | tee -a "$LOG_FILE"
+        exit 1
+    fi
+    sleep 5
+done
+
+if [ $? -eq 0 ]; then
+    exit_from_repo
+    echo -e "$(date '+%Y-%m-%d %H:%M:%S') - Backup completed successfully. You can check the log at $LOG_FILE\n" | tee -a "$LOG_FILE"
+else
+    exit_from_repo
+    echo -e "$(date '+%Y-%m-%d %H:%M:%S') - Error during backup. You can check the log at $LOG_FILE\n" | tee -a "$LOG_FILE"
+    exit 1
+fi