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

Source for file Schema.php

Documentation is available at Schema.php

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

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