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

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