Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

migrator.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 19.91 KiB


 001  <?php
 002  /**
 003  *
 004  * This file is part of the phpBB Forum Software package.
 005  *
 006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
 007  * @license GNU General Public License, version 2 (GPL-2.0)
 008  *
 009  * For full copyright and license information, please see
 010  * the docs/CREDITS.txt file.
 011  *
 012  */
 013   
 014  namespace phpbb\db;
 015   
 016  /**
 017  * The migrator is responsible for applying new migrations in the correct order.
 018  */
 019  class migrator
 020  {
 021      /** @var \phpbb\config\config */
 022      protected $config;
 023   
 024      /** @var \phpbb\db\driver\driver_interface */
 025      protected $db;
 026   
 027      /** @var \phpbb\db\tools */
 028      protected $db_tools;
 029   
 030      /** @var \phpbb\db\migration\helper */
 031      protected $helper;
 032   
 033      /** @var string */
 034      protected $table_prefix;
 035   
 036      /** @var string */
 037      protected $phpbb_root_path;
 038   
 039      /** @var string */
 040      protected $php_ext;
 041   
 042      /** @var string */
 043      protected $migrations_table;
 044   
 045      /**
 046      * State of all migrations
 047      *
 048      * (SELECT * FROM migrations table)
 049      *
 050      * @var array
 051      */
 052      protected $migration_state = array();
 053   
 054      /**
 055      * Array of all migrations available to be run
 056      *
 057      * @var array
 058      */
 059      protected $migrations = array();
 060   
 061      /**
 062      * 'name,' 'class,' and 'state' of the last migration run
 063      *
 064      * 'effectively_installed' set and set to true if the migration was effectively_installed
 065      *
 066      * @var array
 067      */
 068      public $last_run_migration = false;
 069   
 070      /**
 071       * The output handler. A null handler is configured by default.
 072       *
 073       * @var migrator_output_handler
 074       */
 075      public $output_handler;
 076   
 077      /**
 078      * Constructor of the database migrator
 079      */
 080      public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\db\tools $db_tools, $migrations_table, $phpbb_root_path, $php_ext, $table_prefix, $tools, \phpbb\db\migration\helper $helper)
 081      {
 082          $this->config = $config;
 083          $this->db = $db;
 084          $this->db_tools = $db_tools;
 085          $this->helper = $helper;
 086   
 087          $this->migrations_table = $migrations_table;
 088   
 089          $this->phpbb_root_path = $phpbb_root_path;
 090          $this->php_ext = $php_ext;
 091   
 092          $this->table_prefix = $table_prefix;
 093   
 094          $this->output_handler = new null_migrator_output_handler();
 095   
 096          foreach ($tools as $tool)
 097          {
 098              $this->tools[$tool->get_name()] = $tool;
 099          }
 100   
 101          $this->tools['dbtools'] = $this->db_tools;
 102   
 103          $this->load_migration_state();
 104      }
 105   
 106      /**
 107       * Set the output handler.
 108       *
 109       * @param migrator_output_handler $handler The output handler
 110       */
 111      public function set_output_handler(migrator_output_handler_interface $handler)
 112      {
 113          $this->output_handler = $handler;
 114      }
 115   
 116      /**
 117      * Loads all migrations and their application state from the database.
 118      *
 119      * @return null
 120      */
 121      public function load_migration_state()
 122      {
 123          $this->migration_state = array();
 124   
 125          // prevent errors in case the table does not exist yet
 126          $this->db->sql_return_on_error(true);
 127   
 128          $sql = "SELECT *
 129              FROM " . $this->migrations_table;
 130          $result = $this->db->sql_query($sql);
 131   
 132          if (!$this->db->get_sql_error_triggered())
 133          {
 134              while ($migration = $this->db->sql_fetchrow($result))
 135              {
 136                  $this->migration_state[$migration['migration_name']] = $migration;
 137   
 138                  $this->migration_state[$migration['migration_name']]['migration_depends_on'] = unserialize($migration['migration_depends_on']);
 139              }
 140          }
 141   
 142          $this->db->sql_freeresult($result);
 143   
 144          $this->db->sql_return_on_error(false);
 145      }
 146   
 147      /**
 148      * Sets the list of available migration class names to the given array.
 149      *
 150      * @param array $class_names An array of migration class names
 151      * @return null
 152      */
 153      public function set_migrations($class_names)
 154      {
 155          $this->migrations = $class_names;
 156      }
 157   
 158      /**
 159      * Runs a single update step from the next migration to be applied.
 160      *
 161      * The update step can either be a schema or a (partial) data update. To
 162      * check if update() needs to be called again use the finished() method.
 163      *
 164      * @return null
 165      */
 166      public function update()
 167      {
 168          foreach ($this->migrations as $name)
 169          {
 170              if (!isset($this->migration_state[$name]) ||
 171                  !$this->migration_state[$name]['migration_schema_done'] ||
 172                  !$this->migration_state[$name]['migration_data_done'])
 173              {
 174                  if (!$this->try_apply($name))
 175                  {
 176                      continue;
 177                  }
 178                  else
 179                  {
 180                      return;
 181                  }
 182              }
 183              else
 184              {
 185                  $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
 186              }
 187          }
 188      }
 189   
 190      /**
 191      * Attempts to apply a step of the given migration or one of its dependencies
 192      *
 193      * @param    string    $name The class name of the migration
 194      * @return    bool    Whether any update step was successfully run
 195      * @throws \phpbb\db\migration\exception
 196      */
 197      protected function try_apply($name)
 198      {
 199          if (!class_exists($name))
 200          {
 201              $this->output_handler->write(array('MIGRATION_NOT_VALID', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
 202              return false;
 203          }
 204   
 205          $migration = $this->get_migration($name);
 206   
 207          $state = (isset($this->migration_state[$name])) ?
 208              $this->migration_state[$name] :
 209              array(
 210                  'migration_depends_on'    => $migration->depends_on(),
 211                  'migration_schema_done' => false,
 212                  'migration_data_done'    => false,
 213                  'migration_data_state'    => '',
 214                  'migration_start_time'    => 0,
 215                  'migration_end_time'    => 0,
 216              );
 217   
 218          if (!empty($state['migration_depends_on']))
 219          {
 220              $this->output_handler->write(array('MIGRATION_APPLY_DEPENDENCIES', $name), migrator_output_handler_interface::VERBOSITY_DEBUG);
 221          }
 222   
 223          foreach ($state['migration_depends_on'] as $depend)
 224          {
 225              if ($this->unfulfillable($depend) !== false)
 226              {
 227                  throw new \phpbb\db\migration\exception('MIGRATION_NOT_FULFILLABLE', $name, $depend);
 228              }
 229   
 230              if (!isset($this->migration_state[$depend]) ||
 231                  !$this->migration_state[$depend]['migration_schema_done'] ||
 232                  !$this->migration_state[$depend]['migration_data_done'])
 233              {
 234                  return $this->try_apply($depend);
 235              }
 236          }
 237   
 238          $this->last_run_migration = array(
 239              'name'    => $name,
 240              'class'    => $migration,
 241              'state'    => $state,
 242              'task'    => '',
 243          );
 244   
 245          if (!isset($this->migration_state[$name]))
 246          {
 247              if ($state['migration_start_time'] == 0 && $migration->effectively_installed())
 248              {
 249                  $state = array(
 250                      'migration_depends_on'    => $migration->depends_on(),
 251                      'migration_schema_done' => true,
 252                      'migration_data_done'    => true,
 253                      'migration_data_state'    => '',
 254                      'migration_start_time'    => 0,
 255                      'migration_end_time'    => 0,
 256                  );
 257   
 258                  $this->last_run_migration['effectively_installed'] = true;
 259   
 260                  $this->output_handler->write(array('MIGRATION_EFFECTIVELY_INSTALLED', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
 261              }
 262              else
 263              {
 264                  $state['migration_start_time'] = time();
 265              }
 266          }
 267   
 268          $this->set_migration_state($name, $state);
 269   
 270          if (!$state['migration_schema_done'])
 271          {
 272              $this->output_handler->write(array('MIGRATION_SCHEMA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
 273   
 274              $this->last_run_migration['task'] = 'process_schema_step';
 275              $elapsed_time = microtime(true);
 276              $steps = $this->helper->get_schema_steps($migration->update_schema());
 277              $result = $this->process_data_step($steps, $state['migration_data_state']);
 278              $elapsed_time = microtime(true) - $elapsed_time;
 279   
 280              $state['migration_data_state'] = ($result === true) ? '' : $result;
 281              $state['migration_schema_done'] = ($result === true);
 282   
 283              $this->output_handler->write(array('MIGRATION_SCHEMA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
 284          }
 285          else if (!$state['migration_data_done'])
 286          {
 287              try
 288              {
 289                  $this->output_handler->write(array('MIGRATION_DATA_RUNNING', $name), migrator_output_handler_interface::VERBOSITY_VERBOSE);
 290   
 291                  $this->last_run_migration['task'] = 'process_data_step';
 292   
 293                  $elapsed_time = microtime(true);
 294                  $result = $this->process_data_step($migration->update_data(), $state['migration_data_state']);
 295                  $elapsed_time = microtime(true) - $elapsed_time;
 296   
 297                  $state['migration_data_state'] = ($result === true) ? '' : $result;
 298                  $state['migration_data_done'] = ($result === true);
 299                  $state['migration_end_time'] = ($result === true) ? time() : 0;
 300   
 301                  if ($state['migration_schema_done'])
 302                  {
 303                      $this->output_handler->write(array('MIGRATION_DATA_DONE', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_NORMAL);
 304                  }
 305                  else
 306                  {
 307                      $this->output_handler->write(array('MIGRATION_DATA_IN_PROGRESS', $name, $elapsed_time), migrator_output_handler_interface::VERBOSITY_VERY_VERBOSE);
 308                  }
 309              }
 310              catch (\phpbb\db\migration\exception $e)
 311              {
 312                  // Revert the schema changes
 313                  $this->revert($name);
 314   
 315                  // Rethrow exception
 316                  throw $e;
 317              }
 318          }
 319   
 320          $this->set_migration_state($name, $state);
 321   
 322          return true;
 323      }
 324   
 325      /**
 326      * Runs a single revert step from the last migration installed
 327      *
 328      * YOU MUST ADD/SET ALL MIGRATIONS THAT COULD BE DEPENDENT ON THE MIGRATION TO REVERT TO BEFORE CALLING THIS METHOD!
 329      * The revert step can either be a schema or a (partial) data revert. To
 330      * check if revert() needs to be called again use the migration_state() method.
 331      *
 332      * @param string $migration String migration name to revert (including any that depend on this migration)
 333      * @return null
 334      */
 335      public function revert($migration)
 336      {
 337          if (!isset($this->migration_state[$migration]))
 338          {
 339              // Not installed
 340              return;
 341          }
 342   
 343          foreach ($this->migration_state as $name => $state)
 344          {
 345              if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on']))
 346              {
 347                  $this->revert($name);
 348              }
 349          }
 350   
 351          $this->try_revert($migration);
 352      }
 353   
 354      /**
 355      * Attempts to revert a step of the given migration or one of its dependencies
 356      *
 357      * @param    string    $name The class name of the migration
 358      * @return    bool    Whether any update step was successfully run
 359      */
 360      protected function try_revert($name)
 361      {
 362          if (!class_exists($name))
 363          {
 364              return false;
 365          }
 366   
 367          $migration = $this->get_migration($name);
 368   
 369          $state = $this->migration_state[$name];
 370   
 371          $this->last_run_migration = array(
 372              'name'    => $name,
 373              'class'    => $migration,
 374              'task'    => '',
 375          );
 376   
 377          if ($state['migration_data_done'])
 378          {
 379              if ($state['migration_data_state'] !== 'revert_data')
 380              {
 381                  $result = $this->process_data_step($migration->update_data(), $state['migration_data_state'], true);
 382   
 383                  $state['migration_data_state'] = ($result === true) ? 'revert_data' : $result;
 384              }
 385              else
 386              {
 387                  $result = $this->process_data_step($migration->revert_data(), '', false);
 388   
 389                  $state['migration_data_state'] = ($result === true) ? '' : $result;
 390                  $state['migration_data_done'] = ($result === true) ? false : true;
 391              }
 392   
 393              $this->set_migration_state($name, $state);
 394          }
 395          else if ($state['migration_schema_done'])
 396          {
 397              $steps = $this->helper->get_schema_steps($migration->revert_schema());
 398              $result = $this->process_data_step($steps, $state['migration_data_state']);
 399   
 400              $state['migration_data_state'] = ($result === true) ? '' : $result;
 401              $state['migration_schema_done'] = ($result === true) ? false : true;
 402   
 403              if (!$state['migration_schema_done'])
 404              {
 405                  $sql = 'DELETE FROM ' . $this->migrations_table . "
 406                      WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
 407                  $this->db->sql_query($sql);
 408   
 409                  unset($this->migration_state[$name]);
 410              }
 411          }
 412   
 413          return true;
 414      }
 415   
 416      /**
 417      * Process the data step of the migration
 418      *
 419      * @param array $steps The steps to run
 420      * @param bool|string $state Current state of the migration
 421      * @param bool $revert true to revert a data step
 422      * @return bool|string migration state. True if completed, serialized array if not finished
 423      * @throws \phpbb\db\migration\exception
 424      */
 425      protected function process_data_step($steps, $state, $revert = false)
 426      {
 427          $state = ($state) ? unserialize($state) : false;
 428   
 429          // reverse order of steps if reverting
 430          if ($revert === true)
 431          {
 432              $steps = array_reverse($steps);
 433          }
 434   
 435          foreach ($steps as $step_identifier => $step)
 436          {
 437              $last_result = 0;
 438              if ($state)
 439              {
 440                  // Continue until we reach the step that matches the last step called
 441                  if ($state['step'] != $step_identifier)
 442                  {
 443                      continue;
 444                  }
 445   
 446                  // We send the result from last time to the callable function
 447                  $last_result = $state['result'];
 448   
 449                  // Set state to false since we reached the point we were at
 450                  $state = false;
 451              }
 452   
 453              try
 454              {
 455                  // Result will be null or true if everything completed correctly
 456                  $result = $this->run_step($step, $last_result, $revert);
 457                  if ($result !== null && $result !== true)
 458                  {
 459                      return serialize(array(
 460                          'result'    => $result,
 461                          'step'        => $step_identifier,
 462                      ));
 463                  }
 464              }
 465              catch (\phpbb\db\migration\exception $e)
 466              {
 467                  // We should try rolling back here
 468                  foreach ($steps as $reverse_step_identifier => $reverse_step)
 469                  {
 470                      // If we've reached the current step we can break because we reversed everything that was run
 471                      if ($reverse_step_identifier == $step_identifier)
 472                      {
 473                          break;
 474                      }
 475   
 476                      // Reverse the step that was run
 477                      $result = $this->run_step($reverse_step, false, !$revert);
 478                  }
 479   
 480                  // rethrow the exception
 481                  throw $e;
 482              }
 483          }
 484   
 485          return true;
 486      }
 487   
 488      /**
 489      * Run a single step
 490      *
 491      * An exception should be thrown if an error occurs
 492      *
 493      * @param mixed $step Data step from migration
 494      * @param mixed $last_result Result to pass to the callable (only for 'custom' method)
 495      * @param bool $reverse False to install, True to attempt uninstallation by reversing the call
 496      * @return null
 497      */
 498      protected function run_step($step, $last_result = 0, $reverse = false)
 499      {
 500          $callable_and_parameters = $this->get_callable_from_step($step, $last_result, $reverse);
 501   
 502          if ($callable_and_parameters === false)
 503          {
 504              return;
 505          }
 506   
 507          $callable = $callable_and_parameters[0];
 508          $parameters = $callable_and_parameters[1];
 509   
 510          return call_user_func_array($callable, $parameters);
 511      }
 512   
 513      /**
 514      * Get a callable statement from a data step
 515      *
 516      * @param array $step Data step from migration
 517      * @param mixed $last_result Result to pass to the callable (only for 'custom' method)
 518      * @param bool $reverse False to install, True to attempt uninstallation by reversing the call
 519      * @return array Array with parameters for call_user_func_array(), 0 is the callable, 1 is parameters
 520      * @throws \phpbb\db\migration\exception
 521      */
 522      protected function get_callable_from_step(array $step, $last_result = 0, $reverse = false)
 523      {
 524          $type = $step[0];
 525          $parameters = $step[1];
 526   
 527          $parts = explode('.', $type);
 528   
 529          $class = $parts[0];
 530          $method = false;
 531   
 532          if (isset($parts[1]))
 533          {
 534              $method = $parts[1];
 535          }
 536   
 537          switch ($class)
 538          {
 539              case 'if':
 540                  if (!isset($parameters[0]))
 541                  {
 542                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_CONDITION', $step);
 543                  }
 544   
 545                  if (!isset($parameters[1]))
 546                  {
 547                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_MISSING_STEP', $step);
 548                  }
 549   
 550                  $condition = $parameters[0];
 551   
 552                  if (!$condition)
 553                  {
 554                      return false;
 555                  }
 556   
 557                  $step = $parameters[1];
 558   
 559                  return $this->get_callable_from_step($step);
 560              break;
 561   
 562              case 'custom':
 563                  if (!is_callable($parameters[0]))
 564                  {
 565                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_CUSTOM_NOT_CALLABLE', $step);
 566                  }
 567   
 568                  if ($reverse)
 569                  {
 570                      return false;
 571                  }
 572                  else
 573                  {
 574                      return array(
 575                          $parameters[0],
 576                          array($last_result),
 577                      );
 578                  }
 579              break;
 580   
 581              default:
 582                  if (!$method)
 583                  {
 584                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNKNOWN_TYPE', $step);
 585                  }
 586   
 587                  if (!isset($this->tools[$class]))
 588                  {
 589                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_TOOL', $step);
 590                  }
 591   
 592                  if (!method_exists(get_class($this->tools[$class]), $method))
 593                  {
 594                      throw new \phpbb\db\migration\exception('MIGRATION_INVALID_DATA_UNDEFINED_METHOD', $step);
 595                  }
 596   
 597                  // Attempt to reverse operations
 598                  if ($reverse)
 599                  {
 600                      array_unshift($parameters, $method);
 601   
 602                      return array(
 603                          array($this->tools[$class], 'reverse'),
 604                          $parameters,
 605                      );
 606                  }
 607   
 608                  return array(
 609                      array($this->tools[$class], $method),
 610                      $parameters,
 611                  );
 612              break;
 613          }
 614      }
 615   
 616      /**
 617      * Insert/Update migration row into the database
 618      *
 619      * @param string $name Name of the migration
 620      * @param array $state
 621      * @return null
 622      */
 623      protected function set_migration_state($name, $state)
 624      {
 625          $migration_row = $state;
 626          $migration_row['migration_depends_on'] = serialize($state['migration_depends_on']);
 627   
 628          if (isset($this->migration_state[$name]))
 629          {
 630              $sql = 'UPDATE ' . $this->migrations_table . '
 631                  SET ' . $this->db->sql_build_array('UPDATE', $migration_row) . "
 632                  WHERE migration_name = '" . $this->db->sql_escape($name) . "'";
 633              $this->db->sql_query($sql);
 634          }
 635          else
 636          {
 637              $migration_row['migration_name'] = $name;
 638              $sql = 'INSERT INTO ' . $this->migrations_table . '
 639                  ' . $this->db->sql_build_array('INSERT', $migration_row);
 640              $this->db->sql_query($sql);
 641          }
 642   
 643          $this->migration_state[$name] = $state;
 644   
 645          $this->last_run_migration['state'] = $state;
 646      }
 647   
 648      /**
 649      * Checks if a migration's dependencies can even theoretically be satisfied.
 650      *
 651      * @param string    $name The class name of the migration
 652      * @return bool|string False if fulfillable, string of missing migration name if unfulfillable
 653      */
 654      public function unfulfillable($name)
 655      {
 656          if (isset($this->migration_state[$name]))
 657          {
 658              return false;
 659          }
 660   
 661          if (!class_exists($name))
 662          {
 663              return $name;
 664          }
 665   
 666          $migration = $this->get_migration($name);
 667          $depends = $migration->depends_on();
 668   
 669          foreach ($depends as $depend)
 670          {
 671              $unfulfillable = $this->unfulfillable($depend);
 672              if ($unfulfillable !== false)
 673              {
 674                  return $unfulfillable;
 675              }
 676          }
 677   
 678          return false;
 679      }
 680   
 681      /**
 682      * Checks whether all available, fulfillable migrations have been applied.
 683      *
 684      * @return bool Whether the migrations have been applied
 685      */
 686      public function finished()
 687      {
 688          foreach ($this->migrations as $name)
 689          {
 690              if (!isset($this->migration_state[$name]))
 691              {
 692                  // skip unfulfillable migrations, but fulfillables mean we
 693                  // are not finished yet
 694                  if ($this->unfulfillable($name) !== false)
 695                  {
 696                      continue;
 697                  }
 698   
 699                  return false;
 700              }
 701   
 702              $migration = $this->migration_state[$name];
 703   
 704              if (!$migration['migration_schema_done'] || !$migration['migration_data_done'])
 705              {
 706                  return false;
 707              }
 708          }
 709   
 710          return true;
 711      }
 712   
 713      /**
 714      * Gets a migration state (whether it is installed and to what extent)
 715      *
 716      * @param string $migration String migration name to check if it is installed
 717      * @return bool|array False if the migration has not at all been installed, array
 718      */
 719      public function migration_state($migration)
 720      {
 721          if (!isset($this->migration_state[$migration]))
 722          {
 723              return false;
 724          }
 725   
 726          return $this->migration_state[$migration];
 727      }
 728   
 729      /**
 730      * Helper to get a migration
 731      *
 732      * @param string $name Name of the migration
 733      * @return \phpbb\db\migration\migration
 734      */
 735      protected function get_migration($name)
 736      {
 737          return new $name($this->config, $this->db, $this->db_tools, $this->phpbb_root_path, $this->php_ext, $this->table_prefix);
 738      }
 739   
 740      /**
 741      * This function adds all migrations sent to it to the migrations table
 742      *
 743      * THIS SHOULD NOT GENERALLY BE USED! THIS IS FOR THE PHPBB INSTALLER.
 744      * THIS WILL THROW ERRORS IF MIGRATIONS ALREADY EXIST IN THE TABLE, DO NOT CALL MORE THAN ONCE!
 745      *
 746      * @param array $migrations Array of migrations (names) to add to the migrations table
 747      * @return null
 748      */
 749      public function populate_migrations($migrations)
 750      {
 751          foreach ($migrations as $name)
 752          {
 753              if ($this->migration_state($name) === false)
 754              {
 755                  $state = array(
 756                      'migration_depends_on'    => $name::depends_on(),
 757                      'migration_schema_done' => true,
 758                      'migration_data_done'    => true,
 759                      'migration_data_state'    => '',
 760                      'migration_start_time'    => time(),
 761                      'migration_end_time'    => time(),
 762                  );
 763                  $this->set_migration_state($name, $state);
 764              }
 765          }
 766      }
 767   
 768      /**
 769      * Creates the migrations table if it does not exist.
 770      * @return null
 771      */
 772      public function create_migrations_table()
 773      {
 774          // Make sure migrations have been installed.
 775          if (!$this->db_tools->sql_table_exists($this->table_prefix . 'migrations'))
 776          {
 777              $this->db_tools->sql_create_table($this->table_prefix . 'migrations', array(
 778                  'COLUMNS'        => array(
 779                      'migration_name'            => array('VCHAR', ''),
 780                      'migration_depends_on'        => array('TEXT', ''),
 781                      'migration_schema_done'        => array('BOOL', 0),
 782                      'migration_data_done'        => array('BOOL', 0),
 783                      'migration_data_state'        => array('TEXT', ''),
 784                      'migration_start_time'        => array('TIMESTAMP', 0),
 785                      'migration_end_time'        => array('TIMESTAMP', 0),
 786                  ),
 787                  'PRIMARY_KEY'    => 'migration_name',
 788              ));
 789          }
 790      }
 791  }
 792