| 1 | efrain | 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 |   |