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-2008 Manuel Lemos, Tomas V.V.Cox,                 |
  6. // | Stig. S. Bakken, Lukas Smith, Igor Feghali                           |
  7. // | All rights reserved.                                                 |
  8. // +----------------------------------------------------------------------+
  9. // | MDB2_Schema enables users to maintain RDBMS independant schema files |
  10. // | in XML that can be used to manipulate both data and database schemas |
  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, Igor Feghali nor the names of his contributors may be   |
  26. // | used to endorse or promote products derived from this software       |
  27. // | without specific prior 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. // | Author: Igor Feghali <ifeghali@php.net>                              |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: Schema.php,v 1.121 2008/02/06 23:13:51 ifeghali Exp $
  47. //
  48.  
  49. require_once 'MDB2.php';
  50.  
  51. define('MDB2_SCHEMA_DUMP_ALL',          0);
  52. define('MDB2_SCHEMA_DUMP_STRUCTURE',    1);
  53. define('MDB2_SCHEMA_DUMP_CONTENT',      2);
  54.  
  55. /**
  56.  * If you add an error code here, make sure you also add a textual
  57.  * version of it in MDB2_Schema::errorMessage().
  58.  */
  59.  
  60. define('MDB2_SCHEMA_ERROR',                                         -1);
  61. define('MDB2_SCHEMA_ERROR_PARSE',                                   -2);
  62. define('MDB2_SCHEMA_ERROR_VALIDATE',                                -3);
  63. define('MDB2_SCHEMA_ERROR_UNSUPPORTED',                             -4);    // Driver does not support this function
  64. define('MDB2_SCHEMA_ERROR_INVALID',                                 -5);    // Invalid attribute value
  65. define('MDB2_SCHEMA_ERROR_WRITER',                                  -6);
  66.  
  67. /**
  68.  * The database manager is a class that provides a set of database
  69.  * management services like installing, altering and dumping the data
  70.  * structures of databases.
  71.  *
  72.  * @package MDB2_Schema
  73.  * @category Database
  74.  * @author  Lukas Smith <smith@pooteeweet.org>
  75.  */
  76. class MDB2_Schema extends PEAR
  77. {
  78.     // {{{ properties
  79.  
  80.     var $db;
  81.  
  82.     var $warnings = array();
  83.  
  84.     var $options = array(
  85.         'fail_on_invalid_names' => true,
  86.         'dtd_file' => false,
  87.         'valid_types' => array(),
  88.         'force_defaults' => true,
  89.         'parser' => 'MDB2_Schema_Parser',
  90.         'writer' => 'MDB2_Schema_Writer',
  91.         'validate' => 'MDB2_Schema_Validate'
  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.     // {{{ arrayMergeClobber()
  110.  
  111.     /**
  112.      * Clobbers two arrays together
  113.      *
  114.      * @param  array        array that should be clobbered
  115.      * @param  array        array that should be clobbered
  116.      * @return array|false array on success and false on error
  117.      *
  118.      * @access public
  119.      * @author kc@hireability.com
  120.      */
  121.     function arrayMergeClobber($a1$a2)
  122.     {
  123.         if (!is_array($a1|| !is_array($a2)) {
  124.             return false;
  125.         }
  126.         foreach ($a2 as $key => $val{
  127.             if (is_array($val&& array_key_exists($key$a1&& is_array($a1[$key])) {
  128.                 $a1[$keyMDB2_Schema::arrayMergeClobber($a1[$key]$val);
  129.             else {
  130.                 $a1[$key$val;
  131.             }
  132.         }
  133.         return $a1;
  134.     }
  135.  
  136.     // }}}
  137.     // {{{ resetWarnings()
  138.  
  139.     /**
  140.      * reset the warning array
  141.      *
  142.      * @access public
  143.      */
  144.     function resetWarnings()
  145.     {
  146.         $this->warnings = array();
  147.     }
  148.  
  149.     // }}}
  150.     // {{{ getWarnings()
  151.  
  152.     /**
  153.      * Get all warnings in reverse order
  154.      *
  155.      * This means that the last warning is the first element in the array
  156.      *
  157.      * @return array with warnings
  158.      * @access public
  159.      * @see resetWarnings()
  160.      */
  161.     function getWarnings()
  162.     {
  163.         return array_reverse($this->warnings);
  164.     }
  165.  
  166.     // }}}
  167.     // {{{ setOption()
  168.  
  169.     /**
  170.      * Sets the option for the db class
  171.      *
  172.      * @param string option name
  173.      * @param mixed value for the option
  174.      * @return bool|MDB2_ErrorMDB2_OK or error object
  175.      * @access public
  176.      */
  177.     function setOption($option$value)
  178.     {
  179.         if (isset($this->options[$option])) {
  180.             if (is_null($value)) {
  181.                 return $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  182.                     'may not set an option to value null');
  183.             }
  184.             $this->options[$option$value;
  185.             return MDB2_OK;
  186.         }
  187.         return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTEDnullnull,
  188.             "unknown option $option");
  189.     }
  190.  
  191.     // }}}
  192.     // {{{ getOption()
  193.  
  194.     /**
  195.      * returns the value of an option
  196.      *
  197.      * @param string option name
  198.      * @return mixed the option value or error object
  199.      * @access public
  200.      */
  201.     function getOption($option)
  202.     {
  203.         if (isset($this->options[$option])) {
  204.             return $this->options[$option];
  205.         }
  206.         return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED,
  207.             nullnull"unknown option $option");
  208.     }
  209.  
  210.     // }}}
  211.     // {{{ factory()
  212.  
  213.     /**
  214.      * Create a new MDB2 object for the specified database type
  215.      * type
  216.      *
  217.      * @param string|array|MDB2_Driver_Common  'data source name', see the
  218.      *              @see MDB2::parseDSN method for a description of the dsn format.
  219.      *               Can also be specified as an array of the
  220.      *               format returned by @see MDB2::parseDSN.
  221.      *               Finally you can also pass an existing db object to be used.
  222.      * @param array An associative array of option names and their values.
  223.      * @return bool|MDB2_ErrorMDB2_OK or error object
  224.      * @access public
  225.      * @see     MDB2::parseDSN
  226.      */
  227.     function &factory(&$db$options = array())
  228.     {
  229.         $obj =new MDB2_Schema();
  230.         $result $obj->connect($db$options);
  231.         if (PEAR::isError($result)) {
  232.             return $result;
  233.         }
  234.         return $obj;
  235.     }
  236.  
  237.     // }}}
  238.     // {{{ connect()
  239.  
  240.     /**
  241.      * Create a new MDB2 connection object and connect to the specified
  242.      * database
  243.      *
  244.      * @param string|array|MDB2_Driver_Common  'data source name', see the
  245.      *              @see MDB2::parseDSN method for a description of the dsn format.
  246.      *               Can also be specified as an array of the
  247.      *               format returned by @see MDB2::parseDSN.
  248.      *               Finally you can also pass an existing db object to be used.
  249.      * @param array An associative array of option names and their values.
  250.      * @return bool|MDB2_ErrorMDB2_OK or error object
  251.      * @access public
  252.      * @see    MDB2::parseDSN
  253.      */
  254.     function connect(&$db$options = array())
  255.     {
  256.         $db_options = array();
  257.         if (is_array($options)) {
  258.             foreach ($options as $option => $value{
  259.                 if (array_key_exists($option$this->options)) {
  260.                     $result $this->setOption($option$value);
  261.                     if (PEAR::isError($result)) {
  262.                         return $result;
  263.                     }
  264.                 else {
  265.                     $db_options[$option$value;
  266.                 }
  267.             }
  268.         }
  269.         $this->disconnect();
  270.         if (!MDB2::isConnection($db)) {
  271.             $db =MDB2::factory($db$db_options);
  272.         }
  273.         if (PEAR::isError($db)) {
  274.             return $db;
  275.         }
  276.  
  277.         $this->db =$db;
  278.         $this->db->loadModule('Datatype');
  279.         $this->db->loadModule('Manager');
  280.         $this->db->loadModule('Reverse');
  281.         $this->db->loadModule('Function');
  282.         if (empty($this->options['valid_types'])) {
  283.             $this->options['valid_types'$this->db->datatype->getValidTypes();
  284.         }
  285.  
  286.         return MDB2_OK;
  287.     }
  288.  
  289.     // }}}
  290.     // {{{ disconnect()
  291.  
  292.     /**
  293.      * Log out and disconnect from the database.
  294.      *
  295.      * @access public
  296.      */
  297.     function disconnect()
  298.     {
  299.         if (MDB2::isConnection($this->db)) {
  300.             $this->db->disconnect();
  301.             unset($this->db);
  302.         }
  303.     }
  304.  
  305.     // }}}
  306.     // {{{ parseDatabaseDefinition()
  307.  
  308.     /**
  309.      * Parse a database definition from a file or an array
  310.      *
  311.      * @param string|arraythe database schema array or file name
  312.      * @param bool if non readable files should be skipped
  313.      * @param array associative array that the defines the text string values
  314.      *               that are meant to be used to replace the variables that are
  315.      *               used in the schema description.
  316.      * @param bool make function fail on invalid names
  317.      * @param array database structure definition
  318.      * @access public
  319.      */
  320.     function parseDatabaseDefinition($schema$skip_unreadable = false$variables = array(),
  321.         $fail_on_invalid_names = true$structure = false)
  322.     {
  323.         $database_definition = false;
  324.         if (is_string($schema)) {
  325.             // if $schema is not readable then we just skip it
  326.             // and simply copy the $current_schema file to that file name
  327.             if (is_readable($schema)) {
  328.                 $database_definition $this->parseDatabaseDefinitionFile(
  329.                     $schema$variables$fail_on_invalid_names$structure
  330.                 );
  331.             }
  332.         elseif (is_array($schema)) {
  333.             $database_definition $schema;
  334.         }
  335.         if (!$database_definition && !$skip_unreadable{
  336.             $database_definition $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  337.                 'invalid data type of schema or unreadable data source');
  338.         }
  339.         return $database_definition;
  340.     }
  341.  
  342.     // }}}
  343.     // {{{ parseDatabaseDefinitionFile()
  344.  
  345.     /**
  346.      * Parse a database definition file by creating a schema format
  347.      * parser object and passing the file contents as parser input data stream.
  348.      *
  349.      * @param string the database schema file.
  350.      * @param array associative array that the defines the text string values
  351.      *               that are meant to be used to replace the variables that are
  352.      *               used in the schema description.
  353.      * @param bool make function fail on invalid names
  354.      * @param array database structure definition
  355.      * @access public
  356.      */
  357.     function parseDatabaseDefinitionFile($input_file$variables = array(),
  358.         $fail_on_invalid_names = true$structure = false)
  359.     {
  360.         $dtd_file $this->options['dtd_file'];
  361.         if ($dtd_file{
  362.             require_once 'XML/DTD/XmlValidator.php';
  363.             $dtd =new XML_DTD_XmlValidator;
  364.             if (!$dtd->isValid($dtd_file$input_file)) {
  365.                 return $this->raiseError(MDB2_SCHEMA_ERROR_PARSEnullnull$dtd->getMessage());
  366.             }
  367.         }
  368.  
  369.         $class_name $this->options['parser'];
  370.         $result = MDB2::loadClass($class_name$this->db->getOption('debug'));
  371.         if (PEAR::isError($result)) {
  372.             return $result;
  373.         }
  374.  
  375.         $parser =new $class_name($variables$fail_on_invalid_names$structure$this->options['valid_types']$this->options['force_defaults']);
  376.         $result $parser->setInputFile($input_file);
  377.         if (PEAR::isError($result)) {
  378.             return $result;
  379.         }
  380.  
  381.         $result $parser->parse();
  382.         if (PEAR::isError($result)) {
  383.             return $result;
  384.         }
  385.         if (PEAR::isError($parser->error)) {
  386.             return $parser->error;
  387.         }
  388.  
  389.         return $parser->database_definition;
  390.     }
  391.  
  392.     // }}}
  393.     // {{{ getDefinitionFromDatabase()
  394.  
  395.     /**
  396.      * Attempt to reverse engineer a schema structure from an existing MDB2
  397.      * This method can be used if no xml schema file exists yet.
  398.      * The resulting xml schema file may need some manual adjustments.
  399.      *
  400.      * @return array|MDB2_Errorarray with definition or error object
  401.      * @access public
  402.      */
  403.     function getDefinitionFromDatabase()
  404.     {
  405.         $database $this->db->database_name;
  406.         if (empty($database)) {
  407.             return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  408.                 'it was not specified a valid database name');
  409.         }
  410.  
  411.         $class_name $this->options['validate'];
  412.         $result = MDB2::loadClass($class_name$this->db->getOption('debug'));
  413.         if (PEAR::isError($result)) {
  414.             return $result;
  415.         }
  416.  
  417.         $val =new $class_name($this->options['fail_on_invalid_names']$this->options['valid_types']$this->options['force_defaults']);
  418.  
  419.         $database_definition = array(
  420.             'name' => $database,
  421.             'create' => true,
  422.             'overwrite' => false,
  423.             'charset' => '',
  424.             'description' => '',
  425.             'comments' => '',
  426.             'tables' => array(),
  427.             'sequences' => array(),
  428.         );
  429.  
  430.         $tables $this->db->manager->listTables();
  431.         if (PEAR::isError($tables)) {
  432.             return $tables;
  433.         }
  434.  
  435.         foreach ($tables as $table_name{
  436.             $fields $this->db->manager->listTableFields($table_name);
  437.             if (PEAR::isError($fields)) {
  438.                 return $fields;
  439.             }
  440.  
  441.             $database_definition['tables'][$table_name= array(
  442.                 'was' => '',
  443.                 'description' => '',
  444.                 'comments' => '',
  445.                 'fields' => array(),
  446.                 'indexes' => array(),
  447.                 'constraints' => array(),
  448.                 'initialization' => array()
  449.             );
  450.  
  451.             $table_definition =$database_definition['tables'][$table_name];
  452.             foreach ($fields as $field_name{
  453.                 $definition $this->db->reverse->getTableFieldDefinition($table_name$field_name);
  454.                 if (PEAR::isError($definition)) {
  455.                     return $definition;
  456.                 }
  457.  
  458.                 if (!empty($definition[0]['autoincrement'])) {
  459.                     $definition[0]['default''0';
  460.                 }
  461.                 $table_definition['fields'][$field_name$definition[0];
  462.                 $field_choices count($definition);
  463.                 if ($field_choices > 1{
  464.                     $warning = "There are $field_choices type choices in the table $table_name field $field_name (#1 is the default): ";
  465.                     $field_choice_cnt = 1;
  466.                     $table_definition['fields'][$field_name]['choices'= array();
  467.                     foreach ($definition as $field_choice{
  468.                         $table_definition['fields'][$field_name]['choices'][$field_choice;
  469.                         $warning.= 'choice #'.($field_choice_cnt).': '.serialize($field_choice);
  470.                         $field_choice_cnt++;
  471.                     }
  472.                     $this->warnings[$warning;
  473.                 }
  474.  
  475.                 /**
  476.                  * The first parameter is used to verify if there are duplicated
  477.                  * fields which we can guarantee that won't happen when reverse engineering
  478.                  */
  479.                 $result $val->validateField(array()$table_definition['fields'][$field_name]$field_name);
  480.                 if (PEAR::isError($result)) {
  481.                     return $result;
  482.                 }
  483.             }
  484.  
  485.             $keys = array();
  486.             $indexes $this->db->manager->listTableIndexes($table_name);
  487.             if (PEAR::isError($indexes)) {
  488.                 return $indexes;
  489.             }
  490.  
  491.             if (is_array($indexes)) {
  492.                 foreach ($indexes as $index_name{
  493.                     $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  494.                     $definition $this->db->reverse->getTableIndexDefinition($table_name$index_name);
  495.                     $this->db->popExpect();
  496.                     if (PEAR::isError($definition)) {
  497.                         if (PEAR::isError($definitionMDB2_ERROR_NOT_FOUND)) {
  498.                             continue;
  499.                         }
  500.                         return $definition;
  501.                     }
  502.  
  503.                     $keys[$index_name$definition;
  504.                 }
  505.             }
  506.  
  507.             $constraints $this->db->manager->listTableConstraints($table_name);
  508.             if (PEAR::isError($constraints)) {
  509.                 return $constraints;
  510.             }
  511.  
  512.             if (is_array($constraints)) {
  513.                 foreach ($constraints as $constraint_name{
  514.                     $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  515.                     $definition $this->db->reverse->getTableConstraintDefinition($table_name$constraint_name);
  516.                     $this->db->popExpect();
  517.                     if (PEAR::isError($definition)) {
  518.                         if (PEAR::isError($definitionMDB2_ERROR_NOT_FOUND)) {
  519.                             continue;
  520.                         }
  521.                         return $definition;
  522.                     }
  523.  
  524.                     $keys[$constraint_name$definition;
  525.                 }
  526.             }
  527.  
  528.             foreach ($keys as $key_name => $definition{
  529.                 if (array_key_exists('foreign'$definition)
  530.                     && $definition['foreign']
  531.                 {
  532.                     /**
  533.                      * The first parameter is used to verify if there are duplicated
  534.                      * foreign keys which we can guarantee that won't happen when reverse engineering
  535.                      */
  536.                     $result $val->validateConstraint(array()$definition$key_name);
  537.                     if (PEAR::isError($result)) {
  538.                         return $result;
  539.                     }
  540.  
  541.                     foreach ($definition['fields'as $field_name => $field{
  542.                         /**
  543.                          * The first parameter is used to verify if there are duplicated
  544.                          * referencing fields which we can guarantee that won't happen when reverse engineering
  545.                          */
  546.                         $result $val->validateConstraintField(array()$field_name);
  547.                         if (PEAR::isError($result)) {
  548.                             return $result;
  549.                         }
  550.  
  551.                         $definition['fields'][$field_name'';
  552.                     }
  553.  
  554.                     foreach ($definition['references']['fields'as $field_name => $field{
  555.                         /**
  556.                          * The first parameter is used to verify if there are duplicated
  557.                          * referenced fields which we can guarantee that won't happen when reverse engineering
  558.                          */
  559.                         $result $val->validateConstraintReferencedField(array()$field_name);
  560.                         if (PEAR::isError($result)) {
  561.                             return $result;
  562.                         }
  563.  
  564.                         $definition['references']['fields'][$field_name'';
  565.                     }
  566.  
  567.                     $table_definition['constraints'][$key_name$definition;
  568.                 else {
  569.                     /**
  570.                      * The first parameter is used to verify if there are duplicated
  571.                      * indices which we can guarantee that won't happen when reverse engineering
  572.                      */
  573.                     $result $val->validateIndex(array()$definition$key_name);
  574.                     if (PEAR::isError($result)) {
  575.                         return $result;
  576.                     }
  577.  
  578.                     foreach ($definition['fields'as $field_name => $field{
  579.                         /**
  580.                          * The first parameter is used to verify if there are duplicated
  581.                          * index fields which we can guarantee that won't happen when reverse engineering
  582.                          */
  583.                         $result $val->validateIndexField(array()$field$field_name);
  584.                         if (PEAR::isError($result)) {
  585.                             return $result;
  586.                         }
  587.  
  588.                         $definition['fields'][$field_name$field;
  589.                     }
  590.  
  591.                     $table_definition['indexes'][$key_name$definition;
  592.                 }
  593.             }
  594.  
  595.             /**
  596.              * The first parameter is used to verify if there are duplicated
  597.              * tables which we can guarantee that won't happen when reverse engineering
  598.              */
  599.             $result $val->validateTable(array()$table_definition$table_name);
  600.             if (PEAR::isError($result)) {
  601.                 return $result;
  602.             }
  603.             
  604.         }
  605.  
  606.         $sequences $this->db->manager->listSequences();
  607.         if (PEAR::isError($sequences)) {
  608.             return $sequences;
  609.         }
  610.  
  611.         if (is_array($sequences)) {
  612.             foreach ($sequences as $sequence_name{
  613.                 $definition $this->db->reverse->getSequenceDefinition($sequence_name);
  614.                 if (PEAR::isError($definition)) {
  615.                     return $definition;
  616.                 }
  617.                 if (isset($database_definition['tables'][$sequence_name])
  618.                     && isset($database_definition['tables'][$sequence_name]['indexes'])
  619.                 {
  620.                     foreach ($database_definition['tables'][$sequence_name]['indexes'as $index{
  621.                         if (isset($index['primary']&& $index['primary']
  622.                             && count($index['fields'== 1)
  623.                         {
  624.                             $definition['on'= array(
  625.                                 'table' => $sequence_name,
  626.                                 'field' => key($index['fields']),
  627.                             );
  628.                             break;
  629.                         }
  630.                     }
  631.                 }
  632.  
  633.                 /**
  634.                  * The first parameter is used to verify if there are duplicated
  635.                  * sequences which we can guarantee that won't happen when reverse engineering
  636.                  */
  637.                 $result $val->validateSequence(array()$definition$sequence_name);
  638.                 if (PEAR::isError($result)) {
  639.                     return $result;
  640.                 }
  641.  
  642.                 $database_definition['sequences'][$sequence_name$definition;
  643.             }
  644.         }
  645.  
  646.         $result $val->validateDatabase($database_definition);
  647.         if (PEAR::isError($result)) {
  648.             return $result;
  649.         }
  650.  
  651.         return $database_definition;
  652.     }
  653.  
  654.     // }}}
  655.     // {{{ createTableIndexes()
  656.  
  657.     /**
  658.      * A method to create indexes for an existing table
  659.      *
  660.      * @param string  Name of the table
  661.      * @param array   An array of indexes to be created
  662.      * @param boolean If the table/index should be overwritten if it already exists
  663.      * @return mixed  MDB2_Error if there is an error creating an index, MDB2_OK otherwise
  664.      * @access public
  665.      */
  666.     function createTableIndexes($table_name$indexes$overwrite = false)
  667.     {
  668.         if (!$this->db->supports('indexes')) {
  669.             $this->db->debug('Indexes are not supported'__FUNCTION__);
  670.             return MDB2_OK;
  671.         }
  672.  
  673.         $errorcodes = array(MDB2_ERROR_UNSUPPORTEDMDB2_ERROR_NOT_CAPABLE);
  674.         foreach ($indexes as $index_name => $index{
  675.  
  676.             // Does the index already exist, and if so, should it be overwritten?
  677.             $create_index = true;
  678.             $this->db->expectError($errorcodes);
  679.             if (!empty($index['primary']|| !empty($index['unique'])) {
  680.                 $current_indexes $this->db->manager->listTableConstraints($table_name);
  681.             else {
  682.                 $current_indexes $this->db->manager->listTableIndexes($table_name);
  683.             }
  684.             $this->db->popExpect();
  685.             if (PEAR::isError($current_indexes)) {
  686.                 if (!MDB2::isError($current_indexes$errorcodes)) {
  687.                     return $current_indexes;
  688.                 }
  689.             elseif (is_array($current_indexes&& in_array($index_name$current_indexes)) {
  690.                 if (!$overwrite{
  691.                     $this->db->debug('Index already exists: '.$index_name__FUNCTION__);
  692.                     $create_index = false;
  693.                 else {
  694.                     $this->db->debug('Preparing to overwrite index: '.$index_name__FUNCTION__);
  695.  
  696.                     $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  697.                     if (!empty($index['primary']|| !empty($index['unique'])) {
  698.                         $result $this->db->manager->dropConstraint($table_name$index_name);
  699.                     else {
  700.                         $result $this->db->manager->dropIndex($table_name$index_name);
  701.                     }
  702.                     $this->db->popExpect();
  703.                     if (PEAR::isError($result&& !MDB2::isError($resultMDB2_ERROR_NOT_FOUND)) {
  704.                         return $result;
  705.                     }
  706.                 }
  707.             }
  708.  
  709.             // Check if primary is being used and if it's supported
  710.             if (!empty($index['primary']&& !$this->db->supports('primary_key')) {
  711.  
  712.                 // Primary not supported so we fallback to UNIQUE and making the field NOT NULL
  713.                 $index['unique'= true;
  714.                 $changes = array();
  715.  
  716.                 foreach ($index['fields'as $field => $empty{
  717.                     $field_info $this->db->reverse->getTableFieldDefinition($table_name$field);
  718.                     if (PEAR::isError($field_info)) {
  719.                         return $field_info;
  720.                     }
  721.                     if (!$field_info[0]['notnull']{
  722.                         $changes['change'][$field$field_info[0];
  723.                         $changes['change'][$field]['notnull'= true;
  724.                     }
  725.                 }
  726.                 if (!empty($changes)) {
  727.                     $this->db->manager->alterTable($table_name$changesfalse);
  728.                 }
  729.             }
  730.  
  731.             // Should the index be created?
  732.             if ($create_index{
  733.                 if (!empty($index['primary']|| !empty($index['unique'])) {
  734.                     $result $this->db->manager->createConstraint($table_name$index_name$index);
  735.                 else {
  736.                     $result $this->db->manager->createIndex($table_name$index_name$index);
  737.                 }
  738.                 if (PEAR::isError($result)) {
  739.                     return $result;
  740.                 }
  741.             }
  742.         }
  743.         return MDB2_OK;
  744.     }
  745.  
  746.     // }}}
  747.     // {{{ createTableConstraints()
  748.  
  749.     /**
  750.      * A method to create foreign keys for an existing table
  751.      *
  752.      * @param string  Name of the table
  753.      * @param array   An array of foreign keys to be created
  754.      * @param boolean If the foreign key should be overwritten if it already exists
  755.      * @return mixed  MDB2_Error if there is an error creating a foreign key, MDB2_OK otherwise
  756.      * @access public
  757.      */
  758.     function createTableConstraints($table_name$constraints$overwrite = false)
  759.     {
  760.         if (!$this->db->supports('indexes')) {
  761.             $this->db->debug('Indexes are not supported'__FUNCTION__);
  762.             return MDB2_OK;
  763.         }
  764.  
  765.         $errorcodes = array(MDB2_ERROR_UNSUPPORTEDMDB2_ERROR_NOT_CAPABLE);
  766.         foreach ($constraints as $constraint_name => $constraint{
  767.  
  768.             // Does the foreign key already exist, and if so, should it be overwritten?
  769.             $create_constraint = true;
  770.             $this->db->expectError($errorcodes);
  771.             $current_constraints $this->db->manager->listTableConstraints($table_name);
  772.             $this->db->popExpect();
  773.             if (PEAR::isError($current_constraints)) {
  774.                 if (!MDB2::isError($current_constraints$errorcodes)) {
  775.                     return $current_constraints;
  776.                 }
  777.             elseif (is_array($current_constraints&& in_array($constraint_name$current_constraints)) {
  778.                 if (!$overwrite{
  779.                     $this->db->debug('Foreign key already exists: '.$constraint_name__FUNCTION__);
  780.                     $create_constraint = false;
  781.                 else {
  782.                     $this->db->debug('Preparing to overwrite foreign key: '.$constraint_name__FUNCTION__);
  783.                     $result $this->db->manager->dropConstraint($table_name$constraint_name);
  784.                     if (PEAR::isError($result)) {
  785.                         return $result;
  786.                     }
  787.                 }
  788.             }
  789.  
  790.             // Should the foreign key be created?
  791.             if ($create_constraint{
  792.                 $result $this->db->manager->createConstraint($table_name$constraint_name$constraint);
  793.                 if (PEAR::isError($result)) {
  794.                     return $result;
  795.                 }
  796.             }
  797.         }
  798.         return MDB2_OK;
  799.     }
  800.  
  801.     // }}}
  802.     // {{{ createTable()
  803.  
  804.     /**
  805.      * Create a table and inititialize the table if data is available
  806.      *
  807.      * @param string name of the table to be created
  808.      * @param array  multi dimensional array that contains the
  809.      *                structure and optional data of the table
  810.      * @param bool   if the table/index should be overwritten if it already exists
  811.      * @param array  an array of options to be passed to the database specific driver
  812.      *                version of MDB2_Driver_Manager_Common::createTable().
  813.      * @return bool|MDB2_ErrorMDB2_OK or error object
  814.      * @access public
  815.      */
  816.     function createTable($table_name$table$overwrite = false$options = array())
  817.     {
  818.         $create = true;
  819.         $errorcodes = array(MDB2_ERROR_UNSUPPORTEDMDB2_ERROR_NOT_CAPABLE);
  820.         $this->db->expectError($errorcodes);
  821.         $tables $this->db->manager->listTables();
  822.         $this->db->popExpect();
  823.         if (PEAR::isError($tables)) {
  824.             if (!MDB2::isError($tables$errorcodes)) {
  825.                 return $tables;
  826.             }
  827.         elseif (is_array($tables&& in_array($table_name$tables)) {
  828.             if (!$overwrite{
  829.                 $create = false;
  830.                 $this->db->debug('Table already exists: '.$table_name__FUNCTION__);
  831.             else {
  832.                 $result $this->db->manager->dropTable($table_name);
  833.                 if (PEAR::isError($result)) {
  834.                     return $result;
  835.                 }
  836.                 $this->db->debug('Overwritting table: '.$table_name__FUNCTION__);
  837.             }
  838.         }
  839.  
  840.         if ($create{
  841.             $result $this->db->manager->createTable($table_name$table['fields']$options);
  842.             if (PEAR::isError($result)) {
  843.                 return $result;
  844.             }
  845.         }
  846.  
  847.         if (!empty($table['initialization']&& is_array($table['initialization'])) {
  848.             $result $this->initializeTable($table_name$table);
  849.             if (PEAR::isError($result)) {
  850.                 return $result;
  851.             }
  852.         }
  853.  
  854.         if (!empty($table['indexes']&& is_array($table['indexes'])) {
  855.             $result $this->createTableIndexes($table_name$table['indexes']$overwrite);
  856.             if (PEAR::isError($result)) {
  857.                 return $result;
  858.             }
  859.         }
  860.  
  861.         if (!empty($table['constraints']&& is_array($table['constraints'])) {
  862.             $result $this->createTableConstraints($table_name$table['constraints']$overwrite);
  863.             if (PEAR::isError($result)) {
  864.                 return $result;
  865.             }
  866.         }
  867.  
  868.         return MDB2_OK;
  869.     }
  870.  
  871.     // }}}
  872.     // {{{ initializeTable()
  873.  
  874.     /**
  875.      * Inititialize the table with data
  876.      *
  877.      * @param string name of the table
  878.      * @param array  multi dimensional array that contains the
  879.      *                structure and optional data of the table
  880.      * @return bool|MDB2_ErrorMDB2_OK or error object
  881.      * @access public
  882.      */
  883.     function initializeTable($table_name$table)
  884.     {
  885.         $query_insert 'INSERT INTO %s (%s) VALUES (%s)';
  886.         $query_insertselect 'INSERT INTO %s (%s) (SELECT %s FROM %s %s)';
  887.         $query_update 'UPDATE %s SET %s %s';
  888.         $query_delete 'DELETE FROM %s %s';
  889.  
  890.         $table_name $this->db->quoteIdentifier($table_nametrue);
  891.  
  892.         $result = MDB2_OK;
  893.         $support_transactions $this->db->supports('transactions');
  894.  
  895.         foreach ($table['initialization'as $instruction{
  896.             $query '';
  897.             switch ($instruction['type']{
  898.             case 'insert':
  899.                 if (!isset($instruction['data']['select'])) {
  900.                     $data $this->getInstructionFields($instruction['data']$table['fields']);
  901.                     if (!empty($data)) {
  902.                         $fields implode(', 'array_keys($data));
  903.                         $values implode(', 'array_values($data));
  904.  
  905.                         $query sprintf($query_insert$table_name$fields$values);
  906.                     }
  907.                 else {
  908.                     $data $this->getInstructionFields($instruction['data']['select']$table['fields']);
  909.                     $where $this->getInstructionWhere($instruction['data']['select']$table['fields']);
  910.                     $select_table_name $this->db->quoteIdentifier($instruction['data']['select']['table']true);
  911.                     if (!empty($data)) {
  912.                         $fields implode(', 'array_keys($data));
  913.                         $values implode(', 'array_values($data));
  914.  
  915.                         $query sprintf($query_insertselect$table_name$fields$values$select_table_name$where);
  916.                     }
  917.                 }
  918.                 break;
  919.             case 'update':
  920.                 $data $this->getInstructionFields($instruction['data']$table['fields']);
  921.                 $where $this->getInstructionWhere($instruction['data']$table['fields']);
  922.                 if (!empty($data)) {
  923.                     array_walk($dataarray($this'buildFieldValue'));
  924.                     $fields_values implode(', '$data);
  925.  
  926.                     $query sprintf($query_update$table_name$fields_values$where);
  927.                 }
  928.                 break;
  929.             case 'delete':
  930.                 $where $this->getInstructionWhere($instruction['data']$table['fields']);
  931.                 $query sprintf($query_delete$table_name$where);
  932.                 break;
  933.             }
  934.             if ($query{
  935.                 if ($support_transactions && PEAR::isError($res $this->db->beginNestedTransaction())) {
  936.                     return $res;
  937.                 }
  938.  
  939.                 $result $this->db->exec($query);
  940.                 if (PEAR::isError($result)) {
  941.                     return $result;
  942.                 }
  943.  
  944.                 if ($support_transactions && PEAR::isError($res $this->db->completeNestedTransaction())) {
  945.                     return $res;
  946.                 }
  947.             }
  948.         }
  949.         return $result;
  950.     }
  951.  
  952.     // }}}
  953.     // {{{ buildFieldValue()
  954.  
  955.     /**
  956.      * Appends the contents of second argument + '=' to the beginning of first
  957.      * argument.
  958.      *
  959.      * Used with array_walk() in initializeTable() for UPDATEs.
  960.      *
  961.      * @param string  value of array's element
  962.      * @param string  key of array's element
  963.      *
  964.      * @return void 
  965.      *
  966.      * @access public
  967.      * @see MDB2_Schema::initializeTable()
  968.      */
  969.     function buildFieldValue(&$element$key)
  970.     {
  971.        $element $key."=$element";
  972.     }
  973.  
  974.     // }}}
  975.     // {{{ getExpression()
  976.  
  977.     /**
  978.      * Generates a string that represents a value that would be associated
  979.      * with a column in a DML instruction.
  980.      *
  981.      * @param array  multi dimensional array that represents the parsed field
  982.      *                 of a DML instruction.
  983.      * @param array  multi dimensional array that contains the
  984.      *                 definition for current table's fields.
  985.      * @param string  type of given field
  986.      *
  987.      * @return string 
  988.      *
  989.      * @access public
  990.      * @see MDB2_Schema::getInstructionFields(), MDB2_Schema::getInstructionWhere()
  991.      */
  992.     function getExpression($element$fields_definition = array()$type = null)
  993.     {
  994.         $str '';
  995.         switch ($element['type']{
  996.             case 'null':
  997.                 $str.= 'NULL';
  998.             break;
  999.             case 'value':
  1000.                 $str.= $this->db->quote($element['data']$type);
  1001.             break;
  1002.             case 'column':
  1003.                 $str.= $this->db->quoteIdentifier($element['data']true);
  1004.             break;
  1005.             case 'function':
  1006.                 $arguments = array();
  1007.                 if (!empty($element['data']['arguments'])
  1008.                     && is_array($element['data']['arguments'])
  1009.                 {
  1010.                     foreach ($element['data']['arguments'as $v{
  1011.                         $arguments[$this->getExpression($v$fields_definition);
  1012.                     }
  1013.                 }
  1014.                 if (method_exists($this->db->function$element['data']['name'])) {
  1015.                     $str.= call_user_func_array(
  1016.                         array(&$this->db->function$element['data']['name']),
  1017.                         $arguments
  1018.                     );
  1019.                 else {
  1020.                     $str.= $element['data']['name'].'(';
  1021.                     $str.= implode(', '$arguments);
  1022.                     $str.= ')';
  1023.                 }
  1024.             break;
  1025.             case 'expression':
  1026.                 $type0 $type1 = null;
  1027.                 if ($element['data']['operants'][0]['type'== 'column'
  1028.                     && array_key_exists($element['data']['operants'][0]['data']$fields_definition)
  1029.                 {
  1030.                     $type0 $fields_definition[$element['data']['operants'][0]['data']]['type'];
  1031.                 }
  1032.                 if ($element['data']['operants'][1]['type'== 'column'
  1033.                     && array_key_exists($element['data']['operants'][1]['data']$fields_definition)
  1034.                 {
  1035.                     $type1 $fields_definition[$element['data']['operants'][1]['data']]['type'];
  1036.                 }
  1037.                 $str.= '(';
  1038.                 $str.= $this->getExpression($element['data']['operants'][0]$fields_definition$type1);
  1039.                 $str.= $this->getOperator($element['data']['operator']);
  1040.                 $str.= $this->getExpression($element['data']['operants'][1]$fields_definition$type0);
  1041.                 $str.= ')';
  1042.             break;
  1043.         }
  1044.         return $str;
  1045.     }
  1046.  
  1047.     // }}}
  1048.     // {{{ getOperator()
  1049.  
  1050.     /**
  1051.      * Returns the matching SQL operator
  1052.      *
  1053.      * @param string parsed descriptive operator
  1054.      *
  1055.      * @return string matching SQL operator
  1056.      *
  1057.      * @access public
  1058.      * @static
  1059.      * @see MDB2_Schema::getExpression()
  1060.      */
  1061.     function getOperator($op)
  1062.     {
  1063.         switch ($op{
  1064.         case 'PLUS':
  1065.             return ' + ';
  1066.         case 'MINUS':
  1067.             return ' - ';
  1068.         case 'TIMES':
  1069.             return ' * ';
  1070.         case 'DIVIDED':
  1071.             return ' / ';
  1072.         case 'EQUAL':
  1073.             return ' = ';
  1074.         case 'NOT EQUAL':
  1075.             return ' != ';
  1076.         case 'LESS THAN':
  1077.             return ' < ';
  1078.         case 'GREATER THAN':
  1079.             return ' > ';
  1080.         case 'LESS THAN OR EQUAL':
  1081.             return ' <= ';
  1082.         case 'GREATER THAN OR EQUAL':
  1083.             return ' >= ';
  1084.         default:
  1085.             return ' '.$op.' ';
  1086.         }
  1087.     }
  1088.  
  1089.     // }}}
  1090.     // {{{ getInstructionFields()
  1091.  
  1092.     /**
  1093.      * Walks the parsed DML instruction array, field by field,
  1094.      * storing them and their processed values inside a new array.
  1095.      *
  1096.      * @param array  multi dimensional array that contains the parsed
  1097.      *                 DML instruction to be processed.
  1098.      * @param array  multi dimensional array that contains the
  1099.      *                 definition for current table's fields.
  1100.      *
  1101.      * @return array  array of strings in the form 'field_name' => 'value'
  1102.      *
  1103.      * @access public
  1104.      * @static
  1105.      * @see MDB2_Schema::initializeTable()
  1106.      */
  1107.     function getInstructionFields($instruction$fields_definition = array())
  1108.     {
  1109.         $fields = array();
  1110.         if (!empty($instruction['field']&& is_array($instruction['field'])) {
  1111.             foreach ($instruction['field'as $field{
  1112.                 $field_name $this->db->quoteIdentifier($field['name']true);
  1113.                 $fields[$field_name$this->getExpression($field['group']$fields_definition);
  1114.             }
  1115.         }
  1116.         return $fields;
  1117.     }
  1118.  
  1119.     // }}}
  1120.     // {{{ getInstructionWhere()
  1121.  
  1122.     /**
  1123.      * Translates the parsed WHERE expression of a DML instruction
  1124.      * (array structure) to a SQL WHERE clause (string).
  1125.      *
  1126.      * @param array  multi dimensional array that contains the
  1127.      *                 structure of the current DML instruction.
  1128.      * @param array  multi dimensional array that contains the
  1129.      *                 definition for current table's fields.
  1130.      *
  1131.      * @return string SQL WHERE clause
  1132.      *
  1133.      * @access public
  1134.      * @static
  1135.      * @see MDB2_Schema::initializeTable()
  1136.      */
  1137.     function getInstructionWhere($instruction$fields_definition = array())
  1138.     {
  1139.         $where '';
  1140.         if (!empty($instruction['where'])) {
  1141.             $where 'WHERE '.$this->getExpression($instruction['where']$fields_definition);
  1142.         }
  1143.         return $where;
  1144.     }
  1145.  
  1146.     // }}}
  1147.     // {{{ createSequence()
  1148.  
  1149.     /**
  1150.      * Create a sequence
  1151.      *
  1152.      * @param string name of the sequence to be created
  1153.      * @param array  multi dimensional array that contains the
  1154.      *                structure and optional data of the table
  1155.      * @param bool  if the sequence should be overwritten if it already exists
  1156.      * @return bool|MDB2_ErrorMDB2_OK or error object
  1157.      * @access public
  1158.      */
  1159.     function createSequence($sequence_name$sequence$overwrite = false)
  1160.     {
  1161.         if (!$this->db->supports('sequences')) {
  1162.             $this->db->debug('Sequences are not supported'__FUNCTION__);
  1163.             return MDB2_OK;
  1164.         }
  1165.  
  1166.         $errorcodes = array(MDB2_ERROR_UNSUPPORTEDMDB2_ERROR_NOT_CAPABLE);
  1167.         $this->db->expectError($errorcodes);
  1168.         $sequences $this->db->manager->listSequences();
  1169.         $this->db->popExpect();
  1170.         if (PEAR::isError($sequences)) {
  1171.             if (!MDB2::isError($sequences$errorcodes)) {
  1172.                 return $sequences;
  1173.             }
  1174.         elseif (is_array($sequence&& in_array($sequence_name$sequences)) {
  1175.             if (!$overwrite{
  1176.                 $this->db->debug('Sequence already exists: '.$sequence_name__FUNCTION__);
  1177.                 return MDB2_OK;
  1178.             }
  1179.  
  1180.             $result $this->db->manager->dropSequence($sequence_name);
  1181.             if (PEAR::isError($result)) {
  1182.                 return $result;
  1183.             }
  1184.             $this->db->debug('Overwritting sequence: '.$sequence_name__FUNCTION__);
  1185.         }
  1186.  
  1187.         $start = 1;
  1188.         $field '';
  1189.         if (!empty($sequence['on'])) {
  1190.             $table $sequence['on']['table'];
  1191.             $field $sequence['on']['field'];
  1192.  
  1193.             $errorcodes = array(MDB2_ERROR_UNSUPPORTEDMDB2_ERROR_NOT_CAPABLE);
  1194.             $this->db->expectError($errorcodes);
  1195.             $tables $this->db->manager->listTables();
  1196.             $this->db->popExpect();
  1197.             if (PEAR::isError($tables&& !MDB2::isError($tables$errorcodes)) {
  1198.                  return $tables;
  1199.             }
  1200.  
  1201.             if (!PEAR::isError($tables&& is_array($tables&& in_array($table$tables)) {
  1202.                 if ($this->db->supports('summary_functions')) {
  1203.                     $query = "SELECT MAX($field) FROM ".$this->db->quoteIdentifier($tabletrue);
  1204.                 else {
  1205.                     $query = "SELECT $field FROM ".$this->db->quoteIdentifier($tabletrue)." ORDER BY $field DESC";
  1206.                 }
  1207.                 $start $this->db->queryOne($query'integer');
  1208.                 if (PEAR::isError($start)) {
  1209.                     return $start;
  1210.                 }
  1211.                 ++$start;
  1212.             else {
  1213.                 $this->warnings['Could not sync sequence: '.$sequence_name;
  1214.             }
  1215.         elseif (!empty($sequence['start']&& is_numeric($sequence['start'])) {
  1216.             $start $sequence['start'];
  1217.             $table '';
  1218.         }
  1219.  
  1220.         $result $this->db->manager->createSequence($sequence_name$start);
  1221.         if (PEAR::isError($result)) {
  1222.             return $result;
  1223.         }
  1224.  
  1225.         return MDB2_OK;
  1226.     }
  1227.  
  1228.     // }}}
  1229.     // {{{ createDatabase()
  1230.  
  1231.     /**
  1232.      * Create a database space within which may be created database objects
  1233.      * like tables, indexes and sequences. The implementation of this function
  1234.      * is highly DBMS specific and may require special permissions to run
  1235.      * successfully. Consult the documentation or the DBMS drivers that you
  1236.      * use to be aware of eventual configuration requirements.
  1237.      *
  1238.      * @param array multi dimensional array that contains the current definition
  1239.      * @param array  an array of options to be passed to the database specific driver
  1240.      *                version of MDB2_Driver_Manager_Common::createTable().
  1241.      * @return bool|MDB2_ErrorMDB2_OK or error object
  1242.      * @access public
  1243.      */
  1244.     function createDatabase($database_definition$options = array())
  1245.     {
  1246.         if (!isset($database_definition['name']|| !$database_definition['name']{
  1247.             return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1248.                 'no valid database name specified');
  1249.         }
  1250.         $create (isset($database_definition['create']&& $database_definition['create']);
  1251.         $overwrite (isset($database_definition['overwrite']&& $database_definition['overwrite']);
  1252.  
  1253.         /**
  1254.          *
  1255.          * We need to clean up database name before any query to prevent
  1256.          * database driver from using a inexistent database
  1257.          *
  1258.          */
  1259.         $previous_database_name $this->db->setDatabase('');
  1260.  
  1261.         // Lower / Upper case the db name if the portability deems so.
  1262.         if ($this->db->options['portability'MDB2_PORTABILITY_FIX_CASE{
  1263.             $func $this->db->options['field_case'== CASE_LOWER ? 'strtolower' 'strtoupper';
  1264.             $db_name $func($database_definition['name']);
  1265.         }
  1266.  
  1267.         if ($create{
  1268.             if ($overwrite{
  1269.                 $this->db->expectError(MDB2_ERROR_CANNOT_DROP);
  1270.                 $result $this->db->manager->dropDatabase($database_definition['name']);
  1271.                 $this->db->popExpect();
  1272.                 if (PEAR::isError($result&& !MDB2::isError($resultMDB2_ERROR_CANNOT_DROP)) {
  1273.                     return $result;
  1274.                 }
  1275.                 $this->db->debug('Overwritting database: '.$database_definition['name']__FUNCTION__);
  1276.             }
  1277.  
  1278.             $errorcodes = array(MDB2_ERROR_UNSUPPORTEDMDB2_ERROR_UNSUPPORTED);
  1279.             $this->db->expectError($errorcodes);
  1280.             $dbOptions = array();
  1281.             if (isset($database_definition['charset'])) {
  1282.                 $dbOptions['charset'$database_definition['charset'];
  1283.             }
  1284.             $result $this->db->manager->createDatabase($database_definition['name']$dbOptions);
  1285.             $this->db->popExpect();
  1286.             if (PEAR::isError($result&& !MDB2::isError($resultMDB2_ERROR_UNSUPPORTED)) {
  1287.                 if (MDB2::isError($resultMDB2_ERROR_ALREADY_EXISTS)) {
  1288.                     $this->db->debug('Database already exists: ' $database_definition['name']__FUNCTION__);
  1289.                     if (!empty($dbOptions)) {
  1290.                         $result $this->db->manager->alterDatabase($database_definition['name']$dbOptions);
  1291.                         if (PEAR::isError($result)) {
  1292.                             return $result;
  1293.                         }
  1294.                     }
  1295.                     $create = false;
  1296.                 else {
  1297.                     return $result;
  1298.                 }
  1299.             }
  1300.         }
  1301.  
  1302.         $this->db->setDatabase($database_definition['name']);
  1303.         if (($support_transactions $this->db->supports('transactions'))
  1304.             && PEAR::isError($result $this->db->beginNestedTransaction())
  1305.         {
  1306.             return $result;
  1307.         }
  1308.  
  1309.         $created_objects = 0;
  1310.         if (isset($database_definition['tables'])
  1311.             && is_array($database_definition['tables'])
  1312.         {
  1313.             foreach ($database_definition['tables'as $table_name => $table{
  1314.                 $result $this->createTable($table_name$table$overwrite$options);
  1315.                 if (PEAR::isError($result)) {
  1316.                     break;
  1317.                 }
  1318.                 $created_objects++;
  1319.             }
  1320.         }
  1321.         if (!PEAR::isError($result)
  1322.             && isset($database_definition['sequences'])
  1323.             && is_array($database_definition['sequences'])
  1324.         {
  1325.             foreach ($database_definition['sequences'as $sequence_name => $sequence{
  1326.                 $result $this->createSequence($sequence_name$sequencefalse$overwrite);
  1327.  
  1328.                 if (PEAR::isError($result)) {
  1329.                     break;
  1330.                 }
  1331.                 $created_objects++;
  1332.             }
  1333.         }
  1334.  
  1335.         if ($support_transactions{
  1336.             $res $this->db->completeNestedTransaction();
  1337.             if (PEAR::isError($res)) {
  1338.                 $result $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  1339.                     'Could not end transaction ('.
  1340.                     $res->getMessage().' ('.$res->getUserinfo().'))');
  1341.             }
  1342.         elseif (PEAR::isError($result&& $created_objects{
  1343.             $result $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  1344.                 'the database was only partially created ('.
  1345.                 $result->getMessage().' ('.$result->getUserinfo().'))');
  1346.         }
  1347.  
  1348.         $this->db->setDatabase($previous_database_name);
  1349.  
  1350.         if (PEAR::isError($result&& $create
  1351.             && PEAR::isError($result2 $this->db->manager->dropDatabase($database_definition['name']))
  1352.         {
  1353.             if (!MDB2::isError($result2MDB2_ERROR_UNSUPPORTED)) {
  1354.                 return $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  1355.                        'Could not drop the created database after unsuccessful creation attempt ('.
  1356.                        $result2->getMessage().' ('.$result2->getUserinfo().'))');
  1357.             }
  1358.         }
  1359.  
  1360.         return $result;
  1361.     }
  1362.  
  1363.     // }}}
  1364.     // {{{ compareDefinitions()
  1365.  
  1366.     /**
  1367.      * Compare a previous definition with the currently parsed definition
  1368.      *
  1369.      * @param array multi dimensional array that contains the current definition
  1370.      * @param array multi dimensional array that contains the previous definition
  1371.      * @return array|MDB2_Errorarray of changes on success, or a error object
  1372.      * @access public
  1373.      */
  1374.     function compareDefinitions($current_definition$previous_definition)
  1375.     {
  1376.         $changes = array();
  1377.  
  1378.         if (!empty($current_definition['tables']&& is_array($current_definition['tables'])) {
  1379.             $changes['tables'$defined_tables = array();
  1380.             foreach ($current_definition['tables'as $table_name => $table{
  1381.                 $previous_tables = array();
  1382.                 if (!empty($previous_definition&& is_array($previous_definition)) {
  1383.                     $previous_tables $previous_definition['tables'];
  1384.                 }
  1385.                 $change $this->compareTableDefinitions($table_name$table$previous_tables$defined_tables);
  1386.                 if (PEAR::isError($change)) {
  1387.                     return $change;
  1388.                 }
  1389.                 if (!empty($change)) {
  1390.                     $changes['tables'MDB2_Schema::arrayMergeClobber($changes['tables']$change);
  1391.                 }
  1392.             }
  1393.             if (!empty($previous_definition['tables']&& is_array($previous_definition['tables'])) {
  1394.                 foreach ($previous_definition['tables'as $table_name => $table{
  1395.                     if (empty($defined_tables[$table_name])) {
  1396.                         $changes['remove'][$table_name= true;
  1397.                     }
  1398.                 }
  1399.             }
  1400.         }
  1401.         if (!empty($current_definition['sequences']&& is_array($current_definition['sequences'])) {
  1402.             $changes['sequences'$defined_sequences = array();
  1403.             foreach ($current_definition['sequences'as $sequence_name => $sequence{
  1404.                 $previous_sequences = array();
  1405.                 if (!empty($previous_definition&& is_array($previous_definition)) {
  1406.                     $previous_sequences $previous_definition['sequences'];
  1407.                 }
  1408.                 $change $this->compareSequenceDefinitions(
  1409.                     $sequence_name,
  1410.                     $sequence,
  1411.                     $previous_sequences,
  1412.                     $defined_sequences
  1413.                 );
  1414.                 if (PEAR::isError($change)) {
  1415.                     return $change;
  1416.                 }
  1417.                 if (!empty($change)) {
  1418.                     $changes['sequences'MDB2_Schema::arrayMergeClobber($changes['sequences']$change);
  1419.                 }
  1420.             }
  1421.             if (!empty($previous_definition['sequences']&& is_array($previous_definition['sequences'])) {
  1422.                 foreach ($previous_definition['sequences'as $sequence_name => $sequence{
  1423.                     if (empty($defined_sequences[$sequence_name])) {
  1424.                         $changes['remove'][$sequence_name= true;
  1425.                     }
  1426.                 }
  1427.             }
  1428.         }
  1429.         return $changes;
  1430.     }
  1431.  
  1432.     // }}}
  1433.     // {{{ compareTableFieldsDefinitions()
  1434.  
  1435.     /**
  1436.      * Compare a previous definition with the currently parsed definition
  1437.      *
  1438.      * @param string name of the table
  1439.      * @param array multi dimensional array that contains the current definition
  1440.      * @param array multi dimensional array that contains the previous definition
  1441.      * @return array|MDB2_Errorarray of changes on success, or a error object
  1442.      * @access public
  1443.      */
  1444.     function compareTableFieldsDefinitions($table_name$current_definition,
  1445.         $previous_definition)
  1446.     {
  1447.         $changes $defined_fields = array();
  1448.  
  1449.         if (is_array($current_definition)) {
  1450.             foreach ($current_definition as $field_name => $field{
  1451.                 $was_field_name $field['was'];
  1452.                 if (!empty($previous_definition[$field_name])
  1453.                     && (
  1454.                         (isset($previous_definition[$field_name]['was'])
  1455.                          && $previous_definition[$field_name]['was'== $was_field_name)
  1456.                         || !isset($previous_definition[$was_field_name])
  1457.                        )
  1458.                 {
  1459.                     $was_field_name $field_name;
  1460.                 }
  1461.                 if (!empty($previous_definition[$was_field_name])) {
  1462.                     if ($was_field_name != $field_name{
  1463.                         $changes['rename'][$was_field_name= array('name' => $field_name'definition' => $field);
  1464.                     }
  1465.                     if (!empty($defined_fields[$was_field_name])) {
  1466.                         return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1467.                             'the field "'.$was_field_name.
  1468.                             '" was specified for more than one field of table');
  1469.                     }
  1470.                     $defined_fields[$was_field_name= true;
  1471.                     $change $this->db->compareDefinition($field$previous_definition[$was_field_name]);
  1472.                     if (PEAR::isError($change)) {
  1473.                         return $change;
  1474.                     }
  1475.                     if (!empty($change)) {
  1476.                         $change['definition'$field;
  1477.                         $changes['change'][$field_name$change;
  1478.                     }
  1479.                 else {
  1480.                     if ($field_name != $was_field_name{
  1481.                         return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1482.                             'it was specified a previous field name ("'.
  1483.                             $was_field_name.'") for field "'.$field_name.'" of table "'.
  1484.                             $table_name.'" that does not exist');
  1485.                     }
  1486.                     $changes['add'][$field_name$field;
  1487.                 }
  1488.             }
  1489.         }
  1490.         if (isset($previous_definition&& is_array($previous_definition)) {
  1491.             foreach ($previous_definition as $field_previous_name => $field_previous{
  1492.                 if (empty($defined_fields[$field_previous_name])) {
  1493.                     $changes['remove'][$field_previous_name= true;
  1494.                 }
  1495.             }
  1496.         }
  1497.         return $changes;
  1498.     }
  1499.  
  1500.     // }}}
  1501.     // {{{ compareTableIndexesDefinitions()
  1502.  
  1503.     /**
  1504.      * Compare a previous definition with the currently parsed definition
  1505.      *
  1506.      * @param string name of the table
  1507.      * @param array multi dimensional array that contains the current definition
  1508.      * @return array|MDB2_Errorarray of changes on success, or a error object
  1509.      * @access public
  1510.      */
  1511.     function compareTableIndexesDefinitions($table_name$current_definition,
  1512.         $previous_definition)
  1513.     {
  1514.         $changes $defined_indexes = array();
  1515.  
  1516.         if (is_array($current_definition)) {
  1517.             foreach ($current_definition as $index_name => $index{
  1518.                 $was_index_name $index['was'];
  1519.                 if (!empty($previous_definition[$index_name])
  1520.                     && isset($previous_definition[$index_name]['was'])
  1521.                     && $previous_definition[$index_name]['was'== $was_index_name
  1522.                 {
  1523.                     $was_index_name $index_name;
  1524.                 }
  1525.                 if (!empty($previous_definition[$was_index_name])) {
  1526.                     $change = array();
  1527.                     if ($was_index_name != $index_name{
  1528.                         $change['name'$was_index_name;
  1529.                     }
  1530.                     if (!empty($defined_indexes[$was_index_name])) {
  1531.                         return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1532.                             'the index "'.$was_index_name.'" was specified for'.
  1533.                             ' more than one index of table "'.$table_name.'"');
  1534.                     }
  1535.                     $defined_indexes[$was_index_name= true;
  1536.  
  1537.                     $previous_unique array_key_exists('unique'$previous_definition[$was_index_name])
  1538.                         ? $previous_definition[$was_index_name]['unique': false;
  1539.                     $unique array_key_exists('unique'$index$index['unique': false;
  1540.                     if ($previous_unique != $unique{
  1541.                         $change['unique'$unique;
  1542.                     }
  1543.                     $previous_primary array_key_exists('primary'$previous_definition[$was_index_name])
  1544.                         ? $previous_definition[$was_index_name]['primary': false;
  1545.                     $primary array_key_exists('primary'$index$index['primary': false;
  1546.                     if ($previous_primary != $primary{
  1547.                         $change['primary'$primary;
  1548.                     }
  1549.                     $defined_fields = array();
  1550.                     $previous_fields $previous_definition[$was_index_name]['fields'];
  1551.                     if (!empty($index['fields']&& is_array($index['fields'])) {
  1552.                         foreach ($index['fields'as $field_name => $field{
  1553.                             if (!empty($previous_fields[$field_name])) {
  1554.                                 $defined_fields[$field_name= true;
  1555.                                 $previous_sorting array_key_exists('sorting'$previous_fields[$field_name])
  1556.                                     ? $previous_fields[$field_name]['sorting''';
  1557.                                 $sorting array_key_exists('sorting'$field$field['sorting''';
  1558.                                 if ($previous_sorting != $sorting{
  1559.                                     $change['change'= true;
  1560.                                 }
  1561.                             else {
  1562.                                 $change['change'= true;
  1563.                             }
  1564.                         }
  1565.                     }
  1566.                     if (isset($previous_fields&& is_array($previous_fields)) {
  1567.                         foreach ($previous_fields as $field_name => $field{
  1568.                             if (empty($defined_fields[$field_name])) {
  1569.                                 $change['change'= true;
  1570.                             }
  1571.                         }
  1572.                     }
  1573.                     if (!empty($change)) {
  1574.                         $changes['change'][$index_name$current_definition[$index_name];
  1575.                     }
  1576.                 else {
  1577.                     if ($index_name != $was_index_name{
  1578.                         return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1579.                             'it was specified a previous index name ("'.$was_index_name.
  1580.                             ') for index "'.$index_name.'" of table "'.$table_name.'" that does not exist');
  1581.                     }
  1582.                     $changes['add'][$index_name$current_definition[$index_name];
  1583.                 }
  1584.             }
  1585.         }
  1586.         foreach ($previous_definition as $index_previous_name => $index_previous{
  1587.             if (empty($defined_indexes[$index_previous_name])) {
  1588.                 $changes['remove'][$index_previous_name$index_previous;
  1589.             }
  1590.         }
  1591.         return $changes;
  1592.     }
  1593.  
  1594.     // }}}
  1595.     // {{{ compareTableDefinitions()
  1596.  
  1597.     /**
  1598.      * Compare a previous definition with the currently parsed definition
  1599.      *
  1600.      * @param string name of the table
  1601.      * @param array multi dimensional array that contains the current definition
  1602.      * @param array multi dimensional array that contains the previous definition
  1603.      * @param array table names in the schema
  1604.      * @return array|MDB2_Errorarray of changes on success, or a error object
  1605.      * @access public
  1606.      */
  1607.     function compareTableDefinitions($table_name$current_definition,
  1608.         $previous_definition&$defined_tables)
  1609.     {
  1610.         $changes = array();
  1611.  
  1612.         if (is_array($current_definition)) {
  1613.             $was_table_name $table_name;
  1614.             if (!empty($current_definition['was'])) {
  1615.                 $was_table_name $current_definition['was'];
  1616.             }
  1617.             if (!empty($previous_definition[$was_table_name])) {
  1618.                 $changes['change'][$was_table_name= array();
  1619.                 if ($was_table_name != $table_name{
  1620.                     $changes['change'][$was_table_name= array('name' => $table_name);
  1621.                 }
  1622.                 if (!empty($defined_tables[$was_table_name])) {
  1623.                     return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1624.                         'the table "'.$was_table_name.
  1625.                         '" was specified for more than one table of the database');
  1626.                 }
  1627.                 $defined_tables[$was_table_name= true;
  1628.                 if (!empty($current_definition['fields']&& is_array($current_definition['fields'])) {
  1629.                     $previous_fields = array();
  1630.                     if (isset($previous_definition[$was_table_name]['fields'])
  1631.                         && is_array($previous_definition[$was_table_name]['fields'])
  1632.                     {
  1633.                         $previous_fields $previous_definition[$was_table_name]['fields'];
  1634.                     }
  1635.                     $change $this->compareTableFieldsDefinitions(
  1636.                         $table_name,
  1637.                         $current_definition['fields'],
  1638.                         $previous_fields
  1639.                     );
  1640.                     if (PEAR::isError($change)) {
  1641.                         return $change;
  1642.                     }
  1643.                     if (!empty($change)) {
  1644.                         $changes['change'][$was_table_name=
  1645.                             MDB2_Schema::arrayMergeClobber($changes['change'][$was_table_name]$change);
  1646.                     }
  1647.                 }
  1648.                 if (!empty($current_definition['indexes']&& is_array($current_definition['indexes'])) {
  1649.                     $previous_indexes = array();
  1650.                     if (isset($previous_definition[$was_table_name]['indexes'])
  1651.                         && is_array($previous_definition[$was_table_name]['indexes'])
  1652.                     {
  1653.                         $previous_indexes $previous_definition[$was_table_name]['indexes'];
  1654.                     }
  1655.                     $change $this->compareTableIndexesDefinitions(
  1656.                         $table_name,
  1657.                         $current_definition['indexes'],
  1658.                         $previous_indexes
  1659.                     );
  1660.                     if (PEAR::isError($change)) {
  1661.                         return $change;
  1662.                     }
  1663.                     if (!empty($change)) {
  1664.                         $changes['change'][$was_table_name]['indexes'$change;
  1665.                     }
  1666.                 }
  1667.                 if (empty($changes['change'][$was_table_name])) {
  1668.                     unset($changes['change'][$was_table_name]);
  1669.                 }
  1670.                 if (empty($changes['change'])) {
  1671.                     unset($changes['change']);
  1672.                 }
  1673.             else {
  1674.                 if ($table_name != $was_table_name{
  1675.                     return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1676.                         'it was specified a previous table name ("'.$was_table_name.
  1677.                         '") for table "'.$table_name.'" that does not exist');
  1678.                 }
  1679.                 $changes['add'][$table_name= true;
  1680.             }
  1681.         }
  1682.  
  1683.         return $changes;
  1684.     }
  1685.  
  1686.     // }}}
  1687.     // {{{ compareSequenceDefinitions()
  1688.  
  1689.     /**
  1690.      * Compare a previous definition with the currently parsed definition
  1691.      *
  1692.      * @param string name of the sequence
  1693.      * @param array multi dimensional array that contains the current definition
  1694.      * @param array multi dimensional array that contains the previous definition
  1695.      * @param array sequence names in the schema
  1696.      * @return array|MDB2_Errorarray of changes on success, or a error object
  1697.      * @access public
  1698.      */
  1699.     function compareSequenceDefinitions($sequence_name$current_definition,
  1700.         $previous_definition&$defined_sequences)
  1701.     {
  1702.         $changes = array();
  1703.  
  1704.         if (is_array($current_definition)) {
  1705.             $was_sequence_name $sequence_name;
  1706.             if (!empty($previous_definition[$sequence_name])
  1707.                 && isset($previous_definition[$sequence_name]['was'])
  1708.                 && $previous_definition[$sequence_name]['was'== $was_sequence_name
  1709.             {
  1710.                 $was_sequence_name $sequence_name;
  1711.             elseif (!empty($current_definition['was'])) {
  1712.                 $was_sequence_name $current_definition['was'];
  1713.             }
  1714.             if (!empty($previous_definition[$was_sequence_name])) {
  1715.                 if ($was_sequence_name != $sequence_name{
  1716.                     $changes['change'][$was_sequence_name]['name'$sequence_name;
  1717.                 }
  1718.                 if (!empty($defined_sequences[$was_sequence_name])) {
  1719.                     return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1720.                         'the sequence "'.$was_sequence_name.'" was specified as base'.
  1721.                         ' of more than of sequence of the database');
  1722.                 }
  1723.                 $defined_sequences[$was_sequence_name= true;
  1724.                 $change = array();
  1725.                 if (!empty($current_definition['start'])
  1726.                     && isset($previous_definition[$was_sequence_name]['start'])
  1727.                     && $current_definition['start'!= $previous_definition[$was_sequence_name]['start']
  1728.                 {
  1729.                     $change['start'$previous_definition[$sequence_name]['start'];
  1730.                 }
  1731.                 if (isset($current_definition['on']['table'])
  1732.                     && isset($previous_definition[$was_sequence_name]['on']['table'])
  1733.                     && $current_definition['on']['table'!= $previous_definition[$was_sequence_name]['on']['table']
  1734.                     && isset($current_definition['on']['field'])
  1735.                     && isset($previous_definition[$was_sequence_name]['on']['field'])
  1736.                     && $current_definition['on']['field'!= $previous_definition[$was_sequence_name]['on']['field']
  1737.                 {
  1738.                     $change['on'$current_definition['on'];
  1739.                 }
  1740.                 if (!empty($change)) {
  1741.                     $changes['change'][$was_sequence_name][$sequence_name$change;
  1742.                 }
  1743.             else {
  1744.                 if ($sequence_name != $was_sequence_name{
  1745.                     return $this->raiseError(MDB2_SCHEMA_ERROR_INVALIDnullnull,
  1746.                         'it was specified a previous sequence name ("'.$was_sequence_name.
  1747.                         '") for sequence "'.$sequence_name.'" that does not exist');
  1748.                 }
  1749.                 $changes['add'][$sequence_name= true;
  1750.             }
  1751.         }
  1752.         return $changes;
  1753.     }
  1754.     // }}}
  1755.     // {{{ verifyAlterDatabase()
  1756.  
  1757.     /**
  1758.      * Verify that the changes requested are supported
  1759.      *
  1760.      * @param array associative array that contains the definition of the changes
  1761.      *               that are meant to be applied to the database structure.
  1762.      * @return bool|MDB2_ErrorMDB2_OK or error object
  1763.      * @access public
  1764.      */
  1765.     function verifyAlterDatabase($changes)
  1766.     {
  1767.         if (!empty($changes['tables']['change']&& is_array($changes['tables']['change'])) {
  1768.             foreach ($changes['tables']['change'as $table_name => $table{
  1769.                 if (!empty($table['indexes']&& is_array($table['indexes'])) {
  1770.                     if (!$this->db->supports('indexes')) {
  1771.                         return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTEDnullnull,
  1772.                             'indexes are not supported');
  1773.                     }
  1774.                     $table_changes count($table['indexes']);
  1775.                     if (!empty($table['indexes']['add'])) {
  1776.                         $table_changes--;
  1777.                     }
  1778.                     if (!empty($table['indexes']['remove'])) {
  1779.                         $table_changes--;
  1780.                     }
  1781.                     if (!empty($table['indexes']['change'])) {
  1782.                         $table_changes--;
  1783.                     }
  1784.                     if ($table_changes{
  1785.                         return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTEDnullnull,
  1786.                             'index alteration not yet supported: '.implode(', 'array_keys($table['indexes'])));
  1787.                     }
  1788.                 }
  1789.                 unset($table['indexes']);
  1790.                 $result $this->db->manager->alterTable($table_name$tabletrue);
  1791.                 if (PEAR::isError($result)) {
  1792.                     return $result;
  1793.                 }
  1794.             }
  1795.         }
  1796.         if (!empty($changes['sequences']&& is_array($changes['sequences'])) {
  1797.             if (!$this->db->supports('sequences')) {
  1798.                 return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTEDnullnull,
  1799.                     'sequences are not supported');
  1800.             }
  1801.             $sequence_changes count($changes['sequences']);
  1802.             if (!empty($changes['sequences']['add'])) {
  1803.                 $sequence_changes--;
  1804.             }
  1805.             if (!empty($changes['sequences']['remove'])) {
  1806.                 $sequence_changes--;
  1807.             }
  1808.             if (!empty($changes['sequences']['change'])) {
  1809.                 $sequence_changes--;
  1810.             }
  1811.             if ($sequence_changes{
  1812.                 return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTEDnullnull,
  1813.                     'sequence alteration not yet supported: '.implode(', 'array_keys($changes['sequences'])));
  1814.             }
  1815.         }
  1816.         return MDB2_OK;
  1817.     }
  1818.  
  1819.     // }}}
  1820.     // {{{ alterDatabaseIndexes()
  1821.  
  1822.     /**
  1823.      * Execute the necessary actions to implement the requested changes
  1824.      * in the indexes inside a database structure.
  1825.      *
  1826.      * @param string name of the table
  1827.      * @param array associative array that contains the definition of the changes
  1828.      *               that are meant to be applied to the database structure.
  1829.      * @return bool|MDB2_ErrorMDB2_OK or error object
  1830.      * @access public
  1831.      */
  1832.     function alterDatabaseIndexes($table_name$changes)
  1833.     {
  1834.         $alterations = 0;
  1835.         if (empty($changes)) {
  1836.             return $alterations;
  1837.         }
  1838.  
  1839.         if (!empty($changes['remove']&& is_array($changes['remove'])) {
  1840.             foreach ($changes['remove'as $index_name => $index{
  1841.                 $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  1842.                 if (!empty($index['primary']|| !empty($index['unique'])) {
  1843.                     $result $this->db->manager->dropConstraint($table_name$index_name!empty($index['primary']));
  1844.                 else {
  1845.                     $result $this->db->manager->dropIndex($table_name$index_name);
  1846.                 }
  1847.                 $this->db->popExpect();
  1848.                 if (PEAR::isError($result&& !MDB2::isError($resultMDB2_ERROR_NOT_FOUND)) {
  1849.                     return $result;
  1850.                 }
  1851.                 $alterations++;
  1852.             }
  1853.         }
  1854.         if (!empty($changes['change']&& is_array($changes['change'])) {
  1855.             foreach ($changes['change'as $index_name => $index{
  1856.                 if (!empty($index['primary']|| !empty($index['unique'])) {
  1857.                     $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  1858.                     $result $this->db->manager->dropConstraint($table_name$index_name!empty($index['primary']));
  1859.                     $this->db->popExpect();
  1860.                     if (PEAR::isError($result&& !MDB2::isError($resultMDB2_ERROR_NOT_FOUND)) {
  1861.                         return $result;
  1862.                     }
  1863.                     $result $this->db->manager->createConstraint($table_name$index_name$index);
  1864.                 else {
  1865.                     $this->db->expectError(MDB2_ERROR_NOT_FOUND);
  1866.                     $result $this->db->manager->dropIndex($table_name$index_name);
  1867.                     $this->db->popExpect();
  1868.                     if (PEAR::isError($result&& !MDB2::isError($resultMDB2_ERROR_NOT_FOUND)) {
  1869.                         return $result;
  1870.                     }
  1871.                     $result $this->db->manager->createIndex($table_name$index_name$index);
  1872.                 }
  1873.                 if (PEAR::isError($result)) {
  1874.                     return $result;
  1875.                 }
  1876.                 $alterations++;
  1877.             }
  1878.         }
  1879.         if (!empty($changes['add']&& is_array($changes['add'])) {
  1880.             foreach ($changes['add'as $index_name => $index{
  1881.                 if (!empty($index['primary']|| !empty($index['unique'])) {
  1882.                     $result $this->db->manager->createConstraint($table_name$index_name$index);
  1883.                 else {
  1884.                     $result $this->db->manager->createIndex($table_name$index_name$index);
  1885.                 }
  1886.                 if (PEAR::isError($result)) {
  1887.                     return $result;
  1888.                 }
  1889.                 $alterations++;
  1890.             }
  1891.         }
  1892.  
  1893.         return $alterations;
  1894.     }
  1895.  
  1896.     // }}}
  1897.     // {{{ alterDatabaseTables()
  1898.  
  1899.     /**
  1900.      * Execute the necessary actions to implement the requested changes
  1901.      * in the tables inside a database structure.
  1902.      *
  1903.      * @param array multi dimensional array that contains the current definition
  1904.      * @param array multi dimensional array that contains the previous definition
  1905.      * @param array associative array that contains the definition of the changes
  1906.      *               that are meant to be applied to the database structure.
  1907.      * @return bool|MDB2_ErrorMDB2_OK or error object
  1908.      * @access public
  1909.      */
  1910.     function alterDatabaseTables($current_definition$previous_definition$changes)
  1911.     {
  1912.         /* FIXME: tables marked to be added are initialized by createTable(), others don't */
  1913.         $alterations = 0;
  1914.         if (empty($changes)) {
  1915.             return $alterations;
  1916.         }
  1917.  
  1918.         if (!empty($changes['add']&& is_array($changes['add'])) {
  1919.             foreach ($changes['add'as $table_name => $table{
  1920.                 $result $this->createTable($table_name$current_definition[$table_name]);
  1921.                 if (PEAR::isError($result)) {
  1922.                     return $result;
  1923.                 }
  1924.                 $alterations++;
  1925.             }
  1926.         }
  1927.  
  1928.         if (!empty($changes['remove']&& is_array($changes['remove'])) {
  1929.             foreach ($changes['remove'as $table_name => $table{
  1930.                 $result $this->db->manager->dropTable($table_name);
  1931.                 if (PEAR::isError($result)) {
  1932.                     return $result;
  1933.                 }
  1934.                 $alterations++;
  1935.             }
  1936.         }
  1937.  
  1938.         if (!empty($changes['change']&& is_array($changes['change'])) {
  1939.             foreach ($changes['change'as $table_name => $table{
  1940.                 $indexes = array();
  1941.                 if (!empty($table['indexes'])) {
  1942.                     $indexes $table['indexes'];
  1943.                     unset($table['indexes']);
  1944.                 }
  1945.                 if (!empty($indexes['remove'])) {
  1946.                     $result $this->alterDatabaseIndexes($table_namearray('remove' => $indexes['remove']));
  1947.                     if (PEAR::isError($result)) {
  1948.                         return $result;
  1949.                     }
  1950.                     unset($indexes['remove']);
  1951.                     $alterations += $result;
  1952.                 }
  1953.                 $result $this->db->manager->alterTable($table_name$tablefalse);
  1954.                 if (PEAR::isError($result)) {
  1955.                     return $result;
  1956.                 }
  1957.                 $alterations++;
  1958.                 if (!empty($indexes)) {
  1959.                     $result $this->alterDatabaseIndexes($table_name$indexes);
  1960.                     if (PEAR::isError($result)) {
  1961.                         return $result;
  1962.                     }
  1963.                     $alterations += $result;
  1964.                 }
  1965.             }
  1966.         }
  1967.  
  1968.         return $alterations;
  1969.     }
  1970.  
  1971.     // }}}
  1972.     // {{{ alterDatabaseSequences()
  1973.  
  1974.     /**
  1975.      * Execute the necessary actions to implement the requested changes
  1976.      * in the sequences inside a database structure.
  1977.      *
  1978.      * @param array multi dimensional array that contains the current definition
  1979.      * @param array multi dimensional array that contains the previous definition
  1980.      * @param array associative array that contains the definition of the changes
  1981.      *               that are meant to be applied to the database structure.
  1982.      * @return bool|MDB2_ErrorMDB2_OK or error object
  1983.      * @access public
  1984.      */
  1985.     function alterDatabaseSequences($current_definition$previous_definition$changes)
  1986.     {
  1987.         $alterations = 0;
  1988.         if (empty($changes)) {
  1989.             return $alterations;
  1990.         }
  1991.  
  1992.         if (!empty($changes['add']&& is_array($changes['add'])) {
  1993.             foreach ($changes['add'as $sequence_name => $sequence{
  1994.                 $result $this->createSequence($sequence_name$current_definition[$sequence_name]);
  1995.                 if (PEAR::isError($result)) {
  1996.                     return $result;
  1997.                 }
  1998.                 $alterations++;
  1999.             }
  2000.         }
  2001.  
  2002.         if (!empty($changes['remove']&& is_array($changes['remove'])) {
  2003.             foreach ($changes['remove'as $sequence_name => $sequence{
  2004.                 $result $this->db->manager->dropSequence($sequence_name);
  2005.                 if (PEAR::isError($result)) {
  2006.                     return $result;
  2007.                 }
  2008.                 $alterations++;
  2009.             }
  2010.         }
  2011.  
  2012.         if (!empty($changes['change']&& is_array($changes['change'])) {
  2013.             foreach ($changes['change'as $sequence_name => $sequence{
  2014.                 $result $this->db->manager->dropSequence($previous_definition[$sequence_name]['was']);
  2015.                 if (PEAR::isError($result)) {
  2016.                     return $result;
  2017.                 }
  2018.                 $result $this->createSequence($sequence_name$sequence);
  2019.                 if (PEAR::isError($result)) {
  2020.                     return $result;
  2021.                 }
  2022.                 $alterations++;
  2023.             }
  2024.         }
  2025.  
  2026.         return $alterations;
  2027.     }
  2028.  
  2029.     // }}}
  2030.     // {{{ alterDatabase()
  2031.  
  2032.     /**
  2033.      * Execute the necessary actions to implement the requested changes
  2034.      * in a database structure.
  2035.      *
  2036.      * @param array multi dimensional array that contains the current definition
  2037.      * @param array multi dimensional array that contains the previous definition
  2038.      * @param array associative array that contains the definition of the changes
  2039.      *               that are meant to be applied to the database structure.
  2040.      * @return bool|MDB2_ErrorMDB2_OK or error object
  2041.      * @access public
  2042.      */
  2043.     function alterDatabase($current_definition$previous_definition$changes)
  2044.     {
  2045.         $alterations = 0;
  2046.         if (empty($changes)) {
  2047.             return $alterations;
  2048.         }
  2049.  
  2050.         $result $this->verifyAlterDatabase($changes);
  2051.         if (PEAR::isError($result)) {
  2052.             return $result;
  2053.         }
  2054.  
  2055.         if (!empty($current_definition['name'])) {
  2056.             $previous_database_name $this->db->setDatabase($current_definition['name']);
  2057.         }
  2058.  
  2059.         if (($support_transactions $this->db->supports('transactions'))
  2060.             && PEAR::isError($result $this->db->beginNestedTransaction())
  2061.         {
  2062.             return $result;
  2063.         }
  2064.  
  2065.         if (!empty($changes['tables']&& !empty($current_definition['tables'])) {
  2066.             $current_tables = isset($current_definition['tables']$current_definition['tables': array();
  2067.             $previous_tables = isset($previous_definition['tables']$previous_definition['tables': array();
  2068.             $result $this->alterDatabaseTables($current_tables$previous_tables$changes['tables']);
  2069.             if (is_numeric($result)) {
  2070.                 $alterations += $result;
  2071.             }
  2072.         }
  2073.  
  2074.         if (!PEAR::isError($result&& !empty($changes['sequences'])) {
  2075.             $current_sequences = isset($current_definition['sequences']$current_definition['sequences': array();
  2076.             $previous_sequences = isset($previous_definition['sequences']$previous_definition['sequences': array();
  2077.             $result $this->alterDatabaseSequences($current_sequences$previous_sequences$changes['sequences']);
  2078.             if (is_numeric($result)) {
  2079.                 $alterations += $result;
  2080.             }
  2081.         }
  2082.  
  2083.         if ($support_transactions{
  2084.             $res $this->db->completeNestedTransaction();
  2085.             if (PEAR::isError($res)) {
  2086.                 $result $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  2087.                     'Could not end transaction ('.
  2088.                     $res->getMessage().' ('.$res->getUserinfo().'))');
  2089.             }
  2090.         elseif (PEAR::isError($result&& $alterations{
  2091.             $result $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  2092.                 'the requested database alterations were only partially implemented ('.
  2093.                 $result->getMessage().' ('.$result->getUserinfo().'))');
  2094.         }
  2095.  
  2096.         if (isset($previous_database_name)) {
  2097.             $this->db->setDatabase($previous_database_name);
  2098.         }
  2099.         return $result;
  2100.     }
  2101.  
  2102.     // }}}
  2103.     // {{{ dumpDatabaseChanges()
  2104.  
  2105.     /**
  2106.      * Dump the changes between two database definitions.
  2107.      *
  2108.      * @param array associative array that specifies the list of database
  2109.      *               definitions changes as returned by the _compareDefinitions
  2110.      *               manager class function.
  2111.      * @return bool|MDB2_ErrorMDB2_OK or error object
  2112.      * @access public
  2113.      */
  2114.     function dumpDatabaseChanges($changes)
  2115.     {
  2116.         if (!empty($changes['tables'])) {
  2117.             if (!empty($changes['tables']['add']&& is_array($changes['tables']['add'])) {
  2118.                 foreach ($changes['tables']['add'as $table_name => $table{
  2119.                     $this->db->debug("$table_name:"__FUNCTION__);
  2120.                     $this->db->debug("\tAdded table '$table_name'"__FUNCTION__);
  2121.                 }
  2122.             }
  2123.             if (!empty($changes['tables']['remove']&& is_array($changes['tables']['remove'])) {
  2124.                 foreach ($changes['tables']['remove'as $table_name => $table{
  2125.                     $this->db->debug("$table_name:"__FUNCTION__);
  2126.                     $this->db->debug("\tRemoved table '$table_name'"__FUNCTION__);
  2127.                 }
  2128.             }
  2129.             if (!empty($changes['tables']['change']&& is_array($changes['tables']['change'])) {
  2130.                 foreach ($changes['tables']['change'as $table_name => $table{
  2131.                     if (array_key_exists('name'$table)) {
  2132.                         $this->db->debug("\tRenamed table '$table_name' to '".$table['name']."'"__FUNCTION__);
  2133.                     }
  2134.                     if (!empty($table['add']&& is_array($table['add'])) {
  2135.                         foreach ($table['add'as $field_name => $field{
  2136.                             $this->db->debug("\tAdded field '".$field_name."'"__FUNCTION__);
  2137.                         }
  2138.                     }
  2139.                     if (!empty($table['remove']&& is_array($table['remove'])) {
  2140.                         foreach ($table['remove'as $field_name => $field{
  2141.                             $this->db->debug("\tRemoved field '".$field_name."'"__FUNCTION__);
  2142.                         }
  2143.                     }
  2144.                     if (!empty($table['rename']&& is_array($table['rename'])) {
  2145.                         foreach ($table['rename'as $field_name => $field{
  2146.                             $this->db->debug("\tRenamed field '".$field_name."' to '".$field['name']."'"__FUNCTION__);
  2147.                         }
  2148.                     }
  2149.                     if (!empty($table['change']&& is_array($table['change'])) {
  2150.                         foreach ($table['change'as $field_name => $field{
  2151.                             $field $field['definition'];
  2152.                             if (array_key_exists('type'$field)) {
  2153.                                 $this->db->debug(
  2154.                                     "\tChanged field '$field_name' type to '".$field['type']."'"__FUNCTION__);
  2155.                             }
  2156.                             if (array_key_exists('unsigned'$field)) {
  2157.                                 $this->db->debug(
  2158.                                     "\tChanged field '$field_name' type to '".
  2159.                                     (!empty($field['unsigned']&& $field['unsigned''' 'not ')."unsigned'",
  2160.                                     __FUNCTION__);
  2161.                             }
  2162.                             if (array_key_exists('length'$field)) {
  2163.                                 $this->db->debug(
  2164.                                     "\tChanged field '$field_name' length to '".
  2165.                                     (!empty($field['length']$field['length']'no length')."'"__FUNCTION__);
  2166.                             }
  2167.                             if (array_key_exists('default'$field)) {
  2168.                                 $this->db->debug(
  2169.                                     "\tChanged field '$field_name' default to ".
  2170.                                     (isset($field['default']"'".$field['default']."'" 'NULL')__FUNCTION__);
  2171.                             }
  2172.                             if (array_key_exists('notnull'$field)) {
  2173.                                 $this->db->debug(
  2174.                                    "\tChanged field '$field_name' notnull to ".
  2175.                                     (!empty($field['notnull']&& $field['notnull''true' 'false'),
  2176.                                     __FUNCTION__
  2177.                                 );
  2178.                             }
  2179.                         }
  2180.                     }
  2181.                     if (!empty($table['indexes']&& is_array($table['indexes'])) {
  2182.                         if (!empty($table['indexes']['add']&& is_array($table['indexes']['add'])) {
  2183.                             foreach ($table['indexes']['add'as $index_name => $index{
  2184.                                 $this->db->debug("\tAdded index '".$index_name.
  2185.                                     "' of table '$table_name'"__FUNCTION__);
  2186.                             }
  2187.                         }
  2188.                         if (!empty($table['indexes']['remove']&& is_array($table['indexes']['remove'])) {
  2189.                             foreach ($table['indexes']['remove'as $index_name => $index{
  2190.                                 $this->db->debug("\tRemoved index '".$index_name.
  2191.                                     "' of table '$table_name'"__FUNCTION__);
  2192.                             }
  2193.                         }
  2194.                         if (!empty($table['indexes']['change']&& is_array($table['indexes']['change'])) {
  2195.                             foreach ($table['indexes']['change'as $index_name => $index{
  2196.                                 if (array_key_exists('name'$index)) {
  2197.                                     $this->db->debug(
  2198.                                         "\tRenamed index '".$index_name."' to '".$index['name'].
  2199.                                         "' on table '$table_name'"__FUNCTION__);
  2200.                                 }
  2201.                                 if (array_key_exists('unique'$index)) {
  2202.                                     $this->db->debug(
  2203.                                         "\tChanged index '".$index_name."' unique to '".
  2204.                                         !empty($index['unique'])."' on table '$table_name'"__FUNCTION__);
  2205.                                 }
  2206.                                 if (array_key_exists('primary'$index)) {
  2207.                                     $this->db->debug(
  2208.                                         "\tChanged index '".$index_name."' primary to '".
  2209.                                         !empty($index['primary'])."' on table '$table_name'"__FUNCTION__);
  2210.                                 }
  2211.                                 if (array_key_exists('change'$index)) {
  2212.                                     $this->db->debug("\tChanged index '".$index_name.
  2213.                                         "' on table '$table_name'"__FUNCTION__);
  2214.                                 }
  2215.                             }
  2216.                         }
  2217.                     }
  2218.                 }
  2219.             }
  2220.         }
  2221.         if (!empty($changes['sequences'])) {
  2222.             if (!empty($changes['sequences']['add']&& is_array($changes['sequences']['add'])) {
  2223.                 foreach ($changes['sequences']['add'as $sequence_name => $sequence{
  2224.                     $this->db->debug("$sequence_name:"__FUNCTION__);
  2225.                     $this->db->debug("\tAdded sequence '$sequence_name'"__FUNCTION__);
  2226.                 }
  2227.             }
  2228.             if (!empty($changes['sequences']['remove']&& is_array($changes['sequences']['remove'])) {
  2229.                 foreach ($changes['sequences']['remove'as $sequence_name => $sequence{
  2230.                     $this->db->debug("$sequence_name:"__FUNCTION__);
  2231.                     $this->db->debug("\tAdded sequence '$sequence_name'"__FUNCTION__);
  2232.                 }
  2233.             }
  2234.             if (!empty($changes['sequences']['change']&& is_array($changes['sequences']['change'])) {
  2235.                 foreach ($changes['sequences']['change'as $sequence_name => $sequence{
  2236.                     if (array_key_exists('name'$sequence)) {
  2237.                         $this->db->debug(
  2238.                             "\tRenamed sequence '$sequence_name' to '".
  2239.                             $sequence['name']."'"__FUNCTION__);
  2240.                     }
  2241.                     if (!empty($sequence['change']&& is_array($sequence['change'])) {
  2242.                         foreach ($sequence['change'as $sequence_name => $sequence{
  2243.                             if (array_key_exists('start'$sequence)) {
  2244.                                 $this->db->debug(
  2245.                                     "\tChanged sequence '$sequence_name' start to '".
  2246.                                     $sequence['start']."'"__FUNCTION__);
  2247.                             }
  2248.                         }
  2249.                     }
  2250.                 }
  2251.             }
  2252.         }
  2253.         return MDB2_OK;
  2254.     }
  2255.  
  2256.     // }}}
  2257.     // {{{ dumpDatabase()
  2258.  
  2259.     /**
  2260.      * Dump a previously parsed database structure in the Metabase schema
  2261.      * XML based format suitable for the Metabase parser. This function
  2262.      * may optionally dump the database definition with initialization
  2263.      * commands that specify the data that is currently present in the tables.
  2264.      *
  2265.      * @param array multi dimensional array that contains the current definition
  2266.      * @param array associative array that takes pairs of tag
  2267.      *  names and values that define dump options.
  2268.      *                  <pre>array (
  2269.      *                      'output_mode'    =>    String
  2270.      *                          'file' :   dump into a file
  2271.      *                          default:   dump using a function
  2272.      *                      'output'        =>    String
  2273.      *                          depending on the 'Output_Mode'
  2274.      *                                   name of the file
  2275.      *                                   name of the function
  2276.      *                      'end_of_line'        =>    String
  2277.      *                          end of line delimiter that should be used
  2278.      *                          default: "\n"
  2279.      *                  );</pre>
  2280.      * @param int that determines what data to dump
  2281.      *               + MDB2_SCHEMA_DUMP_ALL       : the entire db
  2282.      *               + MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db
  2283.      *               + MDB2_SCHEMA_DUMP_CONTENT   : only the content of the db
  2284.      * @return bool|MDB2_ErrorMDB2_OK or error object
  2285.      * @access public
  2286.      */
  2287.     function dumpDatabase($database_definition$arguments$dump = MDB2_SCHEMA_DUMP_ALL)
  2288.     {
  2289.         $class_name $this->options['writer'];
  2290.         $result = MDB2::loadClass($class_name$this->db->getOption('debug'));
  2291.         if (PEAR::isError($result)) {
  2292.             return $result;
  2293.         }
  2294.  
  2295.         // get initialization data
  2296.         if (isset($database_definition['tables']&& is_array($database_definition['tables'])
  2297.             && $dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT
  2298.         {
  2299.             foreach ($database_definition['tables'as $table_name => $table{
  2300.                 $fields = array();
  2301.                 $fieldsq = array();
  2302.                 foreach ($table['fields'as $field_name => $field{
  2303.                     $fields[$field_name$field['type'];
  2304.                     $fieldsq[$this->db->quoteIdentifier($field_nametrue);
  2305.                 }
  2306.                 $query 'SELECT '.implode(', '$fieldsq).' FROM ';
  2307.                 $query.= $this->db->quoteIdentifier($table_nametrue);
  2308.                 $data $this->db->queryAll($query$fieldsMDB2_FETCHMODE_ASSOC);
  2309.                 if (PEAR::isError($data)) {
  2310.                     return $data;
  2311.                 }
  2312.                 if (!empty($data)) {
  2313.                     $initialization = array();
  2314.                     $lob_buffer_length $this->db->getOption('lob_buffer_length');
  2315.                     foreach ($data as $row{
  2316.                         $rows = array();
  2317.                         foreach($row as $key => $lob{
  2318.                             if (is_resource($lob)) {
  2319.                                 $value '';
  2320.                                 while (!feof($lob)) {
  2321.                                     $value.= fread($lob$lob_buffer_length);
  2322.                                 }
  2323.                                 $row[$key$value;
  2324.                             }
  2325.                             $rows[= array('name' => $key'group' => array('type' => 'value''data' => $row[$key]));
  2326.                         }
  2327.                         $initialization[= array('type' => 'insert''data' => array('field' => $rows));
  2328.                     }
  2329.                     $database_definition['tables'][$table_name]['initialization'$initialization;
  2330.                 }
  2331.             }
  2332.         }
  2333.  
  2334.         $writer =new $class_name($this->options['valid_types']);
  2335.         return $writer->dumpDatabase($database_definition$arguments$dump);
  2336.     }
  2337.  
  2338.     // }}}
  2339.     // {{{ writeInitialization()
  2340.  
  2341.     /**
  2342.      * Write initialization and sequences
  2343.      *
  2344.      * @param string|array data file or data array
  2345.      * @param string|array structure file or array
  2346.      * @param array associative array that is passed to the argument
  2347.      *  of the same name to the parseDatabaseDefinitionFile function. (there third
  2348.      *  param)
  2349.      * @return bool|MDB2_ErrorMDB2_OK or error object
  2350.      * @access public
  2351.      */
  2352.     function writeInitialization($data$structure = false$variables = array())
  2353.     {
  2354.         if ($structure{
  2355.             $structure $this->parseDatabaseDefinition($structurefalse$variables);
  2356.             if (PEAR::isError($structure)) {
  2357.                 return $structure;
  2358.             }
  2359.         }
  2360.  
  2361.         $data $this->parseDatabaseDefinition($datafalse$variablesfalse$structure);
  2362.         if (PEAR::isError($data)) {
  2363.             return $data;
  2364.         }
  2365.  
  2366.         $previous_database_name = null;
  2367.         if (!empty($data['name'])) {
  2368.             $previous_database_name $this->db->setDatabase($data['name']);
  2369.         elseif(!empty($structure['name'])) {
  2370.             $previous_database_name $this->db->setDatabase($structure['name']);
  2371.         }
  2372.  
  2373.         if (!empty($data['tables']&& is_array($data['tables'])) {
  2374.             foreach ($data['tables'as $table_name => $table{
  2375.                 if (empty($table['initialization'])) {
  2376.                     continue;
  2377.                 }
  2378.                 $result $this->initializeTable($table_name$table);
  2379.                 if (PEAR::isError($result)) {
  2380.                     return $result;
  2381.                 }
  2382.             }
  2383.         }
  2384.  
  2385.         if (!empty($structure['sequences']&& is_array($structure['sequences'])) {
  2386.             foreach ($structure['sequences'as $sequence_name => $sequence{
  2387.                 if (isset($data['sequences'][$sequence_name])
  2388.                     || !isset($sequence['on']['table'])
  2389.                     || !isset($data['tables'][$sequence['on']['table']])
  2390.                 {
  2391.                     continue;
  2392.                 }
  2393.                 $result $this->createSequence($sequence_name$sequencetrue);
  2394.                 if (PEAR::isError($result)) {
  2395.                     return $result;
  2396.                 }
  2397.             }
  2398.         }
  2399.         if (!empty($data['sequences']&& is_array($data['sequences'])) {
  2400.             foreach ($data['sequences'as $sequence_name => $sequence{
  2401.                 $result $this->createSequence($sequence_name$sequencetrue);
  2402.                 if (PEAR::isError($result)) {
  2403.                     return $result;
  2404.                 }
  2405.             }
  2406.         }
  2407.  
  2408.         if (isset($previous_database_name)) {
  2409.             $this->db->setDatabase($previous_database_name);
  2410.         }
  2411.  
  2412.         return MDB2_OK;
  2413.     }
  2414.  
  2415.     // }}}
  2416.     // {{{ updateDatabase()
  2417.  
  2418.     /**
  2419.      * Compare the correspondent files of two versions of a database schema
  2420.      * definition: the previously installed and the one that defines the schema
  2421.      * that is meant to update the database.
  2422.      * If the specified previous definition file does not exist, this function
  2423.      * will create the database from the definition specified in the current
  2424.      * schema file.
  2425.      * If both files exist, the function assumes that the database was previously
  2426.      * installed based on the previous schema file and will update it by just
  2427.      * applying the changes.
  2428.      * If this function succeeds, the contents of the current schema file are
  2429.      * copied to replace the previous schema file contents. Any subsequent schema
  2430.      * changes should only be done on the file specified by the $current_schema_file
  2431.      * to let this function make a consistent evaluation of the exact changes that
  2432.      * need to be applied.
  2433.      *
  2434.      * @param string|arrayfilename or array of the updated database schema definition.
  2435.      * @param string|arrayfilename or array of the previously installed database schema definition.
  2436.      * @param array associative array that is passed to the argument of the same
  2437.      *               name to the parseDatabaseDefinitionFile function. (there third param)
  2438.      * @param bool determines if the disable_query option should be set to true
  2439.      *               for the alterDatabase() or createDatabase() call
  2440.      * @return bool|MDB2_ErrorMDB2_OK or error object
  2441.      * @access public
  2442.      */
  2443.     function updateDatabase($current_schema$previous_schema = false
  2444.         $variables = array()$disable_query = false$overwrite_old_schema_file = false)
  2445.     {
  2446.         $current_definition $this->parseDatabaseDefinition(
  2447.             $current_schemafalse$variables$this->options['fail_on_invalid_names']
  2448.         );
  2449.         if (PEAR::isError($current_definition)) {
  2450.             return $current_definition;
  2451.         }
  2452.  
  2453.         $previous_definition = false;
  2454.         if ($previous_schema{
  2455.             $previous_definition $this->parseDatabaseDefinition(
  2456.                 $previous_schematrue$variables$this->options['fail_on_invalid_names']
  2457.             );
  2458.             if (PEAR::isError($previous_definition)) {
  2459.                 return $previous_definition;
  2460.             }
  2461.         }
  2462.  
  2463.         if ($previous_definition{
  2464.             $changes $this->compareDefinitions($current_definition$previous_definition);
  2465.             if (PEAR::isError($changes)) {
  2466.                 return $changes;
  2467.             }
  2468.  
  2469.             if (is_array($changes)) {
  2470.                 $this->db->setOption('disable_query'$disable_query);
  2471.                 $result $this->alterDatabase($current_definition$previous_definition$changes);
  2472.                 $this->db->setOption('disable_query'false);
  2473.                 if (PEAR::isError($result)) {
  2474.                     return $result;
  2475.                 }
  2476.                 $copy = true;
  2477.                 if ($this->db->options['debug']{
  2478.                     $result $this->dumpDatabaseChanges($changes);
  2479.                     if (PEAR::isError($result)) {
  2480.                         return $result;
  2481.                     }
  2482.                 }
  2483.             }
  2484.         else {
  2485.             $this->db->setOption('disable_query'$disable_query);
  2486.             $result $this->createDatabase($current_definition);
  2487.             $this->db->setOption('disable_query'false);
  2488.             if (PEAR::isError($result)) {
  2489.                 return $result;
  2490.             }
  2491.         }
  2492.  
  2493.         if ($overwrite_old_schema_file
  2494.             && !$disable_query
  2495.             && is_string($previous_schema&& is_string($current_schema)
  2496.             && !copy($current_schema$previous_schema)
  2497.         {
  2498.             return $this->raiseError(MDB2_SCHEMA_ERRORnullnull,
  2499.                 'Could not copy the new database definition file to the current file');
  2500.         }
  2501.  
  2502.         return MDB2_OK;
  2503.     }
  2504.     // }}}
  2505.     // {{{ errorMessage()
  2506.  
  2507.     /**
  2508.      * Return a textual error message for a MDB2 error code
  2509.      *
  2510.      * @param   int|arrayinteger error code,
  2511.      *                      <code>null</code> to get the current error code-message map,
  2512.      *                     or an array with a new error code-message map
  2513.      * @return  string  error message, or false if the error code was not recognized
  2514.      * @access public
  2515.      */
  2516.     function errorMessage($value = null)
  2517.     {
  2518.         static $errorMessages;
  2519.         if (is_array($value)) {
  2520.             $errorMessages $value;
  2521.             return MDB2_OK;
  2522.         elseif (!isset($errorMessages)) {
  2523.             $errorMessages = array(
  2524.                 MDB2_SCHEMA_ERROR              => 'unknown error',
  2525.                 MDB2_SCHEMA_ERROR_PARSE        => 'schema parse error',
  2526.                 MDB2_SCHEMA_ERROR_VALIDATE     => 'schema validation error',
  2527.                 MDB2_SCHEMA_ERROR_INVALID      => 'invalid',
  2528.                 MDB2_SCHEMA_ERROR_UNSUPPORTED  => 'not supported',
  2529.                 MDB2_SCHEMA_ERROR_WRITER       => 'schema writer error',
  2530.             );
  2531.         }
  2532.  
  2533.         if (is_null($value)) {
  2534.             return $errorMessages;
  2535.         }
  2536.  
  2537.         if (PEAR::isError($value)) {
  2538.             $value $value->getCode();
  2539.         }
  2540.  
  2541.         return !empty($errorMessages[$value]?
  2542.            $errorMessages[$value$errorMessages[MDB2_SCHEMA_ERROR];
  2543.     }
  2544.  
  2545.     // }}}
  2546.     // {{{ raiseError()
  2547.  
  2548.     /**
  2549.      * This method is used to communicate an error and invoke error
  2550.      * callbacks etc.  Basically a wrapper for PEAR::raiseError
  2551.      * without the message string.
  2552.      *
  2553.      * @param int|PEAR_Error integer error code or and PEAR_Error instance
  2554.      * @param int      error mode, see PEAR_Error docs
  2555.      *
  2556.      *                  error level (E_USER_NOTICE etc).  If error mode is
  2557.      *                  PEAR_ERROR_CALLBACK, this is the callback function,
  2558.      *                  either as a function name, or as an array of an
  2559.      *                  object and method name.  For other error modes this
  2560.      *                  parameter is ignored.
  2561.      * @param array    Options, depending on the mode, @see PEAR::setErrorHandling
  2562.      * @param string   Extra debug information.  Defaults to the last
  2563.      *                  query and native error code.
  2564.      * @return object  PEAR error object
  2565.      * @access  public
  2566.      * @see PEAR_Error
  2567.      */
  2568.     function &raiseError($code = null$mode = null$options = null$userinfo = null)
  2569.     {
  2570.         $err =PEAR::raiseError(null$code$mode$options$userinfo'MDB2_Schema_Error'true);
  2571.         return $err;
  2572.     }
  2573.  
  2574.     // }}}
  2575.     // {{{ isError()
  2576.  
  2577.     /**
  2578.      * Tell whether a value is an MDB2_Schema error.
  2579.      *
  2580.      * @param   mixed the value to test
  2581.      * @param   int   if $data is an error object, return true only if $code is
  2582.                       a string and $db->getMessage() == $code or
  2583.      *                 $code is an integer and $db->getCode() == $code
  2584.      * @return  bool  true if parameter is an error
  2585.      * @access  public
  2586.      */
  2587.     function isError($data$code = null)
  2588.     {
  2589.         if (is_a($data'MDB2_Schema_Error')) {
  2590.             if (is_null($code)) {
  2591.                 return true;
  2592.             elseif (is_string($code)) {
  2593.                 return $data->getMessage(=== $code;
  2594.             else {
  2595.                 $code = (array)$code;
  2596.                 return in_array($data->getCode()$code);
  2597.             }
  2598.         }
  2599.         return false;
  2600.     }
  2601.  
  2602.     // }}}
  2603. }
  2604.  
  2605. /**
  2606.  * MDB2_Schema_Error implements a class for reporting portable database error
  2607.  * messages.
  2608.  *
  2609.  * @package MDB2_Schema
  2610.  * @category Database
  2611.  * @author  Stig Bakken <ssb@fast.no>
  2612.  */
  2613. class MDB2_Schema_Error extends PEAR_Error
  2614. {
  2615.     /**
  2616.      * MDB2_Schema_Error constructor.
  2617.      *
  2618.      * @param mixed     error code, or string with error message.
  2619.      * @param int       what 'error mode' to operate in
  2620.      * @param int       what error level to use for $mode & PEAR_ERROR_TRIGGER
  2621.      * @param mixed     additional debug info, such as the last query
  2622.      * @access  public
  2623.      */
  2624.     function MDB2_Schema_Error($code = MDB2_SCHEMA_ERROR$mode = PEAR_ERROR_RETURN,
  2625.               $level = E_USER_NOTICE$debuginfo = null)
  2626.     {
  2627.         $this->PEAR_Error('MDB2_Schema Error: ' MDB2_Schema::errorMessage($code)$code,
  2628.             $mode$level$debuginfo);
  2629.     }
  2630. }
  2631. ?>

Documentation generated on Mon, 11 Mar 2019 15:16:44 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.