MDB2_Schema
[ class tree: MDB2_Schema ] [ index: MDB2_Schema ] [ all elements ]

Source for file Schema.php

Documentation is available at Schema.php

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

Documentation generated on Mon, 11 Mar 2019 14:13:19 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.