[ Index ]
 

Code source de PHP PEAR 1.4.5

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/MDB/ -> Manager.php (source)

   1  <?php
   2  // +----------------------------------------------------------------------+
   3  // | PHP Version 4                                                        |
   4  // +----------------------------------------------------------------------+
   5  // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
   6  // | Stig. S. Bakken, Lukas Smith                                         |
   7  // | All rights reserved.                                                 |
   8  // +----------------------------------------------------------------------+
   9  // | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
  10  // | API as well as database abstraction for PHP applications.            |
  11  // | This LICENSE is in the BSD license style.                            |
  12  // |                                                                      |
  13  // | Redistribution and use in source and binary forms, with or without   |
  14  // | modification, are permitted provided that the following conditions   |
  15  // | are met:                                                             |
  16  // |                                                                      |
  17  // | Redistributions of source code must retain the above copyright       |
  18  // | notice, this list of conditions and the following disclaimer.        |
  19  // |                                                                      |
  20  // | Redistributions in binary form must reproduce the above copyright    |
  21  // | notice, this list of conditions and the following disclaimer in the  |
  22  // | documentation and/or other materials provided with the distribution. |
  23  // |                                                                      |
  24  // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  25  // | Lukas Smith nor the names of his contributors may be used to endorse |
  26  // | or promote products derived from this software without specific prior|
  27  // | written permission.                                                  |
  28  // |                                                                      |
  29  // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  30  // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  31  // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  32  // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  33  // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  34  // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  35  // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  36  // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  37  // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  38  // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  39  // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  40  // | POSSIBILITY OF SUCH DAMAGE.                                          |
  41  // +----------------------------------------------------------------------+
  42  // | Author: Lukas Smith <smith@backendmedia.com>                         |
  43  // +----------------------------------------------------------------------+
  44  //
  45  // $Id: Manager.php,v 1.75.4.4 2004/03/10 14:42:59 lsmith Exp $
  46  //
  47  
  48  require_once ('MDB/Parser.php');
  49  
  50  define('MDB_MANAGER_DUMP_ALL',          0);
  51  define('MDB_MANAGER_DUMP_STRUCTURE',    1);
  52  define('MDB_MANAGER_DUMP_CONTENT',      2);
  53  
  54  /**
  55   * The database manager is a class that provides a set of database
  56   * management services like installing, altering and dumping the data
  57   * structures of databases.
  58   *
  59   * @package MDB
  60   * @category Database
  61   * @author  Lukas Smith <smith@backendmedia.com>
  62   */
  63  class MDB_Manager extends PEAR
  64  {
  65      // {{{ properties
  66  
  67      var $database;
  68  
  69      var $options = array(
  70              'fail_on_invalid_names' => 1,
  71              'debug' => 0
  72          );
  73      var $invalid_names = array(
  74          'user' => array(),
  75          'is' => array(),
  76          'file' => array(
  77              'oci' => array(),
  78              'oracle' => array()
  79          ),
  80          'notify' => array(
  81              'pgsql' => array()
  82          ),
  83          'restrict' => array(
  84              'mysql' => array()
  85          ),
  86          'password' => array(
  87              'ibase' => array()
  88          )
  89      );
  90      var $default_values = array(
  91          'integer' => 0,
  92          'float' => 0,
  93          'decimal' => 0,
  94          'text' => '',
  95          'timestamp' => '0001-01-01 00:00:00',
  96          'date' => '0001-01-01',
  97          'time' => '00:00:00'
  98      );
  99  
 100      var $warnings = array();
 101  
 102      var $database_definition = array(
 103          'name' => '',
 104          'create' => 0,
 105          'TABLES' => array()
 106      );
 107  
 108      // }}}
 109      // {{{ raiseError()
 110  
 111      /**
 112       * This method is used to communicate an error and invoke error
 113       * callbacks etc.  Basically a wrapper for PEAR::raiseError
 114       * without the message string.
 115       *
 116       * @param mixed $code integer error code, or a PEAR error object (all
 117       *      other parameters are ignored if this parameter is an object
 118       * @param int $mode error mode, see PEAR_Error docs
 119       * @param mixed $options If error mode is PEAR_ERROR_TRIGGER, this is the
 120       *      error level (E_USER_NOTICE etc).  If error mode is
 121       *      PEAR_ERROR_CALLBACK, this is the callback function, either as a
 122       *      function name, or as an array of an object and method name. For
 123       *      other error modes this parameter is ignored.
 124       * @param string $userinfo Extra debug information.  Defaults to the last
 125       *      query and native error code.
 126       * @param mixed $nativecode Native error code, integer or string depending
 127       *      the backend.
 128       * @return object a PEAR error object
 129       * @access public
 130       * @see PEAR_Error
 131       */
 132      function &raiseError($code = MDB_MANAGER_ERROR, $mode = NULL, $options = NULL,
 133          $userinfo = NULL, $nativecode = NULL)
 134      {
 135          // The error is yet a MDB error object
 136          if(is_object($code)) {
 137              $err = PEAR::raiseError($code, NULL, NULL, NULL, NULL, NULL, TRUE);
 138              return($err);
 139          }
 140          
 141          $err = PEAR::raiseError(NULL, $code, $mode, $options, $userinfo,
 142              'MDB_Error', TRUE);
 143          return($err);
 144      }
 145  
 146      // }}}
 147      // {{{ captureDebugOutput()
 148  
 149      /**
 150       * set a debug handler
 151       *
 152       * @param string $capture name of the function that should be used in
 153       *     debug()
 154       * @access public
 155       * @see debug()
 156       */
 157      function captureDebugOutput($capture)
 158      {
 159          $this->options['debug'] = $capture;
 160          $this->database->captureDebugOutput(1);
 161      }
 162  
 163      // }}}
 164      // {{{ debugOutput()
 165  
 166      /**
 167       * output debug info
 168       *
 169       * @return string content of the debug_output class variable
 170       * @access public
 171       */
 172      function debugOutput()
 173      {
 174          return($this->database->debugOutput());
 175      }
 176  
 177      // }}}
 178      // {{{ resetWarnings()
 179  
 180      /**
 181       * reset the warning array
 182       *
 183       * @access public
 184       */
 185      function resetWarnings()
 186      {
 187          $this->warnings = array();
 188      }
 189  
 190      // }}}
 191      // {{{ getWarnings()
 192  
 193      /**
 194       * get all warnings in reverse order.
 195       * This means that the last warning is the first element in the array
 196       *
 197       * @return array with warnings
 198       * @access public
 199       * @see resetWarnings()
 200       */
 201      function getWarnings()
 202      {
 203          return array_reverse($this->warnings);
 204      }
 205  
 206      // }}}
 207      // {{{ setOption()
 208  
 209      /**
 210       * set the option for the db class
 211       *
 212       * @param string $option option name
 213       * @param mixed $value value for the option
 214       * @return mixed MDB_OK or MDB_Error
 215       * @access public
 216       */
 217      function setOption($option, $value)
 218      {
 219          if(isset($this->options[$option])) {
 220              $this->options[$option] = $value;
 221              return(MDB_OK);
 222          }
 223          return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, "unknown option $option"));
 224      }
 225  
 226      // }}}
 227      // {{{ getOption()
 228  
 229      /**
 230       * returns the value of an option
 231       *
 232       * @param string $option option name
 233       * @return mixed the option value or error object
 234       * @access public
 235       */
 236      function getOption($option)
 237      {
 238          if(isset($this->options[$option])) {
 239              return($this->options[$option]);
 240          }
 241          return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, "unknown option $option"));
 242      }
 243  
 244      // }}}
 245      // {{{ connect()
 246  
 247      /**
 248       * Create a new MDB connection object and connect to the specified
 249       * database
 250       *
 251       * @param   mixed   $dbinfo   'data source name', see the MDB::parseDSN
 252       *                            method for a description of the dsn format.
 253       *                            Can also be specified as an array of the
 254       *                            format returned by MDB::parseDSN.
 255       *                            Finally you can also pass an existing db
 256       *                            object to be used.
 257       * @param   mixed   $options  An associative array of option names and
 258       *                            their values.
 259       * @return  mixed MDB_OK on success, or a MDB error object
 260       * @access  public
 261       * @see     MDB::parseDSN
 262       */
 263      function &connect(&$dbinfo, $options = FALSE)
 264      {
 265          if(is_object($this->database) && !MDB::isError($this->database)) {
 266              $this->disconnect();
 267          }
 268          if(is_object($dbinfo)) {
 269               $this->database =& $dbinfo;
 270          } else {
 271              $this->database =& MDB::connect($dbinfo, $options);
 272              if(MDB::isError($this->database)) {
 273                  return($this->database);
 274              }
 275          }
 276          if(is_array($options)) {
 277              $this->options = array_merge($options, $this->options);
 278          }
 279          return(MDB_OK);
 280      }
 281  
 282      // }}}
 283      // {{{ disconnect()
 284  
 285      /**
 286       * Log out and disconnect from the database.
 287       *
 288       * @access public
 289       */
 290      function disconnect()
 291      {
 292          if(is_object($this->database) && !MDB::isError($this->database)) {
 293              $this->database->disconnect();
 294              unset($this->database);
 295          }
 296      }
 297  
 298      // }}}
 299      // {{{ setDatabase()
 300  
 301      /**
 302       * Select a different database
 303       *
 304       * @param string $name name of the database that should be selected
 305       * @return string name of the database previously connected to
 306       * @access public
 307       */
 308      function setDatabase($name)
 309      {
 310          return($this->database->setDatabase($name));
 311      }
 312  
 313      // }}}
 314      // {{{ _createTable()
 315  
 316      /**
 317       * create a table and inititialize the table if data is available
 318       *
 319       * @param string $table_name  name of the table to be created
 320       * @param array  $table       multi dimensional array that containts the
 321       *                            structure and optional data of the table
 322       * @param boolean $overwrite  determine if the table/index should be
 323                                    overwritten if it already exists
 324       * @return mixed MDB_OK on success, or a MDB error object
 325       * @access private
 326       */
 327      function _createTable($table_name, $table, $overwrite = FALSE)
 328      {
 329          $this->expectError(MDB_ERROR_ALREADY_EXISTS);
 330          $result = $this->database->createTable($table_name, $table['FIELDS']);
 331          $this->popExpect();
 332          if(MDB::isError($result)) {
 333              if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) {
 334                  $this->warnings[] = 'Table already exists: '.$table_name;
 335                  if($overwrite) {
 336                      $this->database->debug('Overwritting Table');
 337                      $result = $this->database->dropTable($table_name);
 338                      if(MDB::isError($result)) {
 339                          return($result);
 340                      }
 341                      $result = $this->database->createTable($table_name, $table['FIELDS']);
 342                      if(MDB::isError($result)) {
 343                          return($result);
 344                      }
 345                  } else {
 346                      $result = MDB_OK;
 347                  }
 348              } else {
 349                  $this->database->debug('Create table error: '.$table_name);
 350                  return($result);
 351              }
 352          }
 353          if(isset($table['initialization']) && is_array($table['initialization'])) {
 354              foreach($table['initialization'] as $instruction) {
 355                  switch($instruction['type']) {
 356                      case 'insert':
 357                          $query_fields = $query_values = array();
 358                          if(isset($instruction['FIELDS']) && is_array($instruction['FIELDS'])) {
 359                              foreach($instruction['FIELDS'] as $field_name => $field) {
 360                                  $query_fields[] = $field_name;
 361                                  $query_values[] = '?';
 362                              }
 363                              $query_fields = implode(',',$query_fields);
 364                              $query_values = implode(',',$query_values);
 365                              $result = $prepared_query = $this->database->prepareQuery(
 366                                  "INSERT INTO $table_name ($query_fields) VALUES ($query_values)");
 367                          }
 368                          if(!MDB::isError($prepared_query)) {
 369                              if(isset($instruction['FIELDS']) && is_array($instruction['FIELDS'])) {
 370                                  $lobs = array();
 371                                  $field_number = 0;
 372                                  foreach($instruction['FIELDS'] as $field_name => $field) {
 373                                      $field_number++;
 374                                      $query = $field_name;
 375                                      switch($table['FIELDS'][$field_name]['type']) {
 376                                          case 'integer':
 377                                              $result = $this->database->setParamInteger($prepared_query,
 378                                                  $field_number, intval($field));
 379                                              break;
 380                                          case 'text':
 381                                              $result = $this->database->setParamText($prepared_query,
 382                                                  $field_number, $field);
 383                                              break;
 384                                          case 'clob':
 385                                              $lob_definition = array(
 386                                                  'Database' => $this->database,
 387                                                  'Error' => '',
 388                                                  'Data' => $field
 389                                              );
 390                                              if(MDB::isError($result = $this->database->createLob($lob_definition)))
 391                                              {
 392                                                  break;
 393                                              }
 394                                              $lob = count($lobs);
 395                                              $lobs[$lob] = $result;
 396                                              $result = $this->database->setParamClob($prepared_query,
 397                                                  $field_number, $lobs[$lob], $field_name);
 398                                              break;
 399                                          case 'blob':
 400                                              $lob_definition = array(
 401                                                  'Database' => $this->database,
 402                                                  'Error' => '',
 403                                                  'Data' => $field
 404                                              );
 405                                              if(MDB::isError($result = $this->database->createLob($lob_definition))) {
 406                                                  break;
 407                                              }
 408                                              $lob = count($lobs);
 409                                              $lobs[$lob] = $result;
 410                                              $result = $this->database->setParamBlob($prepared_query,
 411                                                  $field_number, $lobs[$lob], $field_name);
 412                                              break;
 413                                          case 'boolean':
 414                                              $result = $this->database->setParamBoolean($prepared_query,
 415                                                  $field_number, intval($field));
 416                                              break;
 417                                          case 'date':
 418                                              $result = $this->database->setParamDate($prepared_query,
 419                                                  $field_number, $field);
 420                                              break;
 421                                          case 'timestamp':
 422                                              $result = $this->database->setParamTimestamp($prepared_query,
 423                                                  $field_number, $field);
 424                                              break;
 425                                          case 'time':
 426                                              $result = $this->database->setParamTime($prepared_query,
 427                                                  $field_number, $field);
 428                                              break;
 429                                          case 'float':
 430                                              $result = $this->database->setParamFloat($prepared_query,
 431                                                  $field_number, doubleval($field));
 432                                              break;
 433                                          case 'decimal':
 434                                              $result = $this->database->setParamDecimal($prepared_query,
 435                                                  $field_number, $field);
 436                                              break;
 437                                          default:
 438                                              $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
 439                                                  'type "'.$field['type'].'" is not yet supported');
 440                                              break;
 441                                      }
 442                                      if(MDB::isError($result)) {
 443                                          break;
 444                                      }
 445                                  }
 446                              }
 447                              if(!MDB::isError($result)) {
 448                                  $result = $this->database->executeQuery($prepared_query);
 449                              }
 450                              for($lob = 0; $lob < count($lobs); $lob++) {
 451                                  $this->database->destroyLOB($lobs[$lob]);
 452                              }
 453                              $this->database->freePreparedQuery($prepared_query);
 454                          }
 455                          break;
 456                  }
 457              }
 458          };
 459          if(!MDB::isError($result) && isset($table['INDEXES']) && is_array($table['INDEXES'])) {
 460              if(!$this->database->support('Indexes')) {
 461                  return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
 462                      'indexes are not supported'));
 463              }
 464              foreach($table['INDEXES'] as $index_name => $index) {
 465                  $this->expectError(MDB_ERROR_ALREADY_EXISTS);
 466                  $result = $this->database->createIndex($table_name, $index_name, $index);
 467                  $this->popExpect();
 468                  if(MDB::isError($result)) {
 469                      if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) {
 470                          $this->warnings[] = 'Index already exists: '.$index_name;
 471                          if($overwrite) {
 472                              $this->database->debug('Overwritting Index');
 473                              $result = $this->database->dropIndex($table_name, $index_name);
 474                              if(MDB::isError($result)) {
 475                                  break;
 476                              }
 477                              $result = $this->database->createIndex($table_name, $index_name, $index);
 478                              if(MDB::isError($result)) {
 479                                  break;
 480                              }
 481                          } else {
 482                              $result = MDB_OK;
 483                          }
 484                      } else {
 485                          $this->database->debug('Create index error: '.$table_name);
 486                          break;
 487                      }
 488                  }
 489              }
 490          }
 491          if(MDB::isError($result)) {
 492              $result = $this->database->dropTable($table_name);
 493              if(MDB::isError($result)) {
 494                  $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
 495                      'could not drop the table ('
 496                      .$result->getMessage().' ('.$result->getUserinfo(),'))',
 497                      'MDB_Error', TRUE);
 498              }
 499              return($result);
 500          }
 501          return(MDB_OK);
 502      }
 503  
 504      // }}}
 505      // {{{ _dropTable()
 506  
 507      /**
 508       * drop a table
 509       *
 510       * @param string $table_name    name of the table to be dropped
 511       * @return mixed MDB_OK on success, or a MDB error object
 512       * @access private
 513       */
 514      function _dropTable($table_name)
 515      {
 516          return($this->database->dropTable($table_name));
 517      }
 518  
 519      // }}}
 520      // {{{ _createSequence()
 521  
 522      /**
 523       * create a sequence
 524       *
 525       * @param string $sequence_name  name of the sequence to be created
 526       * @param array  $sequence       multi dimensional array that containts the
 527       *                               structure and optional data of the table
 528       * @param string $created_on_table
 529       * @param boolean $overwrite    determine if the sequence should be overwritten
 530                                      if it already exists
 531       * @return mixed MDB_OK on success, or a MDB error object
 532       * @access private
 533       */
 534      function _createSequence($sequence_name, $sequence, $created_on_table, $overwrite = FALSE)
 535      {
 536          if(!$this->database->support('Sequences')) {
 537              return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
 538                  'sequences are not supported'));
 539          }
 540          if(!isset($sequence_name) || !strcmp($sequence_name, '')) {
 541              return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 542                  'no valid sequence name specified'));
 543          }
 544          $this->database->debug('Create sequence: '.$sequence_name);
 545          if(isset($sequence['start']) && $sequence['start'] != '') {
 546              $start = $sequence['start'];
 547          } else if(isset($sequence['on']) && !$created_on_table) {
 548              $table = $sequence['on']['table'];
 549              $field = $sequence['on']['field'];
 550              if($this->database->support('Summaryfunctions')) {
 551                  $field = "MAX($field)";
 552              }
 553              $start = $this->database->queryOne("SELECT $field FROM $table");
 554              if(MDB::isError($start)) {
 555                  return($start);
 556              }
 557          } else {
 558              $start = 1;
 559          }
 560          
 561          $this->expectError(MDB_ERROR_ALREADY_EXISTS);
 562          $result = $this->database->createSequence($sequence_name, $start);
 563          $this->popExpect();
 564          if(MDB::isError($result)) {
 565              if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) {
 566                  $this->warnings[] = 'Sequence already exists: '.$sequence_name;
 567                  if($overwrite) {
 568                      $this->database->debug('Overwritting Sequence');
 569                      $result = $this->database->dropSequence($sequence_name);
 570                      if(MDB::isError($result)) {
 571                          return($result);
 572                      }
 573                      $result = $this->database->createSequence($sequence_name, $start);
 574                      if(MDB::isError($result)) {
 575                          return($result);
 576                      }
 577                  } else {
 578                      return(MDB_OK);
 579                  }
 580              } else {
 581                  $this->database->debug('Create sequence error: '.$sequence_name);
 582                  return($result);
 583              }
 584          }
 585      }
 586  
 587      // }}}
 588      // {{{ _dropSequence()
 589  
 590      /**
 591       * drop a table
 592       *
 593       * @param string $sequence_name    name of the sequence to be dropped
 594       * @return mixed MDB_OK on success, or a MDB error object
 595       * @access private
 596       */
 597      function _dropSequence($sequence_name)
 598      {
 599          if(!$this->database->support('Sequences')) {
 600              return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
 601                  'sequences are not supported'));
 602          }
 603          $this->database->debug('Dropping sequence: '.$sequence_name);
 604          if(!isset($sequence_name) || !strcmp($sequence_name, '')) {
 605              return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 606                  'no valid sequence name specified'));
 607          }
 608          return($this->database->dropSequence($sequence_name));
 609      }
 610  
 611      // }}}
 612      // {{{ _createDatabase()
 613  
 614      /**
 615       * Create a database space within which may be created database objects
 616       * like tables, indexes and sequences. The implementation of this function
 617       * is highly DBMS specific and may require special permissions to run
 618       * successfully. Consult the documentation or the DBMS drivers that you
 619       * use to be aware of eventual configuration requirements.
 620       *
 621       * @return mixed MDB_OK on success, or a MDB error object
 622       * @access private
 623       */
 624      function _createDatabase()
 625      {
 626          if(!isset($this->database_definition['name'])
 627              || !strcmp($this->database_definition['name'], '')
 628          ) {
 629              return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 630                  'no valid database name specified'));
 631          }
 632          $create = (isset($this->database_definition['create']) && $this->database_definition['create']);
 633          $overwrite = (isset($this->database_definition['overwrite']) && $this->database_definition['overwrite']);
 634          if($create) {
 635              $this->database->debug('Create database: '.$this->database_definition['name']);
 636              $this->expectError(MDB_ERROR_ALREADY_EXISTS);
 637              $result = $this->database->createDatabase($this->database_definition['name']);
 638              $this->popExpect();
 639              if(MDB::isError($result)) {
 640                  if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) {
 641                      $this->warnings[] = 'Database already exists: '.$this->database_definition['name'];
 642                      if($overwrite) {
 643                          $this->database->debug('Overwritting Database');
 644                          $result = $this->database->dropDatabase($this->database_definition['name']);
 645                          if(MDB::isError($result)) {
 646                              return($result);
 647                          }
 648                          $result = $this->database->createDatabase($this->database_definition['name']);
 649                          if(MDB::isError($result)) {
 650                              return($result);
 651                          }
 652                      } else {
 653                          $result = MDB_OK;
 654                      }
 655                  } else {
 656                      $this->database->debug('Create database error.');
 657                      return($result);
 658                  }
 659              }
 660          }
 661          $previous_database_name = $this->database->setDatabase($this->database_definition['name']);
 662          if(($support_transactions = $this->database->support('Transactions'))
 663              && MDB::isError($result = $this->database->autoCommit(FALSE))
 664          ) {
 665              return($result);
 666          }
 667  
 668          $created_objects = 0;
 669          if(isset($this->database_definition['TABLES'])
 670              && is_array($this->database_definition['TABLES'])
 671          ) {
 672              foreach($this->database_definition['TABLES'] as $table_name => $table) {
 673                  $result = $this->_createTable($table_name, $table, $overwrite);
 674                  if(MDB::isError($result)) {
 675                      break;
 676                  }
 677                  $created_objects++;
 678              }
 679          }
 680          if(!MDB::isError($result) 
 681              && isset($this->database_definition['SEQUENCES'])
 682              && is_array($this->database_definition['SEQUENCES'])
 683          ) {
 684              foreach($this->database_definition['SEQUENCES'] as $sequence_name => $sequence) {
 685                  $result = $this->_createSequence($sequence_name, $sequence, 0, $overwrite);
 686                  
 687                  if(MDB::isError($result)) {
 688                      break;
 689                  }
 690                  $created_objects++;
 691              }
 692          }
 693          
 694          if(MDB::isError($result)) {
 695              if($created_objects) {
 696                  if($support_transactions) {
 697                      $res = $this->database->rollback();
 698                      if(MDB::isError($res))
 699                          $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
 700                              'Could not rollback the partially created database alterations ('
 701                              .$result->getMessage().' ('.$result->getUserinfo(),'))',
 702                              'MDB_Error', TRUE);
 703                  } else {
 704                      $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
 705                          'the database was only partially created ('
 706                          .$result->getMessage().' ('.$result->getUserinfo(),'))',
 707                          'MDB_Error', TRUE);
 708                  }
 709              }
 710          } else {
 711              if($support_transactions) {
 712                  $res = $this->database->autoCommit(TRUE);
 713                  if(MDB::isError($res))
 714                      $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
 715                          'Could not end transaction after successfully created the database ('
 716                          .$res->getMessage().' ('.$res->getUserinfo(),'))',
 717                          'MDB_Error', TRUE);
 718              }
 719          }
 720          
 721          $this->database->setDatabase($previous_database_name);
 722          
 723          if(MDB::isError($result)
 724              && $create
 725              && MDB::isError($res = $this->database->dropDatabase($this->database_definition['name']))
 726          ) {
 727              return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
 728                  'Could not drop the created database after unsuccessful creation attempt ('
 729                  .$res->getMessage().' ('.$res->getUserinfo(),'))',
 730                  'MDB_Error', TRUE));
 731          }
 732          
 733          if(MDB::isError($result)) {
 734              return($result);
 735          }
 736          
 737          return(MDB_OK);
 738      }
 739  
 740      // }}}
 741      // {{{ _addDefinitionChange()
 742  
 743      /**
 744       * add change to an array of multiple changes
 745       *
 746       * @param array  &$changes
 747       * @param string $definition
 748       * @param string $item
 749       * @param array  $change
 750       * @return mixed MDB_OK on success, or a MDB error object
 751       * @access private
 752       */
 753      function _addDefinitionChange(&$changes, $definition, $item, $change)
 754      {
 755          if(!isset($changes[$definition][$item])) {
 756              $changes[$definition][$item] = array();
 757          }
 758          foreach($change as $change_data_name => $change_data) {
 759              if(isset($change_data) && is_array($change_data)) {
 760                  if(!isset($changes[$definition][$item][$change_data_name])) {
 761                      $changes[$definition][$item][$change_data_name] = array();
 762                  }
 763                  foreach($change_data as $change_part_name => $change_part) {
 764                      $changes[$definition][$item][$change_data_name][$change_part_name] = $change_part;
 765                  }
 766              } else {
 767                  $changes[$definition][$item][$change_data_name] = $change_data;
 768              }
 769          }
 770          return(MDB_OK);
 771      }
 772  
 773      // }}}
 774      // {{{ _compareDefinitions()
 775  
 776      /**
 777       * compare a previous definition with the currenlty parsed definition
 778       *
 779       * @param array multi dimensional array that contains the previous definition
 780       * @return mixed array of changes on success, or a MDB error object
 781       * @access private
 782       */
 783      function _compareDefinitions($previous_definition)
 784      {
 785          $defined_tables = $changes = array();
 786          if(isset($this->database_definition['TABLES']) && is_array($this->database_definition['TABLES'])) {
 787              foreach($this->database_definition['TABLES'] as $table_name => $table) {
 788                  $was_table_name = $table['was'];
 789                  if(isset($previous_definition['TABLES'][$table_name])
 790                      && isset($previous_definition['TABLES'][$table_name]['was'])
 791                      && !strcmp($previous_definition['TABLES'][$table_name]['was'], $was_table_name)
 792                  ) {
 793                      $was_table_name = $table_name;
 794                  }
 795                  if(isset($previous_definition['TABLES'][$was_table_name])) {
 796                      if(strcmp($was_table_name, $table_name)) {
 797                          $this->_addDefinitionChange($changes, 'TABLES', $was_table_name, array('name' => $table_name));
 798                          $this->database->debug("Renamed table '$was_table_name' to '$table_name'");
 799                      }
 800                      if(isset($defined_tables[$was_table_name])) {
 801                          return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 802                              'the table "'.$was_table_name.'" was specified as base of more than of table of the database',
 803                              'MDB_Error', TRUE));
 804                      }
 805                      $defined_tables[$was_table_name] = 1;
 806                      
 807                      $previous_fields = $previous_definition['TABLES'][$was_table_name]['FIELDS'];
 808                      $defined_fields = array();
 809                      if(isset($table['FIELDS']) && is_array($table['FIELDS'])) {
 810                          foreach($table['FIELDS'] as $field_name => $field) {
 811                              $was_field_name = $field['was'];
 812                              if(isset($previous_fields[$field_name])
 813                                  && isset($previous_fields[$field_name]['was'])
 814                                  && !strcmp($previous_fields[$field_name]['was'], $was_field_name)
 815                              ) {
 816                                  $was_field_name = $field_name;
 817                              }
 818                              if(isset($previous_fields[$was_field_name])) {
 819                                  if(strcmp($was_field_name, $field_name)) {
 820                                      $query = $this->database->getFieldDeclaration($field_name, $field);
 821                                      if(MDB::isError($query)) {
 822                                          return($query);
 823                                      }
 824                                      $this->_addDefinitionChange($changes, 'TABLES', $was_table_name,
 825                                          array(
 826                                              'RenamedFields' => array(
 827                                                  $was_field_name => array(
 828                                                      'name' => $field_name,
 829                                                      'Declaration' => $query
 830                                                  )
 831                                              )
 832                                          )
 833                                      );
 834                                      $this->database->debug("Renamed field '$was_field_name' to '$field_name' in table '$table_name'");
 835                                  }
 836                                  if(isset($defined_fields[$was_field_name])) {
 837                                      return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 838                                          'the field "'.$was_table_name.'" was specified as base of more than one field of table',
 839                                          'MDB_Error', TRUE));
 840                                  }
 841                                  $defined_fields[$was_field_name] = 1;
 842                                  $change = array();
 843                                  if($field['type'] == $previous_fields[$was_field_name]['type']) {
 844                                      switch($field['type']) {
 845                                          case 'integer':
 846                                              $previous_unsigned = isset($previous_fields[$was_field_name]['unsigned']);
 847                                              $unsigned = isset($fields[$field_name]['unsigned']);
 848                                              if(strcmp($previous_unsigned, $unsigned)) {
 849                                                  $change['unsigned'] = $unsigned;
 850                                                  $this->database->debug("Changed field '$field_name' type from '".($previous_unsigned ? 'unsigned ' : '').$previous_fields[$was_field_name]['type']."' to '".($unsigned ? 'unsigned ' : '').$field['type']."' in table '$table_name'");
 851                                              }
 852                                              break;
 853                                          case 'text':
 854                                          case 'clob':
 855                                          case 'blob':
 856                                              $previous_length = (isset($previous_fields[$was_field_name]['length']) ? $previous_fields[$was_field_name]['length'] : 0);
 857                                              $length = (isset($field['length']) ? $field['length'] : 0);
 858                                              if(strcmp($previous_length, $length)) {
 859                                                  $change['length'] = $length;
 860                                                  $this->database->debug("Changed field '$field_name' length from '".$previous_fields[$was_field_name]['type'].($previous_length == 0 ? ' no length' : "($previous_length)")."' to '".$field['type'].($length == 0 ? ' no length' : "($length)")."' in table '$table_name'");
 861                                              }
 862                                              break;
 863                                          case 'date':
 864                                          case 'timestamp':
 865                                          case 'time':
 866                                          case 'boolean':
 867                                          case 'float':
 868                                          case 'decimal':
 869                                              break;
 870                                          default:
 871                                              return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
 872                                                  'type "'.$field['type'].'" is not yet supported',
 873                                                  'MDB_Error', TRUE));
 874                                      }
 875                                      
 876                                      $previous_notnull = isset($previous_fields[$was_field_name]['notnull']);
 877                                      $notnull = isset($field['notnull']);
 878                                      if($previous_notnull != $notnull) {
 879                                          $change['ChangedNotNull'] = 1;
 880                                          if($notnull) {
 881                                              $change['notnull'] = isset($field['notnull']);
 882                                          }
 883                                          $this->database->debug("Changed field '$field_name' notnull from $previous_notnull to $notnull in table '$table_name'");
 884                                      }
 885                                      
 886                                      $previous_default = isset($previous_fields[$was_field_name]['default']);
 887                                      $default = isset($field['default']);
 888                                      if(strcmp($previous_default, $default)) {
 889                                          $change['ChangedDefault'] = 1;
 890                                          if($default) {
 891                                              $change['default'] = $field['default'];
 892                                          }
 893                                          $this->database->debug("Changed field '$field_name' default from ".($previous_default ? "'".$previous_fields[$was_field_name]['default']."'" : 'NULL').' TO '.($default ? "'".$fields[$field_name]['default']."'" : 'NULL')." IN TABLE '$table_name'");
 894                                      } else {
 895                                          if($default
 896                                              && strcmp($previous_fields[$was_field_name]['default'], $field['default'])
 897                                          ) {
 898                                              $change['ChangedDefault'] = 1;
 899                                              $change['default'] = $field['default'];
 900                                              $this->database->debug("Changed field '$field_name' default from '".$previous_fields[$was_field_name]['default']."' to '".$fields[$field_name]['default']."' in table '$table_name'");
 901                                          }
 902                                      }
 903                                  } else {
 904                                      $change['type'] = $field['type'];
 905                                      $this->database->debug("Changed field '$field_name' type from '".$previous_fields[$was_field_name]['type']."' to '".$fields[$field_name]['type']."' in table '$table_name'");
 906                                  }
 907                                  if(count($change)) {
 908                                      $query = $this->database->getFieldDeclaration($field_name, $field);
 909                                      if(MDB::isError($query)) {
 910                                          return($query);
 911                                      }
 912                                      $change['Declaration'] = $query;
 913                                      $change['Definition'] = $field;
 914                                      $this->_addDefinitionChange($changes, 'TABLES', $was_table_name, array('ChangedFields' => array($field_name => $change)));
 915                                  }
 916                              } else {
 917                                  if(strcmp($field_name, $was_field_name)) {
 918                                      return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 919                                          'it was specified a previous field name ("'
 920                                          .$was_field_name.'") for field "'.$field_name.'" of table "'
 921                                          .$table_name.'" that does not exist',
 922                                          'MDB_Error', TRUE));
 923                                  }
 924                                  $query = $this->database->getFieldDeclaration($field_name, $field);
 925                                  if(MDB::isError($query)) {
 926                                      return($query);
 927                                  }
 928                                  $change['Declaration'] = $query;
 929                                  $this->_addDefinitionChange($changes, 'TABLES', $table_name, array('AddedFields' => array($field_name => $change)));
 930                                  $this->database->debug("Added field '$field_name' to table '$table_name'");
 931                              }
 932                          }
 933                      }
 934                      if(isset($previous_fields) && is_array($previous_fields)) {
 935                          foreach ($previous_fields as $field_previous_name => $field_previous) {
 936                              if(!isset($defined_fields[$field_previous_name])) {
 937                                  $this->_addDefinitionChange($changes, 'TABLES', $table_name, array('RemovedFields' => array($field_previous_name => array())));
 938                                  $this->database->debug("Removed field '$field_name' from table '$table_name'");
 939                              }
 940                          }
 941                      }
 942                      $indexes = array();
 943                      if(isset($this->database_definition['TABLES'][$table_name]['INDEXES'])
 944                          && is_array($this->database_definition['TABLES'][$table_name]['INDEXES'])
 945                      ) {
 946                          $indexes = $this->database_definition['TABLES'][$table_name]['INDEXES'];
 947                      }
 948                      $previous_indexes = array();
 949                      if(isset($previous_definition['TABLES'][$was_table_name]['INDEXES'])
 950                          && is_array($previous_definition['TABLES'][$was_table_name]['INDEXES'])
 951                      ) {
 952                          $previous_indexes = $previous_definition['TABLES'][$was_table_name]['INDEXES'];
 953                      }
 954                      $defined_indexes = array();
 955                      foreach($indexes as $index_name => $index) {
 956                          $was_index_name = $index['was'];
 957                          if(isset($previous_indexes[$index_name])
 958                              && isset($previous_indexes[$index_name]['was'])
 959                              && !strcmp($previous_indexes[$index_name]['was'], $was_index_name)
 960                          ) {
 961                              $was_index_name = $index_name;
 962                          }
 963                          if(isset($previous_indexes[$was_index_name])) {
 964                              $change = array();
 965                              
 966                              if(strcmp($was_index_name, $index_name)) {
 967                                  $change['name'] = $was_index_name;
 968                                  $this->database->debug("Changed index '$was_index_name' name to '$index_name' in table '$table_name'");
 969                              }
 970                              if(isset($defined_indexes[$was_index_name])) {
 971                                  return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
 972                                      'the index "'.$was_index_name.'" was specified as base of'
 973                                      .' more than one index of table "'.$table_name.'"',
 974                                      'MDB_Error', TRUE));
 975                              }
 976                              $defined_indexes[$was_index_name] = 1;
 977                              
 978                              $previous_unique = isset($previous_indexes[$was_index_name]['unique']);
 979                              $unique = isset($index['unique']);
 980                              if($previous_unique != $unique) {
 981                                  $change['ChangedUnique'] = 1;
 982                                  if($unique) {
 983                                      $change['unique'] = $unique;
 984                                  }
 985                                  $this->database->debug("Changed index '$index_name' unique from $previous_unique to $unique in table '$table_name'");
 986                              }
 987                              $defined_fields = array();
 988                              $previous_fields = $previous_indexes[$was_index_name]['FIELDS'];
 989                              if(isset($index['FIELDS']) && is_array($index['FIELDS'])) {
 990                                  foreach($index['FIELDS'] as $field_name => $field) {
 991                                      if(isset($previous_fields[$field_name])) {
 992                                          $defined_fields[$field_name] = 1;
 993                                          $sorting = (isset($field['sorting']) ? $field['sorting'] : '');
 994                                          $previous_sorting = (isset($previous_fields[$field_name]['sorting']) ? $previous_fields[$field_name]['sorting'] : '');
 995                                          if(strcmp($sorting, $previous_sorting)) {
 996                                              $this->database->debug("Changed index field '$field_name' sorting default from '$previous_sorting' to '$sorting' in table '$table_name'");
 997                                              $change['ChangedFields'] = 1;
 998                                          }
 999                                      } else {
1000                                          $change['ChangedFields'] = 1;
1001                                          $this->database->debug("Added field '$field_name' to index '$index_name' of table '$table_name'");
1002                                      }
1003                                  }
1004                              }
1005                              if(isset($previous_fields) && is_array($previous_fields)) {
1006                                  foreach($previous_fields as $field_name => $field) {
1007                                      if(!isset($defined_fields[$field_name])) {
1008                                          $change['ChangedFields'] = 1;
1009                                          $this->database->debug("Removed field '$field_name' from index '$index_name' of table '$table_name'");
1010                                      }
1011                                  }
1012                              }
1013                              
1014                              if(count($change)) {
1015                                  $this->_addDefinitionChange($changes, 'INDEXES', $table_name,array('ChangedIndexes' => array($index_name => $change)));
1016                              }
1017                          } else {
1018                              if(strcmp($index_name, $was_index_name)) {
1019                                  return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
1020                                      'it was specified a previous index name ("'.$was_index_name
1021                                      .') for index "'.$index_name.'" of table "'.$table_name.'" that does not exist',
1022                                      'MDB_Error', TRUE));
1023                              }
1024                              $this->_addDefinitionChange($changes, 'INDEXES', $table_name,array('AddedIndexes' => array($index_name => $indexes[$index_name])));
1025                              $this->database->debug("Added index '$index_name' to table '$table_name'");
1026                          }
1027                      }
1028                      foreach($previous_indexes as $index_previous_name => $index_previous) {
1029                          if(!isset($defined_indexes[$index_previous_name])) {
1030                              $this->_addDefinitionChange($changes, 'INDEXES', $table_name, array('RemovedIndexes' => array($index_previous_name => $was_table_name)));
1031                              $this->database->debug("Removed index '$index_name' from table '$table_name'");
1032                          }
1033                      }
1034                  } else {
1035                      if(strcmp($table_name, $was_table_name)) {
1036                          return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
1037                              'it was specified a previous table name ("'
1038                              .$was_table_name.'") for table "'.$table_name.'" that does not exist',
1039                              'MDB_Error', TRUE));
1040                      }
1041                      $this->_addDefinitionChange($changes, 'TABLES', $table_name,array('Add' => 1));
1042                      $this->database->debug("Added table '$table_name'");
1043                  }
1044              }
1045              if(isset($previous_definition['TABLES']) && is_array($previous_definition['TABLES'])) {
1046                  foreach ($previous_definition['TABLES'] as $table_name => $table) {
1047                      if(!isset($defined_tables[$table_name])) {
1048                          $this->_addDefinitionChange($changes, 'TABLES', $table_name, array('Remove' => 1));
1049                          $this->database->debug("Removed table '$table_name'");
1050                      }
1051                  }
1052              }
1053              if(isset($this->database_definition['SEQUENCES']) && is_array($this->database_definition['SEQUENCES'])) {
1054                  foreach ($this->database_definition['SEQUENCES'] as $sequence_name => $sequence) {
1055                      $was_sequence_name = $sequence['was'];
1056                      if(isset($previous_definition['SEQUENCES'][$sequence_name])
1057                          && isset($previous_definition['SEQUENCES'][$sequence_name]['was'])
1058                          && !strcmp($previous_definition['SEQUENCES'][$sequence_name]['was'], $was_sequence_name)
1059                      ) {
1060                          $was_sequence_name = $sequence_name;
1061                      }
1062                      if(isset($previous_definition['SEQUENCES'][$was_sequence_name])) {
1063                          if(strcmp($was_sequence_name, $sequence_name)) {
1064                              $this->_addDefinitionChange($changes, 'SEQUENCES', $was_sequence_name,array('name' => $sequence_name));
1065                              $this->database->debug("Renamed sequence '$was_sequence_name' to '$sequence_name'");
1066                          }
1067                          if(isset($defined_sequences[$was_sequence_name])) {
1068                              return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
1069                                  'the sequence "'.$was_sequence_name.'" was specified as base'
1070                                  .' of more than of sequence of the database',
1071                                  'MDB_Error', TRUE));
1072                          }
1073                          $defined_sequences[$was_sequence_name] = 1;
1074                          $change = array();
1075                          if(strcmp($sequence['start'], $previous_definition['SEQUENCES'][$was_sequence_name]['start'])) {
1076                              $change['start'] = $this->database_definition['SEQUENCES'][$sequence_name]['start'];
1077                              $this->database->debug("Changed sequence '$sequence_name' start from '".$previous_definition['SEQUENCES'][$was_sequence_name]['start']."' to '".$this->database_definition['SEQUENCES'][$sequence_name]['start']."'");
1078                          }
1079                          if(strcmp($sequence['on']['table'], $previous_definition['SEQUENCES'][$was_sequence_name]['on']['table'])
1080                              || strcmp($sequence['on']['field'], $previous_definition['SEQUENCES'][$was_sequence_name]['on']['field'])
1081                          ) {
1082                              $change['on'] = $sequence['on'];
1083                              $this->database->debug("Changed sequence '$sequence_name' on table field from '".$previous_definition['SEQUENCES'][$was_sequence_name]['on']['table'].'.'.$previous_definition['SEQUENCES'][$was_sequence_name]['on']['field']."' to '".$this->database_definition['SEQUENCES'][$sequence_name]['on']['table'].'.'.$this->database_definition['SEQUENCES'][$sequence_name]['on']['field']."'");
1084                          }
1085                          if(count($change)) {
1086                              $this->_addDefinitionChange($changes, 'SEQUENCES', $was_sequence_name,array('Change' => array($sequence_name => array($change))));
1087                          }
1088                      } else {
1089                          if(strcmp($sequence_name, $was_sequence_name)) {
1090                              return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
1091                                  'it was specified a previous sequence name ("'.$was_sequence_name
1092                                  .'") for sequence "'.$sequence_name.'" that does not exist',
1093                                  'MDB_Error', TRUE));
1094                          }
1095                          $this->_addDefinitionChange($changes, 'SEQUENCES', $sequence_name, array('Add' => 1));
1096                          $this->database->debug("Added sequence '$sequence_name'");
1097                      }
1098                  }
1099              }
1100              if(isset($previous_definition['SEQUENCES']) && is_array($previous_definition['SEQUENCES'])) {
1101                  foreach ($previous_definition['SEQUENCES'] as $sequence_name => $sequence) {
1102                      if(!isset($defined_sequences[$sequence_name])) {
1103                          $this->_addDefinitionChange($changes, 'SEQUENCES', $sequence_name, array('Remove' => 1));
1104                          $this->database->debug("Removed sequence '$sequence_name'");
1105                      }
1106                  }
1107              }
1108          }
1109          return($changes);
1110      }
1111  
1112      // }}}
1113      // {{{ _alterDatabase()
1114  
1115      /**
1116       * Execute the necessary actions to implement the requested changes
1117       * in a database structure.
1118       *
1119       * @param array $previous_definition an associative array that contains
1120       * the definition of the database structure before applying the requested
1121       * changes. The definition of this array may be built separately, but
1122       * usually it is built by the Parse method the Metabase parser class.
1123       * @param array $changes an associative array that contains the definition of
1124       * the changes that are meant to be applied to the database structure.
1125       * @return mixed MDB_OK on success, or a MDB error object
1126       * @access private
1127       */
1128      function _alterDatabase($previous_definition, $changes)
1129      {
1130          $result = '';
1131          if(isset($changes['TABLES']) && is_array($changes['TABLES'])) {
1132              foreach($changes['TABLES'] as $table_name => $table) {
1133                  if(isset($table['Add']) || isset($table['Remove'])) {
1134                      continue;
1135                  }
1136                  $result = $this->database->alterTable($table_name, $table, 1);
1137                  if(MDB::isError($result)) {
1138                      return($result);
1139                  }
1140              }
1141          }
1142          if(isset($changes['SEQUENCES']) && is_array($changes['SEQUENCES'])) {
1143              if(!$this->database->support('Sequences')) {
1144                  return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
1145                      'sequences are not supported'));
1146              }
1147              foreach($changes['SEQUENCES'] as $sequence) {
1148                  if(isset($sequence['Add'])
1149                      || isset($sequence['Remove'])
1150                      || isset($sequence['Change'])
1151                  ) {
1152                      continue;
1153                  }
1154                  return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
1155                      'some sequences changes are not yet supported'));
1156              }
1157          }
1158          if(isset($changes['INDEXES']) && is_array($changes['INDEXES'])) {
1159              if(!$this->database->support('Indexes')) {
1160                  return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
1161                      'indexes are not supported'));
1162              }
1163              foreach($changes['INDEXES'] as $index) {
1164                  $table_changes = count($index);
1165                  if(isset($index['AddedIndexes'])) {
1166                      $table_changes--;
1167                  }
1168                  if(isset($index['RemovedIndexes'])) {
1169                      $table_changes--;
1170                  }
1171                  if(isset($index['ChangedIndexes'])) {
1172                      $table_changes--;
1173                  }
1174                  if($table_changes) {
1175                      return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
1176                          'index alteration not yet supported'));
1177                  }
1178              }
1179          }
1180          
1181          $previous_database_name = $this->database->setDatabase($this->database_definition['name']);
1182          if(($support_transactions = $this->database->support('Transactions'))
1183              && MDB::isError($result = $this->database->autoCommit(FALSE))
1184          ) {
1185              return($result);
1186          }
1187          $error = '';
1188          $alterations = 0;
1189          if(isset($changes['INDEXES']) && is_array($changes['INDEXES'])) {
1190              foreach($changes['INDEXES'] as $index_name => $index) {
1191                  if(isset($index['RemovedIndexes']) && is_array($index['RemovedIndexes'])) {
1192                      foreach($index['RemovedIndexes'] as $index_remove_name => $index_remove) {
1193                          $result = $this->database->dropIndex($index_name,$index_remove_name);
1194                          if(MDB::isError($result)) {
1195                              break;
1196                          }
1197                          $alterations++;
1198                      }
1199                  }
1200                  if(!MDB::isError($result)
1201                      && is_array($index['ChangedIndexes'])
1202                  ) {
1203                      foreach($index['ChangedIndexes'] as $index_changed_name => $index_changed) {
1204                          $was_name = (isset($indexes[$name]['name']) ? $indexes[$index_changed_name]['name'] : $index_changed_name);
1205                          $result = $this->database->dropIndex($index_name, $was_name);
1206                          if(MDB::isError($result)) {
1207                              break;
1208                          }
1209                          $alterations++;
1210                      }
1211                  }
1212                  if(MDB::isError($result)) {
1213                      break;
1214                  }
1215              }
1216          }
1217          if(!MDB::isError($result) && isset($changes['TABLES'])
1218              && is_array($changes['TABLES'])
1219          ) {
1220              foreach($changes['TABLES'] as $table_name => $table) {
1221                  if(isset($table['Remove'])) {
1222                      $result = $this->_dropTable($table_name);
1223                      if(!MDB::isError($result)) {
1224                          $alterations++;
1225                      }
1226                  } else {
1227                      if(!isset($table['Add'])) {
1228                          $result = $this->database->alterTable($table_name, $changes['TABLES'][$table_name], 0);
1229                          if(!MDB::isError($result)) {
1230                              $alterations++;
1231                          }
1232                      }
1233                  }
1234                  if(MDB::isError($result)) {
1235                      break;
1236                  }
1237              }
1238              foreach($changes['TABLES'] as $table_name => $table) {
1239                  if(isset($table['Add'])) {
1240                      $result = $this->_createTable($table_name, $this->database_definition['TABLES'][$table_name]);
1241                      if(!MDB::isError($result)) {
1242                          $alterations++;
1243                      }
1244                  }
1245                  if(MDB::isError($result)) {
1246                      break;
1247                  }
1248              }
1249          }
1250          if(!MDB::isError($result) && isset($changes['SEQUENCES']) && is_array($changes['SEQUENCES'])) {
1251              foreach($changes['SEQUENCES'] as $sequence_name => $sequence) {
1252                  if(isset($sequence['Add'])) {
1253                      $created_on_table = 0;
1254                      if(isset($this->database_definition['SEQUENCES'][$sequence_name]['on'])) {
1255                          $table = $this->database_definition['SEQUENCES'][$sequence_name]['on']['table'];
1256                          if(isset($changes['TABLES'])
1257                              && isset($changes['TABLES'][$table_name])
1258                              && isset($changes['TABLES'][$table_name]['Add'])
1259                          ) {
1260                              $created_on_table = 1;
1261                          }
1262                      }
1263                      
1264                      $result = $this->_createSequence($sequence_name,
1265                          $this->database_definition['SEQUENCES'][$sequence_name], $created_on_table);
1266                      if(!MDB::isError($result)) {
1267                          $alterations++;
1268                      }
1269                  } else {
1270                      if(isset($sequence['Remove'])) {
1271                          if(!strcmp($error = $this->_dropSequence($sequence_name), '')) {
1272                              $alterations++;
1273                          }
1274                      } else {
1275                          if(isset($sequence['Change'])) {
1276                              $created_on_table = 0;
1277                              if(isset($this->database_definition['SEQUENCES'][$sequence_name]['on'])) {
1278                                  $table = $this->database_definition['SEQUENCES'][$sequence_name]['on']['table'];
1279                                  if(isset($changes['TABLES'])
1280                                      && isset($changes['TABLES'][$table_name])
1281                                      && isset($changes['TABLES'][$table_name]['Add'])
1282                                  ) {
1283                                      $created_on_table = 1;
1284                                  }
1285                              }
1286                              if(!MDB::isError($result = $this->_dropSequence(
1287                                      $this->database_definition['SEQUENCES'][$sequence_name]['was']), '')
1288                                  && !MDB::isError($result = $this->_createSequence(
1289                                      $sequence_name, $this->database_definition['SEQUENCES'][$sequence_name], $created_on_table), '')
1290                              ) {
1291                                  $alterations++;
1292                              }
1293                          } else {
1294                              return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
1295                                  'changing sequences is not yet supported'));
1296                          }
1297                      }
1298                  }
1299                  if(MDB::isError($result)) {
1300                      break;
1301                  }
1302              }
1303          }
1304          if(!MDB::isError($result) && isset($changes['INDEXES']) && is_array($changes['INDEXES'])) {
1305              foreach($changes['INDEXES'] as $table_name => $indexes) {
1306                  if(isset($indexes['ChangedIndexes'])) {
1307                      $changedindexes = $indexes['ChangedIndexes'];
1308                      foreach($changedindexes as $index_name => $index) {
1309                          $result = $this->database->createIndex($table_name, $index_name,
1310                              $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name]);
1311                          if(MDB::isError($result)) {
1312                              break;
1313                          }
1314                          $alterations++;
1315                      }
1316                  }
1317                  if(!MDB::isError($result)
1318                      && isset($indexes['AddedIndexes'])
1319                  ) {
1320                      $addedindexes = $indexes['AddedIndexes'];
1321                      foreach($addedindexes as $index_name => $index) {
1322                          $result = $this->database->createIndex($table_name, $index_name,
1323                              $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name]);
1324                          if(MDB::isError($result)) {
1325                              break;
1326                          }
1327                          $alterations++;
1328                      }
1329                  }
1330                  if(MDB::isError($result)) {
1331                      break;
1332                  }
1333              }
1334          }
1335          if($alterations && MDB::isError($result)) {
1336              if($support_transactions) {
1337                  $res = $this->database->rollback();
1338                  if(MDB::isError($res))
1339                      $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
1340                          'Could not rollback the partially created database alterations ('
1341                          .$result->getMessage().' ('.$result->getUserinfo(),'))',
1342                          'MDB_Error', TRUE);
1343              } else {
1344                  $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
1345                      'the requested database alterations were only partially implemented ('
1346                      .$result->getMessage().' ('.$result->getUserinfo(),'))',
1347                      'MDB_Error', TRUE);
1348              }
1349          }
1350          if($support_transactions) {
1351              $result = $this->database->autoCommit(TRUE);
1352              if(MDB::isError($result)) {
1353                  $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
1354                      'Could not end transaction after successfully implemented the requested database alterations ('
1355                      .$result->getMessage().' ('.$result->getUserinfo(),'))',
1356                      'MDB_Error', TRUE);
1357              }
1358          }
1359          $this->database->setDatabase($previous_database_name);
1360          return($result);
1361      }
1362  
1363      // }}}
1364      // {{{ _escapeSpecialCharacters()
1365  
1366      /**
1367       * add escapecharacters to all special characters in a string
1368       *
1369       * @param string $string string that should be escaped
1370       * @return string escaped string
1371       * @access private
1372       */
1373      function _escapeSpecialCharacters($string)
1374      {
1375          if(gettype($string) != 'string') {
1376              $string = strval($string);
1377          }
1378          for($escaped = '', $character = 0;
1379              $character < strlen($string);
1380              $character++)
1381          {
1382              switch($string[$character]) {
1383                  case '\"':
1384                  case '>':
1385                  case '<':
1386                  case '&':
1387                      $code = ord($string[$character]);
1388                      break;
1389                  default:
1390                      $code = ord($string[$character]);
1391                      if($code < 32 || $code>127) {
1392                          break;
1393                      }
1394                      $escaped .= $string[$character];
1395                      continue 2;
1396              }
1397              $escaped .= "&#$code;";
1398          }
1399          return($escaped);
1400      }
1401  
1402      // }}}
1403      // {{{ _dumpSequence()
1404  
1405      /**
1406       * dump the structure of a sequence
1407       *
1408       * @param string  $sequence_name
1409       * @param string  $eol
1410       * @return mixed string with xml seqeunce definition on success, or a MDB error object
1411       * @access private
1412       */
1413      function _dumpSequence($sequence_name, $eol, $dump = MDB_MANAGER_DUMP_ALL)
1414      {
1415          $sequence_definition = $this->database_definition['SEQUENCES'][$sequence_name];
1416          $buffer = "$eol <sequence>$eol  <name>$sequence_name</name>$eol";
1417          if($dump == MDB_MANAGER_DUMP_ALL || $dump == MDB_MANAGER_DUMP_CONTENT) {
1418              if(isset($sequence_definition['start'])) {
1419                  $start = $sequence_definition['start'];
1420                  $buffer .= "  <start>$start</start>$eol";
1421              }
1422          }
1423          if(isset($sequence_definition['on'])) {
1424              $buffer .= "  <on>$eol   <table>".$sequence_definition['on']['table']."</table>$eol   <field>".$sequence_definition['on']['field']."</field>$eol  </on>$eol";
1425          }
1426          $buffer .= " </sequence>$eol";
1427          return($buffer);
1428      }
1429  
1430      // }}}
1431      // {{{ parseDatabaseDefinitionFile()
1432  
1433      /**
1434       * Parse a database definition file by creating a Metabase schema format
1435       * parser object and passing the file contents as parser input data stream.
1436       *
1437       * @param string $input_file the path of the database schema file.
1438       * @param array $variables an associative array that the defines the text
1439       * string values that are meant to be used to replace the variables that are
1440       * used in the schema description.
1441       * @param bool $fail_on_invalid_names (optional) make function fail on invalid
1442       * names
1443       * @return mixed MDB_OK on success, or a MDB error object
1444       * @access public
1445       */
1446      function parseDatabaseDefinitionFile($input_file, $variables, $fail_on_invalid_names = 1)
1447      {
1448          $parser =& new MDB_Parser($variables, $fail_on_invalid_names);
1449          $result = $parser->setInputFile($input_file);
1450          if(MDB::isError($result)) {
1451              return($result);
1452          };
1453          $result = $parser->parse();
1454          if(MDB::isError($result)) {
1455              return($result);
1456          };
1457          if(MDB::isError($parser->error)) {
1458              return($parser->error);
1459          }
1460          return($parser->database_definition);
1461      }
1462  
1463      // }}}
1464      // {{{ _debugDatabaseChanges()
1465  
1466      /**
1467       * Dump the changes between two database definitions.
1468       *
1469       * @param array $changes an associative array that specifies the list
1470       * of database definitions changes as returned by the _compareDefinitions
1471       * manager class function.
1472       * @return mixed MDB_OK on success, or a MDB error object
1473       * @access private
1474       */
1475      function _debugDatabaseChanges($changes)
1476      {
1477          if(isset($changes['TABLES'])) {
1478              foreach($changes['TABLES'] as $table_name => $table)
1479              {
1480                  $this->database->debug("$table_name:");
1481                  if(isset($table['Add'])) {
1482                      $this->database->debug("\tAdded table '$table_name'");
1483                  } elseif(isset($table['Remove'])) {
1484                      $this->database->debug("\tRemoved table '$table_name'");
1485                  } else {
1486                      if(isset($table['name'])) {
1487                          $this->database->debug("\tRenamed table '$table_name' to '".$table['name']."'");
1488                      }
1489                      if(isset($table['AddedFields'])) {
1490                          foreach($table['AddedFields'] as $field_name => $field) {
1491                              $this->database->debug("\tAdded field '".$field_name."'");
1492                          }
1493                      }
1494                      if(isset($table['RemovedFields'])) {
1495                          foreach($table['RemovedFields'] as $field_name => $field) {
1496                              $this->database->debug("\tRemoved field '".$field_name."'");
1497                          }
1498                      }
1499                      if(isset($table['RenamedFields'])) {
1500                          foreach($table['RenamedFields'] as $field_name => $field) {
1501                              $this->database->debug("\tRenamed field '".$field_name."' to '".$field['name']."'");
1502                          }
1503                      }
1504                      if(isset($table['ChangedFields'])) {
1505                          foreach($table['ChangedFields'] as $field_name => $field) {
1506                              if(isset($field['type'])) {
1507                                  $this->database->debug(
1508                                      "\tChanged field '$field_name' type to '".$field['type']."'");
1509                              }
1510                              if(isset($field['unsigned'])) {
1511                                  $this->database->debug(
1512                                      "\tChanged field '$field_name' type to '".
1513                                      ($field['unsigned'] ? '' : 'not ')."unsigned'");
1514                              }
1515                              if(isset($field['length'])) {
1516                                  $this->database->debug(
1517                                      "\tChanged field '$field_name' length to '".
1518                                      ($field['length'] == 0 ? 'no length' : $field['length'])."'");
1519                              }
1520                              if(isset($field['ChangedDefault'])) {
1521                                  $this->database->debug(
1522                                      "\tChanged field '$field_name' default to ".
1523                                      (isset($field['default']) ? "'".$field['default']."'" : 'NULL'));
1524                              }
1525                              if(isset($field['ChangedNotNull'])) {
1526                                  $this->database->debug(
1527                                     "\tChanged field '$field_name' notnull to ".(isset($field['notnull']) ? "'1'" : '0'));
1528                              }
1529                          }
1530                      }
1531                  }
1532              }
1533          }
1534          if(isset($changes['SEQUENCES'])) {
1535              foreach($changes['SEQUENCES'] as $sequence_name => $sequence)
1536              {
1537                  $this->database->debug("$sequence_name:");
1538                  if(isset($sequence['Add'])) {
1539                      $this->database->debug("\tAdded sequence '$sequence_name'");
1540                  } elseif(isset($sequence['Remove'])) {
1541                      $this->database->debug("\tRemoved sequence '$sequence_name'");
1542                  } else {
1543                      if(isset($sequence['name'])) {
1544                          $this->database->debug("\tRenamed sequence '$sequence_name' to '".$sequence['name']."'");
1545                      }
1546                      if(isset($sequence['Change'])) {
1547                          foreach($sequence['Change'] as $sequence_name => $sequence) {
1548                              if(isset($sequence['start'])) {
1549                                  $this->database->debug(
1550                                      "\tChanged sequence '$sequence_name' start to '".$sequence['start']."'");
1551                              }
1552                          }
1553                      }
1554                  }
1555              }
1556          }
1557          if(isset($changes['INDEXES'])) {
1558              foreach($changes['INDEXES'] as $table_name => $table)
1559              {
1560                  $this->database->debug("$table_name:");
1561                  if(isset($table['AddedIndexes'])) {
1562                      foreach($table['AddedIndexes'] as $index_name => $index) {
1563                          $this->database->debug("\tAdded index '".$index_name."' of table '$table_name'");
1564                      }
1565                  }
1566                  if(isset($table['RemovedIndexes'])) {
1567                      foreach($table['RemovedIndexes'] as $index_name => $index) {
1568                          $this->database->debug("\tRemoved index '".$index_name."' of table '$table_name'");
1569                      }
1570                  }
1571                  if(isset($table['ChangedIndexes'])) {
1572                      foreach($table['ChangedIndexes'] as $index_name => $index) {
1573                          if(isset($index['name'])) {
1574                              $this->database->debug(
1575                                  "\tRenamed index '".$index_name."' to '".$index['name']."' on table '$table_name'");
1576                          }
1577                          if(isset($index['ChangedUnique'])) {
1578                              $this->database->debug(
1579                                  "\tChanged index '".$index_name."' unique to '".
1580                                  isset($index['unique'])."' on table '$table_name'");
1581                          }
1582                          if(isset($index['ChangedFields'])) {
1583                              $this->database->debug("\tChanged index '".$index_name."' on table '$table_name'");
1584                          }
1585                      }
1586                  }
1587              }
1588          }
1589          return(MDB_OK);
1590      }
1591  
1592      // }}}
1593      // {{{ _dumpDatabaseContents()
1594  
1595      /**
1596       * Parse a database schema definition file and dump the respective structure
1597       * and contents.
1598       *
1599       * @param string $schema_file path of the database schema file.
1600       * @param mixed $setup_arguments an associative array that takes pairs of tag names and values
1601       * that define the setup arguments that are passed to the
1602       * MDB_Manager::connect function.
1603       * @param array $dump_arguments an associative array that takes pairs of tag names and values
1604       * that define dump options as defined for the MDB_Manager::DumpDatabase
1605       * function.
1606       * @param array $variables an associative array that the defines the text string values
1607       * that are meant to be used to replace the variables that are used in the
1608       * schema description as defined for the
1609       * MDB_Manager::parseDatabaseDefinitionFile function.
1610       * @return mixed MDB_OK on success, or a MDB error object
1611       * @access private
1612       */
1613      function _dumpDatabaseContents($schema_file, $setup_arguments, $dump_arguments, $variables)
1614      {
1615          $database_definition = $this->parseDatabaseDefinitionFile($schema_file,
1616              $variables, $this->options['fail_on_invalid_names']);
1617          if(MDB::isError($database_definition)) {
1618              return($database_definition);
1619          }
1620          
1621          $this->database_definition = $database_definition;
1622          
1623          $result = $this->connect($setup_arguments);
1624          if(MDB::isError($result)) {
1625              return($result);
1626          }
1627          
1628          return($this->dumpDatabase($dump_arguments));
1629      }
1630  
1631      // }}}
1632      // {{{ getDefinitionFromDatabase()
1633  
1634      /**
1635       * Attempt to reverse engineer a schema structure from an existing MDB
1636       * This method can be used if no xml schema file exists yet.
1637       * The resulting xml schema file may need some manual adjustments.
1638       *
1639       * @return mixed MDB_OK or array with all ambiguities on success, or a MDB error object
1640       * @access public
1641       */
1642      function getDefinitionFromDatabase()
1643      {
1644          $database = $this->database->database_name;
1645          if(strlen($database) == 0) {
1646              return('it was not specified a valid database name');
1647          }
1648          $this->database_definition = array(
1649              'name' => $database,
1650              'create' => 1,
1651              'TABLES' => array()
1652          );
1653          $tables = $this->database->listTables();
1654          if(MDB::isError($tables)) {
1655              return($tables);
1656          }
1657          for($table = 0; $table < count($tables); $table++) {
1658              $table_name = $tables[$table];
1659              $fields = $this->database->listTableFields($table_name);
1660              if(MDB::isError($fields)) {
1661                  return($fields);
1662              }
1663              $this->database_definition['TABLES'][$table_name] = array('FIELDS' => array());
1664              for($field = 0; $field < count($fields); $field++)
1665              {
1666                  $field_name = $fields[$field];
1667                  $definition = $this->database->getTableFieldDefinition($table_name, $field_name);
1668                  if(MDB::isError($definition)) {
1669                      return($definition);
1670                  }
1671                  $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name] = $definition[0][0];
1672                  $field_choices = count($definition[0]);
1673                  if($field_choices > 1) {
1674                      $warning = "There are $field_choices type choices in the table $table_name field $field_name (#1 is the default): ";
1675                      $field_choice_cnt = 1;
1676                      $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name]['CHOICES'] = array();
1677                      foreach($definition[0] as $field_choice) {
1678                          $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name]['CHOICES'][] = $field_choice;
1679                          $warning .= 'choice #'.($field_choice_cnt).': '.serialize($field_choice);
1680                          $field_choice_cnt++;
1681                      }
1682                      $this->warnings[] = $warning;
1683                  }
1684                  if(isset($definition[1])) {
1685                      $sequence = $definition[1]['definition'];
1686                      $sequence_name = $definition[1]['name'];
1687                      $this->database->debug('Implicitly defining sequence: '.$sequence_name);
1688                      if(!isset($this->database_definition['SEQUENCES'])) {
1689                          $this->database_definition['SEQUENCES'] = array();
1690                      }
1691                      $this->database_definition['SEQUENCES'][$sequence_name] = $sequence;
1692                  }
1693                  if(isset($definition[2])) {
1694                      $index = $definition[2]['definition'];
1695                      $index_name = $definition[2]['name'];
1696                      $this->database->debug('Implicitly defining index: '.$index_name);
1697                      if(!isset($this->database_definition['TABLES'][$table_name]['INDEXES'])) {
1698                          $this->database_definition['TABLES'][$table_name]['INDEXES'] = array();
1699                      }
1700                      $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name] = $index;
1701                  }
1702              }
1703              $indexes = $this->database->listTableIndexes($table_name);
1704              if(MDB::isError($indexes)) {
1705                  return($indexes);
1706              }
1707              if(is_array($indexes) && count($indexes) > 0 && !isset($this->database_definition['TABLES'][$table_name]['INDEXES'])) {
1708                  $this->database_definition['TABLES'][$table_name]['INDEXES'] = array();
1709              }
1710              for($index = 0, $index_cnt = count($indexes); $index < $index_cnt; $index++)
1711              {
1712                  $index_name = $indexes[$index];
1713                  $definition = $this->database->getTableIndexDefinition($table_name, $index_name);
1714                  if(MDB::isError($definition)) {
1715                      return($definition);
1716                  }
1717                 $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name] = $definition;
1718              }
1719              // ensure that all fields that have an index on them are set to not null
1720              if(isset($this->database_definition['TABLES'][$table_name]['INDEXES'])
1721                  && is_array($this->database_definition['TABLES'][$table_name]['INDEXES'])
1722                  && count($this->database_definition['TABLES'][$table_name]['INDEXES']) > 0
1723              ) {
1724                  foreach($this->database_definition['TABLES'][$table_name]['INDEXES'] as $index_check_null) {
1725                      foreach($index_check_null['FIELDS'] as $field_name_check_null => $field_check_null) {
1726                          $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name_check_null]['notnull'] = 1;
1727                      }
1728                  }
1729              }
1730              // ensure that all fields that are set to not null also have a default value
1731              if(is_array($this->database_definition['TABLES'][$table_name]['FIELDS'])
1732                  && count($this->database_definition['TABLES'][$table_name]['FIELDS']) > 0
1733              ) {
1734                  foreach($this->database_definition['TABLES'][$table_name]['FIELDS'] as $field_set_default_name => $field_set_default) {
1735                      if(isset($field_set_default['notnull']) && $field_set_default['notnull']
1736                          && !isset($field_set_default['default'])
1737                      ) {
1738                          if(isset($this->default_values[$field_set_default['type']])) {
1739                              $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['default'] = $this->default_values[$field_set_default['type']];
1740                          } else {
1741                              $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['default'] = 0;
1742                          }
1743                      }
1744                      if(isset($field_set_default['CHOICES']) && is_array($field_set_default['CHOICES'])) {
1745                          foreach($field_set_default['CHOICES'] as $field_choices_set_default_name => $field_choices_set_default) {
1746                              if(isset($field_choices_set_default['notnull'])
1747                                  && $field_choices_set_default['notnull']
1748                                  && !isset($field_choices_set_default['default'])
1749                              ) {
1750                                  if(isset($this->default_values[$field_choices_set_default['type']])) {
1751                                      $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['CHOICES']
1752                                          [$field_choices_set_default_name]['default'] = $this->default_values[$field_choices_set_default['type']];
1753                                  } else {
1754                                      $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['CHOICES']
1755                                          [$field_choices_set_default_name]['default'] = 0;
1756                                  }
1757                              }
1758                          }
1759                      }
1760                  }
1761              }
1762          }
1763          $sequences = $this->database->listSequences();
1764          if(MDB::isError($sequences)) {
1765              return($sequences);
1766          }
1767          if(is_array($sequences) && count($sequences) > 0 && !isset($this->database_definition['SEQUENCES'])) {
1768              $this->database_definition['SEQUENCES'] = array();
1769          }
1770          for($sequence = 0; $sequence < count($sequences); $sequence++) {
1771              $sequence_name = $sequences[$sequence];
1772              $definition = $this->database->getSequenceDefinition($sequence_name);
1773              if(MDB::isError($definition)) {
1774                  return($definition);
1775              }
1776              $this->database_definition['SEQUENCES'][$sequence_name] = $definition;
1777          }
1778          return(MDB_OK);
1779      }
1780  
1781      // }}}
1782      // {{{ dumpDatabase()
1783  
1784      /**
1785       * Dump a previously parsed database structure in the Metabase schema
1786       * XML based format suitable for the Metabase parser. This function
1787       * may optionally dump the database definition with initialization
1788       * commands that specify the data that is currently present in the tables.
1789       *
1790       * @param array $arguments an associative array that takes pairs of tag
1791       * names and values that define dump options.
1792       *                 array (
1793       *                     'Definition'    =>    Boolean
1794       *                         TRUE   :  dump currently parsed definition
1795       *                         default:  dump currently connected database
1796       *                     'Output_Mode'    =>    String
1797       *                         'file' :   dump into a file
1798       *                         default:   dump using a function
1799       *                     'Output'        =>    String
1800       *                         depending on the 'Output_Mode'
1801       *                                  name of the file
1802       *                                  name of the function
1803       *                     'EndOfLine'        =>    String
1804       *                         end of line delimiter that should be used
1805       *                         default: "\n"
1806       *                 );
1807       * @param integer $dump constant that determines what data to dump
1808       *                      MDB_MANAGER_DUMP_ALL       : the entire db
1809       *                      MDB_MANAGER_DUMP_STRUCTURE : only the structure of the db
1810       *                      MDB_MANAGER_DUMP_CONTENT   : only the content of the db
1811       * @return mixed MDB_OK on success, or a MDB error object
1812       * @access public
1813       */
1814      function dumpDatabase($arguments, $dump = MDB_MANAGER_DUMP_ALL)
1815      {
1816          if(isset($arguments['Definition']) && $arguments['Definition']) {
1817              $dump_definition = TRUE;
1818          } else {
1819              if(!$this->database) {
1820                  return($this->raiseError(MDB_ERROR_NODBSELECTED,
1821                      NULL, NULL, 'please connect to a RDBMS first'));
1822              }
1823              $error = $this->getDefinitionFromDatabase();
1824              if(MDB::isError($error)) {
1825                  return($error);
1826              }
1827              $dump_definition = FALSE;
1828          }
1829          if(isset($arguments['Output'])) {
1830              if(isset($arguments['Output_Mode']) && $arguments['Output_Mode'] == 'file') {
1831                  $fp = fopen($arguments['Output'], 'w');
1832                  $output = FALSE;
1833              } elseif(function_exists($arguments['Output'])) {
1834                  $output = $arguments['Output'];
1835              } else {
1836                  return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
1837                          'no valid output function specified'));
1838              }
1839          } else {
1840              return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
1841                  'no output method specified'));
1842          }
1843          if(isset($arguments['EndOfLine'])) {
1844              $eol = $arguments['EndOfLine'];
1845          } else {
1846              $eol = "\n";
1847          }
1848          
1849          $sequences = array();
1850          if(isset($this->database_definition['SEQUENCES'])
1851              && is_array($this->database_definition['SEQUENCES'])
1852          ) {
1853              foreach($this->database_definition['SEQUENCES'] as $sequence_name => $sequence) {
1854                  if(isset($sequence['on'])) {
1855                      $table = $sequence['on']['table'];
1856                  } else {
1857                      $table = '';
1858                  }
1859                  $sequences[$table][] = $sequence_name;
1860              }
1861          }
1862          $previous_database_name = (strcmp($this->database_definition['name'], '') ? $this->database->setDatabase($this->database_definition['name']) : '');
1863          $buffer = ('<?xml version="1.0" encoding="ISO-8859-1" ?>'.$eol);
1864          $buffer .= ("<database>$eol$eol <name>".$this->database_definition['name']."</name>$eol <create>".$this->database_definition['create']."</create>$eol");
1865          
1866          if($output) {
1867              $output($buffer);
1868          } else {
1869              fwrite($fp, $buffer);
1870          }
1871          $buffer = '';
1872          if(isset($this->database_definition['TABLES']) && is_array($this->database_definition['TABLES'])) {
1873              foreach($this->database_definition['TABLES'] as $table_name => $table) {
1874                  $buffer = ("$eol <table>$eol$eol  <name>$table_name</name>$eol");
1875                  if($dump == MDB_MANAGER_DUMP_ALL || $dump == MDB_MANAGER_DUMP_STRUCTURE) {
1876                      $buffer .= ("$eol  <declaration>$eol");
1877                      if(isset($table['FIELDS']) && is_array($table['FIELDS'])) {
1878                          foreach($table['FIELDS'] as $field_name => $field) {
1879                              if(!isset($field['type'])) {
1880                                  return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
1881                                      'it was not specified the type of the field "'.$field_name.'" of the table "'.$table_name));
1882                              }
1883                              $buffer .=("$eol   <field>$eol    <name>$field_name</name>$eol    <type>".$field['type']."</type>$eol");
1884                              if(in_array($field_name, array_keys($this->invalid_names))) {
1885                                  $this->warnings[] = "invalid field name: $field_name. You will need to set the class var \$fail_on_invalid_names to FALSE or change the field name.";
1886                              }
1887                              switch($field['type']) {
1888                                  case 'integer':
1889                                      if(isset($field['unsigned'])) {
1890                                          $buffer .=("    <unsigned>1</unsigned>$eol");
1891                                      }
1892                                      break;
1893                                  case 'text':
1894                                  case 'clob':
1895                                  case 'blob':
1896                                      if(isset($field['length'])) {
1897                                          $buffer .=('    <length>'.$field['length']."</length>$eol");
1898                                      }
1899                                      break;
1900                                  case 'boolean':
1901                                  case 'date':
1902                                  case 'timestamp':
1903                                  case 'time':
1904                                  case 'float':
1905                                  case 'decimal':
1906                                      break;
1907                                  default:
1908                                      return('type "'.$field['type'].'" is not yet supported');
1909                              }
1910                              if(isset($field['notnull'])) {
1911                                  $buffer .=("    <notnull>1</notnull>$eol");
1912                              }
1913                              if(isset($field['default'])) {
1914                                  $buffer .=('    <default>'.$this->_escapeSpecialCharacters($field['default'])."</default>$eol");
1915                              }
1916                              $buffer .=("   </field>$eol");
1917                          }
1918                      }
1919                      if(isset($table['INDEXES']) && is_array($table['INDEXES'])) {
1920                          foreach($table['INDEXES'] as $index_name => $index) {
1921                              $buffer .=("$eol   <index>$eol    <name>$index_name</name>$eol");
1922                              if(isset($index['unique'])) {
1923                                  $buffer .=("    <unique>1</unique>$eol");
1924                              }
1925                              foreach($index['FIELDS'] as $field_name => $field) {
1926                                  $buffer .=("    <field>$eol     <name>$field_name</name>$eol");
1927                                  if(is_array($field) && isset($field['sorting'])) { 
1928                                      $buffer .=('     <sorting>'.$field['sorting']."</sorting>$eol");
1929                                  }
1930                                  $buffer .=("    </field>$eol");
1931                              }
1932                              $buffer .=("   </index>$eol");
1933                          }
1934                      }
1935                      $buffer .= ("$eol  </declaration>$eol");
1936                  }
1937                  if($output) {
1938                      $output($buffer);
1939                  } else {
1940                      fwrite($fp, $buffer);
1941                  }
1942                  $buffer = '';
1943                  if($dump == MDB_MANAGER_DUMP_ALL || $dump == MDB_MANAGER_DUMP_CONTENT) {
1944                      if($dump_definition) {
1945                          if(isset($table['initialization']) && is_array($table['initialization'])) {
1946                              $buffer = ("$eol  <initialization>$eol");
1947                              foreach($table['initialization'] as $instruction_name => $instruction) {
1948                                  switch($instruction['type']) {
1949                                      case 'insert':
1950                                          $buffer .= ("$eol   <insert>$eol");
1951                                          foreach($instruction['FIELDS'] as $field_name => $field) {
1952                                              $buffer .= ("$eol    <field>$eol     <name>$field_name</name>$eol     <value>".$this->_escapeSpecialCharacters($field)."</value>$eol   </field>$eol");
1953                                          }
1954                                          $buffer .= ("$eol   </insert>$eol");
1955                                          break;
1956                                  }
1957                              }
1958                              $buffer .= ("$eol  </initialization>$eol");
1959                          }
1960                      } else {
1961                          $types = array();
1962                          foreach($table['FIELDS'] as $field) {
1963                              $types[] = $field['type'];
1964                          }
1965                          $query = 'SELECT '.implode(',',array_keys($table['FIELDS']))." FROM $table_name";
1966                          $result = $this->database->queryAll($query, $types, MDB_FETCHMODE_ASSOC);
1967                          if(MDB::isError($result)) {
1968                              return($result);
1969                          }
1970                          $rows = count($result);
1971                          if($rows > 0) {
1972                              $buffer = ("$eol  <initialization>$eol");
1973                              if($output) {
1974                                  $output($buffer);
1975                              } else {
1976                                  fwrite($fp, $buffer);
1977                              }
1978                              
1979                              for($row = 0; $row < $rows; $row++) {
1980                                  $buffer = ("$eol   <insert>$eol");
1981                                  $values = $result[$row];
1982                                  if(!is_array($values)) {
1983                                      break;
1984                                  } else {
1985                                      foreach($values as $field_name => $field) {
1986                                              $buffer .= ("$eol   <field>$eol     <name>$field_name</name>$eol     <value>");
1987                                              $buffer .= $this->_escapeSpecialCharacters($values[$field_name]);
1988                                              $buffer .= ("</value>$eol   </field>$eol");
1989                                      }
1990                                  }
1991                                  $buffer .= ("$eol   </insert>$eol");
1992                                  if($output) {
1993                                      $output($buffer);
1994                                  } else {
1995                                      fwrite($fp, $buffer);
1996                                  }
1997                                  $buffer = '';
1998                              }
1999                              $buffer = ("$eol  </initialization>$eol");
2000                              if($output) {
2001                                  $output($buffer);
2002                              } else {
2003                                  fwrite($fp, $buffer);
2004                              }
2005                              $buffer = '';
2006                          }
2007                      }
2008                  }
2009                  $buffer .= ("$eol </table>$eol");
2010                  if($output) {
2011                      $output($buffer);
2012                  } else {
2013                      fwrite($fp, $buffer);
2014                  }
2015                  if(isset($sequences[$table_name])) {
2016                      for($sequence = 0, $j = count($sequences[$table_name]);
2017                          $sequence < $j;
2018                          $sequence++)
2019                      {
2020                          $result = $this->_dumpSequence($sequences[$table_name][$sequence], $eol, $dump);
2021                          if(MDB::isError($result)) {
2022                              return($result);
2023                          }
2024                          if($output) {
2025                              $output($result);
2026                          } else {
2027                              fwrite($fp, $result);
2028                          }
2029                      }
2030                  }
2031              }
2032          }
2033          if(isset($sequences[''])) {
2034              for($sequence = 0;
2035                  $sequence < count($sequences['']);
2036                  $sequence++)
2037              {
2038                  $result = $this->_dumpSequence($sequences[''][$sequence], $eol, $dump);
2039                  if(MDB::isError($result)) {
2040                      return($result);
2041                  }
2042                  if($output) {
2043                         $output($result);
2044                     } else {
2045                         fwrite($fp, $result);
2046                  }
2047              }
2048          }
2049          
2050          $buffer = ("$eol</database>$eol");
2051          if($output) {
2052              $output($buffer);
2053          } else {
2054              fwrite($fp, $buffer);
2055              fclose($fp);
2056          }
2057          
2058          if(strcmp($previous_database_name, '')) {
2059              $this->database->setDatabase($previous_database_name);
2060          }
2061          return(MDB_OK);
2062      }
2063  
2064      // }}}
2065      // {{{ updateDatabase()
2066  
2067      /**
2068       * Compare the correspondent files of two versions of a database schema
2069       * definition: the previously installed and the one that defines the schema
2070       * that is meant to update the database.
2071       * If the specified previous definition file does not exist, this function
2072       * will create the database from the definition specified in the current
2073       * schema file.
2074       * If both files exist, the function assumes that the database was previously
2075       * installed based on the previous schema file and will update it by just
2076       * applying the changes.
2077       * If this function succeeds, the contents of the current schema file are
2078       * copied to replace the previous schema file contents. Any subsequent schema
2079       * changes should only be done on the file specified by the $current_schema_file
2080       * to let this function make a consistent evaluation of the exact changes that
2081       * need to be applied.
2082       *
2083       * @param string $current_schema_file name of the updated database schema
2084       * definition file.
2085       * @param string $previous_schema_file name the previously installed database
2086       * schema definition file.
2087       * @param array $variables an associative array that is passed to the argument
2088       * of the same name to the parseDatabaseDefinitionFile function. (there third
2089       * param)
2090       * @return mixed MDB_OK on success, or a MDB error object
2091       * @access public
2092       */
2093      function updateDatabase($current_schema_file, $previous_schema_file = FALSE, $variables = array())
2094      {
2095          $database_definition = $this->parseDatabaseDefinitionFile($current_schema_file,
2096              $variables, $this->options['fail_on_invalid_names']);
2097          if(MDB::isError($database_definition)) {
2098              return($database_definition);
2099          }
2100          $this->database_definition = $database_definition;
2101          $copy = 0;
2102  /*
2103          $this->expectError(MDB_ERROR_UNSUPPORTED);
2104          $databases = $this->database->listDatabases();
2105          $this->popExpect();
2106          if((MDB::isError($databases) || (is_array($databases) && in_array($this->database_definition['name'], $databases)))
2107              && $previous_schema_file && file_exists($previous_schema_file))
2108          {
2109  */
2110          if($previous_schema_file && file_exists($previous_schema_file)) {
2111              $previous_definition = $this->parseDatabaseDefinitionFile($previous_schema_file, $variables, 0);
2112              if(MDB::isError($previous_definition)) {
2113                  return($previous_definition);
2114              }
2115              $changes = $this->_compareDefinitions($previous_definition);
2116              if(MDB::isError($changes)) {
2117                  return($changes);
2118              }
2119              if(isset($changes) && is_array($changes)) {
2120                  $result = $this->_alterDatabase($previous_definition, $changes);
2121                  if(MDB::isError($result)) {
2122                      return($result);
2123                  }
2124                  $copy = 1;
2125                  if($this->options['debug']) {
2126                      $result = $this->_debugDatabaseChanges($changes);
2127                      if(MDB::isError($result)) {
2128                          return($result);
2129                      }
2130                  }
2131              }
2132          } else {
2133              $result = $this->_createDatabase();
2134              if(MDB::isError($result)) {
2135                  return($result);
2136              }
2137              $copy = 1;
2138          }
2139          if($copy && $previous_schema_file && !copy($current_schema_file, $previous_schema_file)) {
2140              return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL,
2141                  'Could not copy the new database definition file to the current file'));
2142          }
2143          return(MDB_OK);
2144      }
2145  
2146      // }}}
2147  }
2148  ?>


Généré le : Sun Feb 25 14:08:00 2007 par Balluche grâce à PHPXref 0.7