Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * MySQL table row compression tool tool.
  19   *
  20   * @package   core
  21   * @copyright 2014 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   * @author    Petr Skoda <petr.skoda@totaralms.com>
  24   */
  25  
  26  define('CLI_SCRIPT', true);
  27  
  28  require(__DIR__.'/../../config.php');
  29  require_once($CFG->libdir . '/clilib.php');
  30  
  31  if ($DB->get_dbfamily() !== 'mysql') {
  32      cli_error('This script is used for MySQL databases only.');
  33  }
  34  
  35  $engine = strtolower($DB->get_dbengine());
  36  if ($engine !== 'innodb' and $engine !== 'xtradb') {
  37      cli_error('This script is for MySQL servers using InnoDB or XtraDB engines only.');
  38  }
  39  
  40  list($options, $unrecognized) = cli_get_params(
  41      array('help' => false, 'info' => false, 'list' => false, 'fix' => false, 'showsql' => false),
  42      array('h' => 'help', 'i' => 'info', 'l' => 'list', 'f' => 'fix', 's' => 'showsql')
  43  );
  44  
  45  if ($unrecognized) {
  46      $unrecognized = implode("\n  ", $unrecognized);
  47      cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
  48  }
  49  
  50  $help =
  51      "Script for detection of row size problems in MySQL InnoDB tables.
  52  
  53  By default InnoDB storage table is using legacy Antelope file format
  54  which has major restriction on database row size.
  55  Use this script to detect and fix database tables with potential data
  56  overflow problems.
  57  
  58  Options:
  59  -i, --info            Show database information
  60  -l, --list            List problematic tables
  61  -f, --fix             Attempt to fix all tables (requires SUPER privilege)
  62  -s, --showsql         Print SQL statements for fixing of tables
  63  -h, --help            Print out this help
  64  
  65  Example:
  66  \$ sudo -u www-data /usr/bin/php admin/cli/mysql_compressed_rows.php -l
  67  ";
  68  
  69  /** @var mysql_sql_generator $generator */
  70  $generator = $DB->get_manager()->generator;
  71  
  72  $info = $DB->get_server_info();
  73  $filepertable = $DB->get_record_sql("SHOW VARIABLES LIKE 'innodb_file_per_table'");
  74  $filepertable = $filepertable ? $filepertable->value : '';
  75  $fileformat = $DB->get_record_sql("SHOW VARIABLES LIKE 'innodb_file_format'");
  76  $fileformat = $fileformat ? $fileformat->value : '';
  77  $prefix = $DB->get_prefix();
  78  $database = $CFG->dbname;
  79  
  80  if (!empty($options['info'])) {
  81      echo "Database version:      " . $info['description'] . "\n";
  82      echo "Database name:         $database\n";
  83      echo "Database engine:       " . $DB->get_dbengine() . "\n";
  84      echo "innodb_file_per_table: $filepertable\n";
  85      echo "innodb_file_format:    $fileformat\n";
  86  
  87      exit(0);
  88  
  89  } else if (!empty($options['list'])) {
  90      $problem = false;
  91      foreach ($DB->get_tables(false) as $table) {
  92          $columns = $DB->get_columns($table, false);
  93          $size = $generator->guess_antelope_row_size($columns);
  94          $format = $DB->get_row_format($table);
  95          if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
  96              continue;
  97          }
  98  
  99          echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT);
 100          echo str_pad($format, 11, ' ', STR_PAD_RIGHT);
 101  
 102          if ($format === 'Compact' or $format === 'Redundant') {
 103              $problem = true;
 104              echo " (needs fixing)\n";
 105  
 106          } else if ($format !== 'Compressed' and $format !== 'Dynamic') {
 107              echo " (unknown)\n";
 108  
 109          } else {
 110              echo "\n";
 111          }
 112      }
 113  
 114      if ($problem) {
 115          exit(1);
 116      }
 117      exit(0);
 118  
 119  } else if (!empty($options['fix'])) {
 120      $fixtables = array();
 121      foreach ($DB->get_tables(false) as $table) {
 122          $columns = $DB->get_columns($table, false);
 123          $size = $generator->guess_antelope_row_size($columns);
 124          $format = $DB->get_row_format($table);
 125          if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
 126              continue;
 127          }
 128          if ($format === 'Compact' or $format === 'Redundant') {
 129              $fixtables[$table] = $table;
 130          }
 131      }
 132  
 133      if (!$fixtables) {
 134          echo "No changes necessary\n";
 135          exit(0);
 136      }
 137  
 138      if ($filepertable !== 'ON') {
 139          try {
 140              $DB->execute("SET GLOBAL innodb_file_per_table=1");
 141          } catch (dml_exception $e) {
 142              echo "Cannot enable GLOBAL innodb_file_per_table setting, use --showsql option and execute the statements manually.";
 143              throw $e;
 144          }
 145      }
 146      if ($fileformat !== 'Barracuda') {
 147          try {
 148              $DB->execute("SET GLOBAL innodb_file_format=Barracuda");
 149          } catch (dml_exception $e) {
 150              echo "Cannot change GLOBAL innodb_file_format setting, use --showsql option and execute the statements manually.";
 151              throw $e;
 152          }
 153      }
 154  
 155      if (!$DB->is_compressed_row_format_supported(false)) {
 156          echo "MySQL server is not compatible with compressed row format.";
 157          exit(1);
 158      }
 159  
 160      foreach ($fixtables as $table) {
 161          $DB->change_database_structure("ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed");
 162          echo str_pad($prefix . $table, 32, ' ', STR_PAD_RIGHT) . " ... Compressed\n";
 163      }
 164  
 165      exit(0);
 166  
 167  } else if (!empty($options['showsql'])) {
 168      $fixtables = array();
 169  
 170      foreach ($DB->get_tables(false) as $table) {
 171          $columns = $DB->get_columns($table, false);
 172          $size = $generator->guess_antelope_row_size($columns);
 173          $format = $DB->get_row_format($table);
 174          if ($size <= $generator::ANTELOPE_MAX_ROW_SIZE) {
 175              continue;
 176          }
 177          if ($format === 'Compact' or $format === 'Redundant') {
 178              $fixtables[$table] = $table;
 179          }
 180      }
 181      if (!$fixtables) {
 182          echo "No changes necessary\n";
 183          exit(0);
 184      }
 185  
 186      echo "Copy the following SQL statements and execute them using account with SUPER privilege:\n\n";
 187      echo "USE $database;\n";
 188      echo "SET SESSION sql_mode=STRICT_ALL_TABLES;\n";
 189      echo "SET GLOBAL innodb_file_per_table=1;\n";
 190      echo "SET GLOBAL innodb_file_format=Barracuda;\n";
 191      foreach ($fixtables as $table) {
 192          echo "ALTER TABLE `{$prefix}$table` ROW_FORMAT=Compressed;\n";
 193      }
 194      echo "\n";
 195      exit(0);
 196  
 197  } else {
 198      echo $help;
 199      die;
 200  }
 201  
 202