Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   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 specific SQL code generator.
  19   *
  20   * @package    core_ddl
  21   * @copyright  1999 onwards Martin Dougiamas     http://dougiamas.com
  22   *             2001-3001 Eloy Lafuente (stronk7) http://contiento.com
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once($CFG->libdir.'/ddl/sql_generator.php');
  29  
  30  /**
  31   * This class generate SQL code to be used against MySQL
  32   * It extends XMLDBgenerator so everything can be
  33   * overridden as needed to generate correct SQL.
  34   *
  35   * @property mysqli_native_moodle_database $mdb
  36   *
  37   * @package    core_ddl
  38   * @copyright  1999 onwards Martin Dougiamas     http://dougiamas.com
  39   *             2001-3001 Eloy Lafuente (stronk7) http://contiento.com
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class mysql_sql_generator extends sql_generator {
  43  
  44      // Only set values that are different from the defaults present in XMLDBgenerator
  45  
  46      /** @var string Used to quote names. */
  47      public $quote_string = '`';
  48  
  49      /** @var string To define the default to set for NOT NULLs CHARs without default (null=do nothing).*/
  50      public $default_for_char = '';
  51  
  52      /** @var bool To specify if the generator must use some DEFAULT clause to drop defaults.*/
  53      public $drop_default_value_required = true;
  54  
  55      /** @var string The DEFAULT clause required to drop defaults.*/
  56      public $drop_default_value = null;
  57  
  58      /** @var string To force primary key names to one string (null=no force).*/
  59      public $primary_key_name = '';
  60  
  61      /** @var string Template to drop PKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
  62      public $drop_primary_key = 'ALTER TABLE TABLENAME DROP PRIMARY KEY';
  63  
  64      /** @var string Template to drop UKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
  65      public $drop_unique_key = 'ALTER TABLE TABLENAME DROP KEY KEYNAME';
  66  
  67      /** @var string Template to drop FKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
  68      public $drop_foreign_key = 'ALTER TABLE TABLENAME DROP FOREIGN KEY KEYNAME';
  69  
  70      /** @var bool True if the generator needs to add extra code to generate the sequence fields.*/
  71      public $sequence_extra_code = false;
  72  
  73      /** @var string The particular name for inline sequences in this generator.*/
  74      public $sequence_name = 'auto_increment';
  75  
  76      public $add_after_clause = true; // Does the generator need to add the after clause for fields
  77  
  78      /** @var string Characters to be used as concatenation operator.*/
  79      public $concat_character = null;
  80  
  81      /** @var string The SQL template to alter columns where the 'TABLENAME' and 'COLUMNSPECS' keywords are dynamically replaced.*/
  82      public $alter_column_sql = 'ALTER TABLE TABLENAME MODIFY COLUMN COLUMNSPECS';
  83  
  84      /** @var string SQL sentence to drop one index where 'TABLENAME', 'INDEXNAME' keywords are dynamically replaced.*/
  85      public $drop_index_sql = 'ALTER TABLE TABLENAME DROP INDEX INDEXNAME';
  86  
  87      /** @var string SQL sentence to rename one index where 'TABLENAME', 'OLDINDEXNAME' and 'NEWINDEXNAME' are dynamically replaced.*/
  88      public $rename_index_sql = null;
  89  
  90      /** @var string SQL sentence to rename one key 'TABLENAME', 'OLDKEYNAME' and 'NEWKEYNAME' are dynamically replaced.*/
  91      public $rename_key_sql = null;
  92  
  93      /** Maximum size of InnoDB row in Antelope file format */
  94      const ANTELOPE_MAX_ROW_SIZE = 8126;
  95  
  96      /**
  97       * Reset a sequence to the id field of a table.
  98       *
  99       * @param xmldb_table|string $table name of table or the table object.
 100       * @return array of sql statements
 101       */
 102      public function getResetSequenceSQL($table) {
 103  
 104          if ($table instanceof xmldb_table) {
 105              $tablename = $table->getName();
 106          } else {
 107              $tablename = $table;
 108          }
 109  
 110          // From http://dev.mysql.com/doc/refman/5.0/en/alter-table.html
 111          $value = (int)$this->mdb->get_field_sql('SELECT MAX(id) FROM {'.$tablename.'}');
 112          $value++;
 113          return array("ALTER TABLE $this->prefix$tablename AUTO_INCREMENT = $value");
 114      }
 115  
 116      /**
 117       * Calculate proximate row size when using InnoDB
 118       * tables in Antelope row format.
 119       *
 120       * Note: the returned value is a bit higher to compensate for
 121       *       errors and changes of column data types.
 122       *
 123       * @deprecated since Moodle 2.9 MDL-49723 - please do not use this function any more.
 124       */
 125      public function guess_antolope_row_size(array $columns) {
 126          throw new coding_exception('guess_antolope_row_size() can not be used any more, please use guess_antelope_row_size() instead.');
 127      }
 128  
 129      /**
 130       * Calculate proximate row size when using InnoDB tables in Antelope row format.
 131       *
 132       * Note: the returned value is a bit higher to compensate for errors and changes of column data types.
 133       *
 134       * @param xmldb_field[]|database_column_info[] $columns
 135       * @return int approximate row size in bytes
 136       */
 137      public function guess_antelope_row_size(array $columns) {
 138  
 139          if (empty($columns)) {
 140              return 0;
 141          }
 142  
 143          $size = 0;
 144          $first = reset($columns);
 145  
 146          if (count($columns) > 1) {
 147              // Do not start with zero because we need to cover changes of field types and
 148              // this calculation is most probably not be accurate.
 149              $size += 1000;
 150          }
 151  
 152          if ($first instanceof xmldb_field) {
 153              foreach ($columns as $field) {
 154                  switch ($field->getType()) {
 155                      case XMLDB_TYPE_TEXT:
 156                          $size += 768;
 157                          break;
 158                      case XMLDB_TYPE_BINARY:
 159                          $size += 768;
 160                          break;
 161                      case XMLDB_TYPE_CHAR:
 162                          $bytes = $field->getLength() * 3;
 163                          if ($bytes > 768) {
 164                              $bytes = 768;
 165                          }
 166                          $size += $bytes;
 167                          break;
 168                      default:
 169                          // Anything else is usually maximum 8 bytes.
 170                          $size += 8;
 171                  }
 172              }
 173  
 174          } else if ($first instanceof database_column_info) {
 175              foreach ($columns as $column) {
 176                  switch ($column->meta_type) {
 177                      case 'X':
 178                          $size += 768;
 179                          break;
 180                      case 'B':
 181                          $size += 768;
 182                          break;
 183                      case 'C':
 184                          $bytes = $column->max_length * 3;
 185                          if ($bytes > 768) {
 186                              $bytes = 768;
 187                          }
 188                          $size += $bytes;
 189                          break;
 190                      default:
 191                          // Anything else is usually maximum 8 bytes.
 192                          $size += 8;
 193                  }
 194              }
 195          }
 196  
 197          return $size;
 198      }
 199  
 200      /**
 201       * Given one correct xmldb_table, returns the SQL statements
 202       * to create it (inside one array).
 203       *
 204       * @param xmldb_table $xmldb_table An xmldb_table instance.
 205       * @return array An array of SQL statements, starting with the table creation SQL followed
 206       * by any of its comments, indexes and sequence creation SQL statements.
 207       */
 208      public function getCreateTableSQL($xmldb_table) {
 209          // First find out if want some special db engine.
 210          $engine = $this->mdb->get_dbengine();
 211          // Do we know collation?
 212          $collation = $this->mdb->get_dbcollation();
 213  
 214          // Do we need to use compressed format for rows?
 215          $rowformat = "";
 216          $size = $this->guess_antelope_row_size($xmldb_table->getFields());
 217          if ($size > self::ANTELOPE_MAX_ROW_SIZE) {
 218              if ($this->mdb->is_compressed_row_format_supported()) {
 219                  $rowformat = "\n ROW_FORMAT=Compressed";
 220              }
 221          }
 222  
 223          $utf8mb4rowformat = $this->mdb->get_row_format_sql($engine, $collation);
 224          $rowformat = ($utf8mb4rowformat == '') ? $rowformat : $utf8mb4rowformat;
 225  
 226          $sqlarr = parent::getCreateTableSQL($xmldb_table);
 227  
 228          // This is a very nasty hack that tries to use just one query per created table
 229          // because MySQL is stupidly slow when modifying empty tables.
 230          // Note: it is safer to inject everything on new lines because there might be some trailing -- comments.
 231          $sqls = array();
 232          $prevcreate = null;
 233          $matches = null;
 234          foreach ($sqlarr as $sql) {
 235              if (preg_match('/^CREATE TABLE ([^ ]+)/', $sql, $matches)) {
 236                  $prevcreate = $matches[1];
 237                  $sql = preg_replace('/\s*\)\s*$/s', '/*keyblock*/)', $sql);
 238                  // Let's inject the extra MySQL tweaks here.
 239                  if ($engine) {
 240                      $sql .= "\n ENGINE = $engine";
 241                  }
 242                  if ($collation) {
 243                      if (strpos($collation, 'utf8_') === 0) {
 244                          $sql .= "\n DEFAULT CHARACTER SET utf8";
 245                      }
 246                      $sql .= "\n DEFAULT COLLATE = $collation ";
 247                  }
 248                  if ($rowformat) {
 249                      $sql .= $rowformat;
 250                  }
 251                  $sqls[] = $sql;
 252                  continue;
 253              }
 254              if ($prevcreate) {
 255                  if (preg_match('/^ALTER TABLE '.$prevcreate.' COMMENT=(.*)$/s', $sql, $matches)) {
 256                      $prev = array_pop($sqls);
 257                      $prev .= "\n COMMENT=$matches[1]";
 258                      $sqls[] = $prev;
 259                      continue;
 260                  }
 261                  if (preg_match('/^CREATE INDEX ([^ ]+) ON '.$prevcreate.' (.*)$/s', $sql, $matches)) {
 262                      $prev = array_pop($sqls);
 263                      if (strpos($prev, '/*keyblock*/')) {
 264                          $prev = str_replace('/*keyblock*/', "\n, KEY $matches[1] $matches[2]/*keyblock*/", $prev);
 265                          $sqls[] = $prev;
 266                          continue;
 267                      } else {
 268                          $sqls[] = $prev;
 269                      }
 270                  }
 271                  if (preg_match('/^CREATE UNIQUE INDEX ([^ ]+) ON '.$prevcreate.' (.*)$/s', $sql, $matches)) {
 272                      $prev = array_pop($sqls);
 273                      if (strpos($prev, '/*keyblock*/')) {
 274                          $prev = str_replace('/*keyblock*/', "\n, UNIQUE KEY $matches[1] $matches[2]/*keyblock*/", $prev);
 275                          $sqls[] = $prev;
 276                          continue;
 277                      } else {
 278                          $sqls[] = $prev;
 279                      }
 280                  }
 281              }
 282              $prevcreate = null;
 283              $sqls[] = $sql;
 284          }
 285  
 286          foreach ($sqls as $key => $sql) {
 287              $sqls[$key] = str_replace('/*keyblock*/', "\n", $sql);
 288          }
 289  
 290          return $sqls;
 291      }
 292  
 293      /**
 294       * Given one xmldb_table and one xmldb_field, return the SQL statements needed to add the field to the table.
 295       *
 296       * @param xmldb_table $xmldb_table The table related to $xmldb_field.
 297       * @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
 298       * @param string $skip_type_clause The type clause on alter columns, NULL by default.
 299       * @param string $skip_default_clause The default clause on alter columns, NULL by default.
 300       * @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
 301       * @return array The SQL statement for adding a field to the table.
 302       */
 303      public function getAddFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
 304          $sqls = parent::getAddFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause, $skip_default_clause, $skip_notnull_clause);
 305  
 306          if ($this->table_exists($xmldb_table)) {
 307              $tablename = $xmldb_table->getName();
 308  
 309              $size = $this->guess_antelope_row_size($this->mdb->get_columns($tablename));
 310              $size += $this->guess_antelope_row_size(array($xmldb_field));
 311  
 312              if ($size > self::ANTELOPE_MAX_ROW_SIZE) {
 313                  if ($this->mdb->is_compressed_row_format_supported()) {
 314                      $format = strtolower($this->mdb->get_row_format($tablename));
 315                      if ($format === 'compact' or $format === 'redundant') {
 316                          // Change the format before conversion so that we do not run out of space.
 317                          array_unshift($sqls, "ALTER TABLE {$this->prefix}$tablename ROW_FORMAT=Compressed");
 318                      }
 319                  }
 320              }
 321          }
 322  
 323          return $sqls;
 324      }
 325  
 326      public function getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL)
 327      {
 328          $tablename = $xmldb_table->getName();
 329          $dbcolumnsinfo = $this->mdb->get_columns($tablename);
 330  
 331          if (($this->mdb->has_breaking_change_sqlmode()) &&
 332              ($dbcolumnsinfo[$xmldb_field->getName()]->meta_type == 'X') &&
 333              ($xmldb_field->getType() == XMLDB_TYPE_INTEGER)) {
 334              // Ignore 1292 ER_TRUNCATED_WRONG_VALUE Truncated incorrect INTEGER value: '%s'.
 335              $altercolumnsqlorig = $this->alter_column_sql;
 336              $this->alter_column_sql = str_replace('ALTER TABLE', 'ALTER IGNORE TABLE', $this->alter_column_sql);
 337              $result = parent::getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause, $skip_default_clause, $skip_notnull_clause);
 338              // Restore the original ALTER SQL statement pattern.
 339              $this->alter_column_sql = $altercolumnsqlorig;
 340  
 341              return $result;
 342          }
 343  
 344          return parent::getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause, $skip_default_clause, $skip_notnull_clause);
 345      }
 346  
 347      /**
 348       * Given one correct xmldb_table, returns the SQL statements
 349       * to create temporary table (inside one array).
 350       *
 351       * @param xmldb_table $xmldb_table The xmldb_table object instance.
 352       * @return array of sql statements
 353       */
 354      public function getCreateTempTableSQL($xmldb_table) {
 355          // Do we know collation?
 356          $collation = $this->mdb->get_dbcollation();
 357          $this->temptables->add_temptable($xmldb_table->getName());
 358  
 359          $sqlarr = parent::getCreateTableSQL($xmldb_table);
 360  
 361          // Let's inject the extra MySQL tweaks.
 362          foreach ($sqlarr as $i=>$sql) {
 363              if (strpos($sql, 'CREATE TABLE ') === 0) {
 364                  // We do not want the engine hack included in create table SQL.
 365                  $sqlarr[$i] = preg_replace('/^CREATE TABLE (.*)/s', 'CREATE TEMPORARY TABLE $1', $sql);
 366                  if ($collation) {
 367                      if (strpos($collation, 'utf8_') === 0) {
 368                          $sqlarr[$i] .= " DEFAULT CHARACTER SET utf8";
 369                      }
 370                      $sqlarr[$i] .= " DEFAULT COLLATE $collation ROW_FORMAT=DYNAMIC";
 371                  }
 372              }
 373          }
 374  
 375          return $sqlarr;
 376      }
 377  
 378      /**
 379       * Given one correct xmldb_table, returns the SQL statements
 380       * to drop it (inside one array).
 381       *
 382       * @param xmldb_table $xmldb_table The table to drop.
 383       * @return array SQL statement(s) for dropping the specified table.
 384       */
 385      public function getDropTableSQL($xmldb_table) {
 386          $sqlarr = parent::getDropTableSQL($xmldb_table);
 387          if ($this->temptables->is_temptable($xmldb_table->getName())) {
 388              $sqlarr = preg_replace('/^DROP TABLE/', "DROP TEMPORARY TABLE", $sqlarr);
 389          }
 390          return $sqlarr;
 391      }
 392  
 393      /**
 394       * Given one XMLDB Type, length and decimals, returns the DB proper SQL type.
 395       *
 396       * @param int $xmldb_type The xmldb_type defined constant. XMLDB_TYPE_INTEGER and other XMLDB_TYPE_* constants.
 397       * @param int $xmldb_length The length of that data type.
 398       * @param int $xmldb_decimals The decimal places of precision of the data type.
 399       * @return string The DB defined data type.
 400       */
 401      public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
 402  
 403          switch ($xmldb_type) {
 404              case XMLDB_TYPE_INTEGER:    // From http://mysql.com/doc/refman/5.0/en/numeric-types.html!
 405                  if (empty($xmldb_length)) {
 406                      $xmldb_length = 10;
 407                  }
 408                  if ($xmldb_length > 9) {
 409                      $dbtype = 'BIGINT';
 410                  } else if ($xmldb_length > 6) {
 411                      $dbtype = 'INT';
 412                  } else if ($xmldb_length > 4) {
 413                      $dbtype = 'MEDIUMINT';
 414                  } else if ($xmldb_length > 2) {
 415                      $dbtype = 'SMALLINT';
 416                  } else {
 417                      $dbtype = 'TINYINT';
 418                  }
 419                  $dbtype .= '(' . $xmldb_length . ')';
 420                  break;
 421              case XMLDB_TYPE_NUMBER:
 422                  $dbtype = $this->number_type;
 423                  if (!empty($xmldb_length)) {
 424                      $dbtype .= '(' . $xmldb_length;
 425                      if (!empty($xmldb_decimals)) {
 426                          $dbtype .= ',' . $xmldb_decimals;
 427                      }
 428                      $dbtype .= ')';
 429                  }
 430                  break;
 431              case XMLDB_TYPE_FLOAT:
 432                  $dbtype = 'DOUBLE';
 433                  if (!empty($xmldb_decimals)) {
 434                      if ($xmldb_decimals < 6) {
 435                          $dbtype = 'FLOAT';
 436                      }
 437                  }
 438                  if (!empty($xmldb_length)) {
 439                      $dbtype .= '(' . $xmldb_length;
 440                      if (!empty($xmldb_decimals)) {
 441                          $dbtype .= ',' . $xmldb_decimals;
 442                      } else {
 443                          $dbtype .= ', 0'; // In MySQL, if length is specified, decimals are mandatory for FLOATs
 444                      }
 445                      $dbtype .= ')';
 446                  }
 447                  break;
 448              case XMLDB_TYPE_CHAR:
 449                  $dbtype = 'VARCHAR';
 450                  if (empty($xmldb_length)) {
 451                      $xmldb_length='255';
 452                  }
 453                  $dbtype .= '(' . $xmldb_length . ')';
 454                  if ($collation = $this->mdb->get_dbcollation()) {
 455                      if (strpos($collation, 'utf8_') === 0) {
 456                          $dbtype .= " CHARACTER SET utf8";
 457                      }
 458                      $dbtype .= " COLLATE $collation";
 459                  }
 460                  break;
 461              case XMLDB_TYPE_TEXT:
 462                  $dbtype = 'LONGTEXT';
 463                  if ($collation = $this->mdb->get_dbcollation()) {
 464                      if (strpos($collation, 'utf8_') === 0) {
 465                          $dbtype .= " CHARACTER SET utf8";
 466                      }
 467                      $dbtype .= " COLLATE $collation";
 468                  }
 469                  break;
 470              case XMLDB_TYPE_BINARY:
 471                  $dbtype = 'LONGBLOB';
 472                  break;
 473              case XMLDB_TYPE_DATETIME:
 474                  $dbtype = 'DATETIME';
 475          }
 476          return $dbtype;
 477      }
 478  
 479      /**
 480       * Given one xmldb_table and one xmldb_field, return the SQL statements needed to add its default
 481       * (usually invoked from getModifyDefaultSQL()
 482       *
 483       * @param xmldb_table $xmldb_table The xmldb_table object instance.
 484       * @param xmldb_field $xmldb_field The xmldb_field object instance.
 485       * @return array Array of SQL statements to create a field's default.
 486       */
 487      public function getCreateDefaultSQL($xmldb_table, $xmldb_field) {
 488          // Just a wrapper over the getAlterFieldSQL() function for MySQL that
 489          // is capable of handling defaults
 490          return $this->getAlterFieldSQL($xmldb_table, $xmldb_field);
 491      }
 492  
 493      /**
 494       * Given one correct xmldb_field and the new name, returns the SQL statements
 495       * to rename it (inside one array).
 496       *
 497       * @param xmldb_table $xmldb_table The table related to $xmldb_field.
 498       * @param xmldb_field $xmldb_field The instance of xmldb_field to get the renamed field from.
 499       * @param string $newname The new name to rename the field to.
 500       * @return array The SQL statements for renaming the field.
 501       */
 502      public function getRenameFieldSQL($xmldb_table, $xmldb_field, $newname) {
 503          // NOTE: MySQL is pretty different from the standard to justify this overloading.
 504  
 505          // Need a clone of xmldb_field to perform the change leaving original unmodified
 506          $xmldb_field_clone = clone($xmldb_field);
 507  
 508          // Change the name of the field to perform the change
 509          $xmldb_field_clone->setName($newname);
 510  
 511          $fieldsql = $this->getFieldSQL($xmldb_table, $xmldb_field_clone);
 512  
 513          $sql = 'ALTER TABLE ' . $this->getTableName($xmldb_table) . ' CHANGE ' .
 514                 $this->getEncQuoted($xmldb_field->getName()) . ' ' . $fieldsql;
 515  
 516          return array($sql);
 517      }
 518  
 519      /**
 520       * Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop its default
 521       * (usually invoked from getModifyDefaultSQL()
 522       *
 523       * Note that this method may be dropped in future.
 524       *
 525       * @param xmldb_table $xmldb_table The xmldb_table object instance.
 526       * @param xmldb_field $xmldb_field The xmldb_field object instance.
 527       * @return array Array of SQL statements to create a field's default.
 528       *
 529       * @todo MDL-31147 Moodle 2.1 - Drop getDropDefaultSQL()
 530       */
 531      public function getDropDefaultSQL($xmldb_table, $xmldb_field) {
 532          // Just a wrapper over the getAlterFieldSQL() function for MySQL that
 533          // is capable of handling defaults
 534          return $this->getAlterFieldSQL($xmldb_table, $xmldb_field);
 535      }
 536  
 537      /**
 538       * Returns the code (array of statements) needed to add one comment to the table.
 539       *
 540       * @param xmldb_table $xmldb_table The xmldb_table object instance.
 541       * @return array Array of SQL statements to add one comment to the table.
 542       */
 543      function getCommentSQL ($xmldb_table) {
 544          $comment = '';
 545  
 546          if ($xmldb_table->getComment()) {
 547              $comment .= 'ALTER TABLE ' . $this->getTableName($xmldb_table);
 548              $comment .= " COMMENT='" . $this->addslashes(substr($xmldb_table->getComment(), 0, 60)) . "'";
 549          }
 550          return array($comment);
 551      }
 552  
 553      /**
 554       * Given one object name and it's type (pk, uk, fk, ck, ix, uix, seq, trg).
 555       *
 556       * (MySQL requires the whole xmldb_table object to be specified, so we add it always)
 557       *
 558       * This is invoked from getNameForObject().
 559       * Only some DB have this implemented.
 560       *
 561       * @param string $object_name The object's name to check for.
 562       * @param string $type The object's type (pk, uk, fk, ck, ix, uix, seq, trg).
 563       * @param string $table_name The table's name to check in
 564       * @return bool If such name is currently in use (true) or no (false)
 565       */
 566      public function isNameInUse($object_name, $type, $table_name) {
 567  
 568          switch($type) {
 569              case 'ix':
 570              case 'uix':
 571                  // First of all, check table exists
 572                  $metatables = $this->mdb->get_tables();
 573                  if (isset($metatables[$table_name])) {
 574                      // Fetch all the indexes in the table
 575                      if ($indexes = $this->mdb->get_indexes($table_name)) {
 576                          // Look for existing index in array
 577                          if (isset($indexes[$object_name])) {
 578                              return true;
 579                          }
 580                      }
 581                  }
 582                  break;
 583          }
 584          return false; //No name in use found
 585      }
 586  
 587  
 588      /**
 589       * Returns an array of reserved words (lowercase) for this DB
 590       * @return array An array of database specific reserved words
 591       */
 592      public static function getReservedWords() {
 593          // This file contains the reserved words for MySQL databases.
 594          $reserved_words = array (
 595              // From http://dev.mysql.com/doc/refman/6.0/en/reserved-words.html.
 596              'accessible', 'add', 'all', 'alter', 'analyze', 'and', 'as', 'asc',
 597              'asensitive', 'before', 'between', 'bigint', 'binary',
 598              'blob', 'both', 'by', 'call', 'cascade', 'case', 'change',
 599              'char', 'character', 'check', 'collate', 'column',
 600              'condition', 'connection', 'constraint', 'continue',
 601              'convert', 'create', 'cross', 'current_date', 'current_time',
 602              'current_timestamp', 'current_user', 'cursor', 'database',
 603              'databases', 'day_hour', 'day_microsecond',
 604              'day_minute', 'day_second', 'dec', 'decimal', 'declare',
 605              'default', 'delayed', 'delete', 'desc', 'describe',
 606              'deterministic', 'distinct', 'distinctrow', 'div', 'double',
 607              'drop', 'dual', 'each', 'else', 'elseif', 'enclosed', 'escaped',
 608              'exists', 'exit', 'explain', 'false', 'fetch', 'float', 'float4',
 609              'float8', 'for', 'force', 'foreign', 'from', 'fulltext', 'grant',
 610              'group', 'having', 'high_priority', 'hour_microsecond',
 611              'hour_minute', 'hour_second', 'if', 'ignore', 'in', 'index',
 612              'infile', 'inner', 'inout', 'insensitive', 'insert', 'int', 'int1',
 613              'int2', 'int3', 'int4', 'int8', 'integer', 'interval', 'into', 'is',
 614              'iterate', 'join', 'key', 'keys', 'kill', 'leading', 'leave', 'left',
 615              'like', 'limit', 'linear', 'lines', 'load', 'localtime', 'localtimestamp',
 616              'lock', 'long', 'longblob', 'longtext', 'loop', 'low_priority', 'master_heartbeat_period',
 617              'master_ssl_verify_server_cert', 'match', 'mediumblob', 'mediumint', 'mediumtext',
 618              'middleint', 'minute_microsecond', 'minute_second',
 619              'mod', 'modifies', 'natural', 'not', 'no_write_to_binlog',
 620              'null', 'numeric', 'on', 'optimize', 'option', 'optionally',
 621              'or', 'order', 'out', 'outer', 'outfile', 'overwrite', 'precision', 'primary',
 622              'procedure', 'purge', 'raid0', 'range', 'read', 'read_only', 'read_write', 'reads', 'real',
 623              'references', 'regexp', 'release', 'rename', 'repeat', 'replace',
 624              'require', 'restrict', 'return', 'revoke', 'right', 'rlike', 'schema',
 625              'schemas', 'second_microsecond', 'select', 'sensitive',
 626              'separator', 'set', 'show', 'smallint', 'soname', 'spatial',
 627              'specific', 'sql', 'sqlexception', 'sqlstate', 'sqlwarning',
 628              'sql_big_result', 'sql_calc_found_rows', 'sql_small_result',
 629              'ssl', 'starting', 'straight_join', 'table', 'terminated', 'then',
 630              'tinyblob', 'tinyint', 'tinytext', 'to', 'trailing', 'trigger', 'true',
 631              'undo', 'union', 'unique', 'unlock', 'unsigned', 'update',
 632              'upgrade', 'usage', 'use', 'using', 'utc_date', 'utc_time',
 633              'utc_timestamp', 'values', 'varbinary', 'varchar', 'varcharacter',
 634              'varying', 'when', 'where', 'while', 'with', 'write', 'x509',
 635              'xor', 'year_month', 'zerofill',
 636              // Added in MySQL 8.0, compared to MySQL 5.7:
 637              // https://dev.mysql.com/doc/refman/8.0/en/keywords.html#keywords-new-in-current-series.
 638              '_filename', 'admin', 'cume_dist', 'dense_rank', 'empty', 'except', 'first_value', 'grouping', 'groups',
 639              'json_table', 'lag', 'last_value', 'lead', 'nth_value', 'ntile',
 640              'of', 'over', 'percent_rank', 'persist', 'persist_only', 'rank', 'recursive', 'row_number',
 641              'system', 'window'
 642          );
 643          return $reserved_words;
 644      }
 645  }