Viewing file: Manager.php (102.93 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php // +----------------------------------------------------------------------+ // | PHP Version 4 | // +----------------------------------------------------------------------+ // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ // | MDB is a merge of PEAR DB and Metabases that provides a unified DB | // | API as well as database abstraction for PHP applications. | // | This LICENSE is in the BSD license style. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | | // | Redistributions in binary form must reproduce the above copyright | // | notice, this list of conditions and the following disclaimer in the | // | documentation and/or other materials provided with the distribution. | // | | // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, | // | Lukas Smith nor the names of his contributors may be used to endorse | // | or promote products derived from this software without specific prior| // | written permission. | // | | // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS| // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY| // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | // | POSSIBILITY OF SUCH DAMAGE. | // +----------------------------------------------------------------------+ // | Author: Lukas Smith <smith@backendmedia.com> | // +----------------------------------------------------------------------+ // // $Id: Manager.php,v 1.75.4.4 2004/03/10 14:42:59 lsmith Exp $ //
require_once('MDB/Parser.php');
define('MDB_MANAGER_DUMP_ALL', 0); define('MDB_MANAGER_DUMP_STRUCTURE', 1); define('MDB_MANAGER_DUMP_CONTENT', 2);
/** * The database manager is a class that provides a set of database * management services like installing, altering and dumping the data * structures of databases. * * @package MDB * @category Database * @author Lukas Smith <smith@backendmedia.com> */ class MDB_Manager extends PEAR { // {{{ properties
var $database;
var $options = array( 'fail_on_invalid_names' => 1, 'debug' => 0 ); var $invalid_names = array( 'user' => array(), 'is' => array(), 'file' => array( 'oci' => array(), 'oracle' => array() ), 'notify' => array( 'pgsql' => array() ), 'restrict' => array( 'mysql' => array() ), 'password' => array( 'ibase' => array() ) ); var $default_values = array( 'integer' => 0, 'float' => 0, 'decimal' => 0, 'text' => '', 'timestamp' => '0001-01-01 00:00:00', 'date' => '0001-01-01', 'time' => '00:00:00' );
var $warnings = array();
var $database_definition = array( 'name' => '', 'create' => 0, 'TABLES' => array() );
// }}} // {{{ raiseError()
/** * This method is used to communicate an error and invoke error * callbacks etc. Basically a wrapper for PEAR::raiseError * without the message string. * * @param mixed $code integer error code, or a PEAR error object (all * other parameters are ignored if this parameter is an object * @param int $mode error mode, see PEAR_Error docs * @param mixed $options If error mode is PEAR_ERROR_TRIGGER, this is the * error level (E_USER_NOTICE etc). If error mode is * PEAR_ERROR_CALLBACK, this is the callback function, either as a * function name, or as an array of an object and method name. For * other error modes this parameter is ignored. * @param string $userinfo Extra debug information. Defaults to the last * query and native error code. * @param mixed $nativecode Native error code, integer or string depending * the backend. * @return object a PEAR error object * @access public * @see PEAR_Error */ function &raiseError($code = MDB_MANAGER_ERROR, $mode = NULL, $options = NULL, $userinfo = NULL, $nativecode = NULL) { // The error is yet a MDB error object if(is_object($code)) { $err = PEAR::raiseError($code, NULL, NULL, NULL, NULL, NULL, TRUE); return($err); } $err = PEAR::raiseError(NULL, $code, $mode, $options, $userinfo, 'MDB_Error', TRUE); return($err); }
// }}} // {{{ captureDebugOutput()
/** * set a debug handler * * @param string $capture name of the function that should be used in * debug() * @access public * @see debug() */ function captureDebugOutput($capture) { $this->options['debug'] = $capture; $this->database->captureDebugOutput(1); }
// }}} // {{{ debugOutput()
/** * output debug info * * @return string content of the debug_output class variable * @access public */ function debugOutput() { return($this->database->debugOutput()); }
// }}} // {{{ resetWarnings()
/** * reset the warning array * * @access public */ function resetWarnings() { $this->warnings = array(); }
// }}} // {{{ getWarnings()
/** * get all warnings in reverse order. * This means that the last warning is the first element in the array * * @return array with warnings * @access public * @see resetWarnings() */ function getWarnings() { return array_reverse($this->warnings); }
// }}} // {{{ setOption()
/** * set the option for the db class * * @param string $option option name * @param mixed $value value for the option * @return mixed MDB_OK or MDB_Error * @access public */ function setOption($option, $value) { if(isset($this->options[$option])) { $this->options[$option] = $value; return(MDB_OK); } return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, "unknown option $option")); }
// }}} // {{{ getOption()
/** * returns the value of an option * * @param string $option option name * @return mixed the option value or error object * @access public */ function getOption($option) { if(isset($this->options[$option])) { return($this->options[$option]); } return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, "unknown option $option")); }
// }}} // {{{ connect()
/** * Create a new MDB connection object and connect to the specified * database * * @param mixed $dbinfo 'data source name', see the MDB::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB::parseDSN. * Finally you can also pass an existing db * object to be used. * @param mixed $options An associative array of option names and * their values. * @return mixed MDB_OK on success, or a MDB error object * @access public * @see MDB::parseDSN */ function &connect(&$dbinfo, $options = FALSE) { if(is_object($this->database) && !MDB::isError($this->database)) { $this->disconnect(); } if(is_object($dbinfo)) { $this->database =& $dbinfo; } else { $this->database =& MDB::connect($dbinfo, $options); if(MDB::isError($this->database)) { return($this->database); } } if(is_array($options)) { $this->options = array_merge($options, $this->options); } return(MDB_OK); }
// }}} // {{{ disconnect()
/** * Log out and disconnect from the database. * * @access public */ function disconnect() { if(is_object($this->database) && !MDB::isError($this->database)) { $this->database->disconnect(); unset($this->database); } }
// }}} // {{{ setDatabase()
/** * Select a different database * * @param string $name name of the database that should be selected * @return string name of the database previously connected to * @access public */ function setDatabase($name) { return($this->database->setDatabase($name)); }
// }}} // {{{ _createTable()
/** * create a table and inititialize the table if data is available * * @param string $table_name name of the table to be created * @param array $table multi dimensional array that containts the * structure and optional data of the table * @param boolean $overwrite determine if the table/index should be overwritten if it already exists * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _createTable($table_name, $table, $overwrite = FALSE) { $this->expectError(MDB_ERROR_ALREADY_EXISTS); $result = $this->database->createTable($table_name, $table['FIELDS']); $this->popExpect(); if(MDB::isError($result)) { if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) { $this->warnings[] = 'Table already exists: '.$table_name; if($overwrite) { $this->database->debug('Overwritting Table'); $result = $this->database->dropTable($table_name); if(MDB::isError($result)) { return($result); } $result = $this->database->createTable($table_name, $table['FIELDS']); if(MDB::isError($result)) { return($result); } } else { $result = MDB_OK; } } else { $this->database->debug('Create table error: '.$table_name); return($result); } } if(isset($table['initialization']) && is_array($table['initialization'])) { foreach($table['initialization'] as $instruction) { switch($instruction['type']) { case 'insert': $query_fields = $query_values = array(); if(isset($instruction['FIELDS']) && is_array($instruction['FIELDS'])) { foreach($instruction['FIELDS'] as $field_name => $field) { $query_fields[] = $field_name; $query_values[] = '?'; } $query_fields = implode(',',$query_fields); $query_values = implode(',',$query_values); $result = $prepared_query = $this->database->prepareQuery( "INSERT INTO $table_name ($query_fields) VALUES ($query_values)"); } if(!MDB::isError($prepared_query)) { if(isset($instruction['FIELDS']) && is_array($instruction['FIELDS'])) { $lobs = array(); $field_number = 0; foreach($instruction['FIELDS'] as $field_name => $field) { $field_number++; $query = $field_name; switch($table['FIELDS'][$field_name]['type']) { case 'integer': $result = $this->database->setParamInteger($prepared_query, $field_number, intval($field)); break; case 'text': $result = $this->database->setParamText($prepared_query, $field_number, $field); break; case 'clob': $lob_definition = array( 'Database' => $this->database, 'Error' => '', 'Data' => $field ); if(MDB::isError($result = $this->database->createLob($lob_definition))) { break; } $lob = count($lobs); $lobs[$lob] = $result; $result = $this->database->setParamClob($prepared_query, $field_number, $lobs[$lob], $field_name); break; case 'blob': $lob_definition = array( 'Database' => $this->database, 'Error' => '', 'Data' => $field ); if(MDB::isError($result = $this->database->createLob($lob_definition))) { break; } $lob = count($lobs); $lobs[$lob] = $result; $result = $this->database->setParamBlob($prepared_query, $field_number, $lobs[$lob], $field_name); break; case 'boolean': $result = $this->database->setParamBoolean($prepared_query, $field_number, intval($field)); break; case 'date': $result = $this->database->setParamDate($prepared_query, $field_number, $field); break; case 'timestamp': $result = $this->database->setParamTimestamp($prepared_query, $field_number, $field); break; case 'time': $result = $this->database->setParamTime($prepared_query, $field_number, $field); break; case 'float': $result = $this->database->setParamFloat($prepared_query, $field_number, doubleval($field)); break; case 'decimal': $result = $this->database->setParamDecimal($prepared_query, $field_number, $field); break; default: $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'type "'.$field['type'].'" is not yet supported'); break; } if(MDB::isError($result)) { break; } } } if(!MDB::isError($result)) { $result = $this->database->executeQuery($prepared_query); } for($lob = 0; $lob < count($lobs); $lob++) { $this->database->destroyLOB($lobs[$lob]); } $this->database->freePreparedQuery($prepared_query); } break; } } }; if(!MDB::isError($result) && isset($table['INDEXES']) && is_array($table['INDEXES'])) { if(!$this->database->support('Indexes')) { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'indexes are not supported')); } foreach($table['INDEXES'] as $index_name => $index) { $this->expectError(MDB_ERROR_ALREADY_EXISTS); $result = $this->database->createIndex($table_name, $index_name, $index); $this->popExpect(); if(MDB::isError($result)) { if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) { $this->warnings[] = 'Index already exists: '.$index_name; if($overwrite) { $this->database->debug('Overwritting Index'); $result = $this->database->dropIndex($table_name, $index_name); if(MDB::isError($result)) { break; } $result = $this->database->createIndex($table_name, $index_name, $index); if(MDB::isError($result)) { break; } } else { $result = MDB_OK; } } else { $this->database->debug('Create index error: '.$table_name); break; } } } } if(MDB::isError($result)) { $result = $this->database->dropTable($table_name); if(MDB::isError($result)) { $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'could not drop the table (' .$result->getMessage().' ('.$result->getUserinfo(),'))', 'MDB_Error', TRUE); } return($result); } return(MDB_OK); }
// }}} // {{{ _dropTable()
/** * drop a table * * @param string $table_name name of the table to be dropped * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _dropTable($table_name) { return($this->database->dropTable($table_name)); }
// }}} // {{{ _createSequence()
/** * create a sequence * * @param string $sequence_name name of the sequence to be created * @param array $sequence multi dimensional array that containts the * structure and optional data of the table * @param string $created_on_table * @param boolean $overwrite determine if the sequence should be overwritten if it already exists * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _createSequence($sequence_name, $sequence, $created_on_table, $overwrite = FALSE) { if(!$this->database->support('Sequences')) { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'sequences are not supported')); } if(!isset($sequence_name) || !strcmp($sequence_name, '')) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'no valid sequence name specified')); } $this->database->debug('Create sequence: '.$sequence_name); if(isset($sequence['start']) && $sequence['start'] != '') { $start = $sequence['start']; } else if(isset($sequence['on']) && !$created_on_table) { $table = $sequence['on']['table']; $field = $sequence['on']['field']; if($this->database->support('Summaryfunctions')) { $field = "MAX($field)"; } $start = $this->database->queryOne("SELECT $field FROM $table"); if(MDB::isError($start)) { return($start); } } else { $start = 1; } $this->expectError(MDB_ERROR_ALREADY_EXISTS); $result = $this->database->createSequence($sequence_name, $start); $this->popExpect(); if(MDB::isError($result)) { if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) { $this->warnings[] = 'Sequence already exists: '.$sequence_name; if($overwrite) { $this->database->debug('Overwritting Sequence'); $result = $this->database->dropSequence($sequence_name); if(MDB::isError($result)) { return($result); } $result = $this->database->createSequence($sequence_name, $start); if(MDB::isError($result)) { return($result); } } else { return(MDB_OK); } } else { $this->database->debug('Create sequence error: '.$sequence_name); return($result); } } }
// }}} // {{{ _dropSequence()
/** * drop a table * * @param string $sequence_name name of the sequence to be dropped * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _dropSequence($sequence_name) { if(!$this->database->support('Sequences')) { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'sequences are not supported')); } $this->database->debug('Dropping sequence: '.$sequence_name); if(!isset($sequence_name) || !strcmp($sequence_name, '')) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'no valid sequence name specified')); } return($this->database->dropSequence($sequence_name)); }
// }}} // {{{ _createDatabase()
/** * Create a database space within which may be created database objects * like tables, indexes and sequences. The implementation of this function * is highly DBMS specific and may require special permissions to run * successfully. Consult the documentation or the DBMS drivers that you * use to be aware of eventual configuration requirements. * * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _createDatabase() { if(!isset($this->database_definition['name']) || !strcmp($this->database_definition['name'], '') ) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'no valid database name specified')); } $create = (isset($this->database_definition['create']) && $this->database_definition['create']); $overwrite = (isset($this->database_definition['overwrite']) && $this->database_definition['overwrite']); if($create) { $this->database->debug('Create database: '.$this->database_definition['name']); $this->expectError(MDB_ERROR_ALREADY_EXISTS); $result = $this->database->createDatabase($this->database_definition['name']); $this->popExpect(); if(MDB::isError($result)) { if($result->getCode() === MDB_ERROR_ALREADY_EXISTS) { $this->warnings[] = 'Database already exists: '.$this->database_definition['name']; if($overwrite) { $this->database->debug('Overwritting Database'); $result = $this->database->dropDatabase($this->database_definition['name']); if(MDB::isError($result)) { return($result); } $result = $this->database->createDatabase($this->database_definition['name']); if(MDB::isError($result)) { return($result); } } else { $result = MDB_OK; } } else { $this->database->debug('Create database error.'); return($result); } } } $previous_database_name = $this->database->setDatabase($this->database_definition['name']); if(($support_transactions = $this->database->support('Transactions')) && MDB::isError($result = $this->database->autoCommit(FALSE)) ) { return($result); }
$created_objects = 0; if(isset($this->database_definition['TABLES']) && is_array($this->database_definition['TABLES']) ) { foreach($this->database_definition['TABLES'] as $table_name => $table) { $result = $this->_createTable($table_name, $table, $overwrite); if(MDB::isError($result)) { break; } $created_objects++; } } if(!MDB::isError($result) && isset($this->database_definition['SEQUENCES']) && is_array($this->database_definition['SEQUENCES']) ) { foreach($this->database_definition['SEQUENCES'] as $sequence_name => $sequence) { $result = $this->_createSequence($sequence_name, $sequence, 0, $overwrite); if(MDB::isError($result)) { break; } $created_objects++; } } if(MDB::isError($result)) { if($created_objects) { if($support_transactions) { $res = $this->database->rollback(); if(MDB::isError($res)) $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'Could not rollback the partially created database alterations (' .$result->getMessage().' ('.$result->getUserinfo(),'))', 'MDB_Error', TRUE); } else { $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'the database was only partially created (' .$result->getMessage().' ('.$result->getUserinfo(),'))', 'MDB_Error', TRUE); } } } else { if($support_transactions) { $res = $this->database->autoCommit(TRUE); if(MDB::isError($res)) $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'Could not end transaction after successfully created the database (' .$res->getMessage().' ('.$res->getUserinfo(),'))', 'MDB_Error', TRUE); } } $this->database->setDatabase($previous_database_name); if(MDB::isError($result) && $create && MDB::isError($res = $this->database->dropDatabase($this->database_definition['name'])) ) { return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'Could not drop the created database after unsuccessful creation attempt (' .$res->getMessage().' ('.$res->getUserinfo(),'))', 'MDB_Error', TRUE)); } if(MDB::isError($result)) { return($result); } return(MDB_OK); }
// }}} // {{{ _addDefinitionChange()
/** * add change to an array of multiple changes * * @param array &$changes * @param string $definition * @param string $item * @param array $change * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _addDefinitionChange(&$changes, $definition, $item, $change) { if(!isset($changes[$definition][$item])) { $changes[$definition][$item] = array(); } foreach($change as $change_data_name => $change_data) { if(isset($change_data) && is_array($change_data)) { if(!isset($changes[$definition][$item][$change_data_name])) { $changes[$definition][$item][$change_data_name] = array(); } foreach($change_data as $change_part_name => $change_part) { $changes[$definition][$item][$change_data_name][$change_part_name] = $change_part; } } else { $changes[$definition][$item][$change_data_name] = $change_data; } } return(MDB_OK); }
// }}} // {{{ _compareDefinitions()
/** * compare a previous definition with the currenlty parsed definition * * @param array multi dimensional array that contains the previous definition * @return mixed array of changes on success, or a MDB error object * @access private */ function _compareDefinitions($previous_definition) { $defined_tables = $changes = array(); if(isset($this->database_definition['TABLES']) && is_array($this->database_definition['TABLES'])) { foreach($this->database_definition['TABLES'] as $table_name => $table) { $was_table_name = $table['was']; if(isset($previous_definition['TABLES'][$table_name]) && isset($previous_definition['TABLES'][$table_name]['was']) && !strcmp($previous_definition['TABLES'][$table_name]['was'], $was_table_name) ) { $was_table_name = $table_name; } if(isset($previous_definition['TABLES'][$was_table_name])) { if(strcmp($was_table_name, $table_name)) { $this->_addDefinitionChange($changes, 'TABLES', $was_table_name, array('name' => $table_name)); $this->database->debug("Renamed table '$was_table_name' to '$table_name'"); } if(isset($defined_tables[$was_table_name])) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'the table "'.$was_table_name.'" was specified as base of more than of table of the database', 'MDB_Error', TRUE)); } $defined_tables[$was_table_name] = 1; $previous_fields = $previous_definition['TABLES'][$was_table_name]['FIELDS']; $defined_fields = array(); if(isset($table['FIELDS']) && is_array($table['FIELDS'])) { foreach($table['FIELDS'] as $field_name => $field) { $was_field_name = $field['was']; if(isset($previous_fields[$field_name]) && isset($previous_fields[$field_name]['was']) && !strcmp($previous_fields[$field_name]['was'], $was_field_name) ) { $was_field_name = $field_name; } if(isset($previous_fields[$was_field_name])) { if(strcmp($was_field_name, $field_name)) { $query = $this->database->getFieldDeclaration($field_name, $field); if(MDB::isError($query)) { return($query); } $this->_addDefinitionChange($changes, 'TABLES', $was_table_name, array( 'RenamedFields' => array( $was_field_name => array( 'name' => $field_name, 'Declaration' => $query ) ) ) ); $this->database->debug("Renamed field '$was_field_name' to '$field_name' in table '$table_name'"); } if(isset($defined_fields[$was_field_name])) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'the field "'.$was_table_name.'" was specified as base of more than one field of table', 'MDB_Error', TRUE)); } $defined_fields[$was_field_name] = 1; $change = array(); if($field['type'] == $previous_fields[$was_field_name]['type']) { switch($field['type']) { case 'integer': $previous_unsigned = isset($previous_fields[$was_field_name]['unsigned']); $unsigned = isset($fields[$field_name]['unsigned']); if(strcmp($previous_unsigned, $unsigned)) { $change['unsigned'] = $unsigned; $this->database->debug("Changed field '$field_name' type from '".($previous_unsigned ? 'unsigned ' : '').$previous_fields[$was_field_name]['type']."' to '".($unsigned ? 'unsigned ' : '').$field['type']."' in table '$table_name'"); } break; case 'text': case 'clob': case 'blob': $previous_length = (isset($previous_fields[$was_field_name]['length']) ? $previous_fields[$was_field_name]['length'] : 0); $length = (isset($field['length']) ? $field['length'] : 0); if(strcmp($previous_length, $length)) { $change['length'] = $length; $this->database->debug("Changed field '$field_name' length from '".$previous_fields[$was_field_name]['type'].($previous_length == 0 ? ' no length' : "($previous_length)")."' to '".$field['type'].($length == 0 ? ' no length' : "($length)")."' in table '$table_name'"); } break; case 'date': case 'timestamp': case 'time': case 'boolean': case 'float': case 'decimal': break; default: return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'type "'.$field['type'].'" is not yet supported', 'MDB_Error', TRUE)); } $previous_notnull = isset($previous_fields[$was_field_name]['notnull']); $notnull = isset($field['notnull']); if($previous_notnull != $notnull) { $change['ChangedNotNull'] = 1; if($notnull) { $change['notnull'] = isset($field['notnull']); } $this->database->debug("Changed field '$field_name' notnull from $previous_notnull to $notnull in table '$table_name'"); } $previous_default = isset($previous_fields[$was_field_name]['default']); $default = isset($field['default']); if(strcmp($previous_default, $default)) { $change['ChangedDefault'] = 1; if($default) { $change['default'] = $field['default']; } $this->database->debug("Changed field '$field_name' default from ".($previous_default ? "'".$previous_fields[$was_field_name]['default']."'" : 'NULL').' TO '.($default ? "'".$fields[$field_name]['default']."'" : 'NULL')." IN TABLE '$table_name'"); } else { if($default && strcmp($previous_fields[$was_field_name]['default'], $field['default']) ) { $change['ChangedDefault'] = 1; $change['default'] = $field['default']; $this->database->debug("Changed field '$field_name' default from '".$previous_fields[$was_field_name]['default']."' to '".$fields[$field_name]['default']."' in table '$table_name'"); } } } else { $change['type'] = $field['type']; $this->database->debug("Changed field '$field_name' type from '".$previous_fields[$was_field_name]['type']."' to '".$fields[$field_name]['type']."' in table '$table_name'"); } if(count($change)) { $query = $this->database->getFieldDeclaration($field_name, $field); if(MDB::isError($query)) { return($query); } $change['Declaration'] = $query; $change['Definition'] = $field; $this->_addDefinitionChange($changes, 'TABLES', $was_table_name, array('ChangedFields' => array($field_name => $change))); } } else { if(strcmp($field_name, $was_field_name)) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'it was specified a previous field name ("' .$was_field_name.'") for field "'.$field_name.'" of table "' .$table_name.'" that does not exist', 'MDB_Error', TRUE)); } $query = $this->database->getFieldDeclaration($field_name, $field); if(MDB::isError($query)) { return($query); } $change['Declaration'] = $query; $this->_addDefinitionChange($changes, 'TABLES', $table_name, array('AddedFields' => array($field_name => $change))); $this->database->debug("Added field '$field_name' to table '$table_name'"); } } } if(isset($previous_fields) && is_array($previous_fields)) { foreach ($previous_fields as $field_previous_name => $field_previous) { if(!isset($defined_fields[$field_previous_name])) { $this->_addDefinitionChange($changes, 'TABLES', $table_name, array('RemovedFields' => array($field_previous_name => array()))); $this->database->debug("Removed field '$field_name' from table '$table_name'"); } } } $indexes = array(); if(isset($this->database_definition['TABLES'][$table_name]['INDEXES']) && is_array($this->database_definition['TABLES'][$table_name]['INDEXES']) ) { $indexes = $this->database_definition['TABLES'][$table_name]['INDEXES']; } $previous_indexes = array(); if(isset($previous_definition['TABLES'][$was_table_name]['INDEXES']) && is_array($previous_definition['TABLES'][$was_table_name]['INDEXES']) ) { $previous_indexes = $previous_definition['TABLES'][$was_table_name]['INDEXES']; } $defined_indexes = array(); foreach($indexes as $index_name => $index) { $was_index_name = $index['was']; if(isset($previous_indexes[$index_name]) && isset($previous_indexes[$index_name]['was']) && !strcmp($previous_indexes[$index_name]['was'], $was_index_name) ) { $was_index_name = $index_name; } if(isset($previous_indexes[$was_index_name])) { $change = array(); if(strcmp($was_index_name, $index_name)) { $change['name'] = $was_index_name; $this->database->debug("Changed index '$was_index_name' name to '$index_name' in table '$table_name'"); } if(isset($defined_indexes[$was_index_name])) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'the index "'.$was_index_name.'" was specified as base of' .' more than one index of table "'.$table_name.'"', 'MDB_Error', TRUE)); } $defined_indexes[$was_index_name] = 1; $previous_unique = isset($previous_indexes[$was_index_name]['unique']); $unique = isset($index['unique']); if($previous_unique != $unique) { $change['ChangedUnique'] = 1; if($unique) { $change['unique'] = $unique; } $this->database->debug("Changed index '$index_name' unique from $previous_unique to $unique in table '$table_name'"); } $defined_fields = array(); $previous_fields = $previous_indexes[$was_index_name]['FIELDS']; if(isset($index['FIELDS']) && is_array($index['FIELDS'])) { foreach($index['FIELDS'] as $field_name => $field) { if(isset($previous_fields[$field_name])) { $defined_fields[$field_name] = 1; $sorting = (isset($field['sorting']) ? $field['sorting'] : ''); $previous_sorting = (isset($previous_fields[$field_name]['sorting']) ? $previous_fields[$field_name]['sorting'] : ''); if(strcmp($sorting, $previous_sorting)) { $this->database->debug("Changed index field '$field_name' sorting default from '$previous_sorting' to '$sorting' in table '$table_name'"); $change['ChangedFields'] = 1; } } else { $change['ChangedFields'] = 1; $this->database->debug("Added field '$field_name' to index '$index_name' of table '$table_name'"); } } } if(isset($previous_fields) && is_array($previous_fields)) { foreach($previous_fields as $field_name => $field) { if(!isset($defined_fields[$field_name])) { $change['ChangedFields'] = 1; $this->database->debug("Removed field '$field_name' from index '$index_name' of table '$table_name'"); } } } if(count($change)) { $this->_addDefinitionChange($changes, 'INDEXES', $table_name,array('ChangedIndexes' => array($index_name => $change))); } } else { if(strcmp($index_name, $was_index_name)) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'it was specified a previous index name ("'.$was_index_name .') for index "'.$index_name.'" of table "'.$table_name.'" that does not exist', 'MDB_Error', TRUE)); } $this->_addDefinitionChange($changes, 'INDEXES', $table_name,array('AddedIndexes' => array($index_name => $indexes[$index_name]))); $this->database->debug("Added index '$index_name' to table '$table_name'"); } } foreach($previous_indexes as $index_previous_name => $index_previous) { if(!isset($defined_indexes[$index_previous_name])) { $this->_addDefinitionChange($changes, 'INDEXES', $table_name, array('RemovedIndexes' => array($index_previous_name => $was_table_name))); $this->database->debug("Removed index '$index_name' from table '$table_name'"); } } } else { if(strcmp($table_name, $was_table_name)) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'it was specified a previous table name ("' .$was_table_name.'") for table "'.$table_name.'" that does not exist', 'MDB_Error', TRUE)); } $this->_addDefinitionChange($changes, 'TABLES', $table_name,array('Add' => 1)); $this->database->debug("Added table '$table_name'"); } } if(isset($previous_definition['TABLES']) && is_array($previous_definition['TABLES'])) { foreach ($previous_definition['TABLES'] as $table_name => $table) { if(!isset($defined_tables[$table_name])) { $this->_addDefinitionChange($changes, 'TABLES', $table_name, array('Remove' => 1)); $this->database->debug("Removed table '$table_name'"); } } } if(isset($this->database_definition['SEQUENCES']) && is_array($this->database_definition['SEQUENCES'])) { foreach ($this->database_definition['SEQUENCES'] as $sequence_name => $sequence) { $was_sequence_name = $sequence['was']; if(isset($previous_definition['SEQUENCES'][$sequence_name]) && isset($previous_definition['SEQUENCES'][$sequence_name]['was']) && !strcmp($previous_definition['SEQUENCES'][$sequence_name]['was'], $was_sequence_name) ) { $was_sequence_name = $sequence_name; } if(isset($previous_definition['SEQUENCES'][$was_sequence_name])) { if(strcmp($was_sequence_name, $sequence_name)) { $this->_addDefinitionChange($changes, 'SEQUENCES', $was_sequence_name,array('name' => $sequence_name)); $this->database->debug("Renamed sequence '$was_sequence_name' to '$sequence_name'"); } if(isset($defined_sequences[$was_sequence_name])) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'the sequence "'.$was_sequence_name.'" was specified as base' .' of more than of sequence of the database', 'MDB_Error', TRUE)); } $defined_sequences[$was_sequence_name] = 1; $change = array(); if(strcmp($sequence['start'], $previous_definition['SEQUENCES'][$was_sequence_name]['start'])) { $change['start'] = $this->database_definition['SEQUENCES'][$sequence_name]['start']; $this->database->debug("Changed sequence '$sequence_name' start from '".$previous_definition['SEQUENCES'][$was_sequence_name]['start']."' to '".$this->database_definition['SEQUENCES'][$sequence_name]['start']."'"); } if(strcmp($sequence['on']['table'], $previous_definition['SEQUENCES'][$was_sequence_name]['on']['table']) || strcmp($sequence['on']['field'], $previous_definition['SEQUENCES'][$was_sequence_name]['on']['field']) ) { $change['on'] = $sequence['on']; $this->database->debug("Changed sequence '$sequence_name' on table field from '".$previous_definition['SEQUENCES'][$was_sequence_name]['on']['table'].'.'.$previous_definition['SEQUENCES'][$was_sequence_name]['on']['field']."' to '".$this->database_definition['SEQUENCES'][$sequence_name]['on']['table'].'.'.$this->database_definition['SEQUENCES'][$sequence_name]['on']['field']."'"); } if(count($change)) { $this->_addDefinitionChange($changes, 'SEQUENCES', $was_sequence_name,array('Change' => array($sequence_name => array($change)))); } } else { if(strcmp($sequence_name, $was_sequence_name)) { return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL, 'it was specified a previous sequence name ("'.$was_sequence_name .'") for sequence "'.$sequence_name.'" that does not exist', 'MDB_Error', TRUE)); } $this->_addDefinitionChange($changes, 'SEQUENCES', $sequence_name, array('Add' => 1)); $this->database->debug("Added sequence '$sequence_name'"); } } } if(isset($previous_definition['SEQUENCES']) && is_array($previous_definition['SEQUENCES'])) { foreach ($previous_definition['SEQUENCES'] as $sequence_name => $sequence) { if(!isset($defined_sequences[$sequence_name])) { $this->_addDefinitionChange($changes, 'SEQUENCES', $sequence_name, array('Remove' => 1)); $this->database->debug("Removed sequence '$sequence_name'"); } } } } return($changes); }
// }}} // {{{ _alterDatabase()
/** * Execute the necessary actions to implement the requested changes * in a database structure. * * @param array $previous_definition an associative array that contains * the definition of the database structure before applying the requested * changes. The definition of this array may be built separately, but * usually it is built by the Parse method the Metabase parser class. * @param array $changes an associative array that contains the definition of * the changes that are meant to be applied to the database structure. * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _alterDatabase($previous_definition, $changes) { $result = ''; if(isset($changes['TABLES']) && is_array($changes['TABLES'])) { foreach($changes['TABLES'] as $table_name => $table) { if(isset($table['Add']) || isset($table['Remove'])) { continue; } $result = $this->database->alterTable($table_name, $table, 1); if(MDB::isError($result)) { return($result); } } } if(isset($changes['SEQUENCES']) && is_array($changes['SEQUENCES'])) { if(!$this->database->support('Sequences')) { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'sequences are not supported')); } foreach($changes['SEQUENCES'] as $sequence) { if(isset($sequence['Add']) || isset($sequence['Remove']) || isset($sequence['Change']) ) { continue; } return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'some sequences changes are not yet supported')); } } if(isset($changes['INDEXES']) && is_array($changes['INDEXES'])) { if(!$this->database->support('Indexes')) { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'indexes are not supported')); } foreach($changes['INDEXES'] as $index) { $table_changes = count($index); if(isset($index['AddedIndexes'])) { $table_changes--; } if(isset($index['RemovedIndexes'])) { $table_changes--; } if(isset($index['ChangedIndexes'])) { $table_changes--; } if($table_changes) { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'index alteration not yet supported')); } } } $previous_database_name = $this->database->setDatabase($this->database_definition['name']); if(($support_transactions = $this->database->support('Transactions')) && MDB::isError($result = $this->database->autoCommit(FALSE)) ) { return($result); } $error = ''; $alterations = 0; if(isset($changes['INDEXES']) && is_array($changes['INDEXES'])) { foreach($changes['INDEXES'] as $index_name => $index) { if(isset($index['RemovedIndexes']) && is_array($index['RemovedIndexes'])) { foreach($index['RemovedIndexes'] as $index_remove_name => $index_remove) { $result = $this->database->dropIndex($index_name,$index_remove_name); if(MDB::isError($result)) { break; } $alterations++; } } if(!MDB::isError($result) && is_array($index['ChangedIndexes']) ) { foreach($index['ChangedIndexes'] as $index_changed_name => $index_changed) { $was_name = (isset($indexes[$name]['name']) ? $indexes[$index_changed_name]['name'] : $index_changed_name); $result = $this->database->dropIndex($index_name, $was_name); if(MDB::isError($result)) { break; } $alterations++; } } if(MDB::isError($result)) { break; } } } if(!MDB::isError($result) && isset($changes['TABLES']) && is_array($changes['TABLES']) ) { foreach($changes['TABLES'] as $table_name => $table) { if(isset($table['Remove'])) { $result = $this->_dropTable($table_name); if(!MDB::isError($result)) { $alterations++; } } else { if(!isset($table['Add'])) { $result = $this->database->alterTable($table_name, $changes['TABLES'][$table_name], 0); if(!MDB::isError($result)) { $alterations++; } } } if(MDB::isError($result)) { break; } } foreach($changes['TABLES'] as $table_name => $table) { if(isset($table['Add'])) { $result = $this->_createTable($table_name, $this->database_definition['TABLES'][$table_name]); if(!MDB::isError($result)) { $alterations++; } } if(MDB::isError($result)) { break; } } } if(!MDB::isError($result) && isset($changes['SEQUENCES']) && is_array($changes['SEQUENCES'])) { foreach($changes['SEQUENCES'] as $sequence_name => $sequence) { if(isset($sequence['Add'])) { $created_on_table = 0; if(isset($this->database_definition['SEQUENCES'][$sequence_name]['on'])) { $table = $this->database_definition['SEQUENCES'][$sequence_name]['on']['table']; if(isset($changes['TABLES']) && isset($changes['TABLES'][$table_name]) && isset($changes['TABLES'][$table_name]['Add']) ) { $created_on_table = 1; } } $result = $this->_createSequence($sequence_name, $this->database_definition['SEQUENCES'][$sequence_name], $created_on_table); if(!MDB::isError($result)) { $alterations++; } } else { if(isset($sequence['Remove'])) { if(!strcmp($error = $this->_dropSequence($sequence_name), '')) { $alterations++; } } else { if(isset($sequence['Change'])) { $created_on_table = 0; if(isset($this->database_definition['SEQUENCES'][$sequence_name]['on'])) { $table = $this->database_definition['SEQUENCES'][$sequence_name]['on']['table']; if(isset($changes['TABLES']) && isset($changes['TABLES'][$table_name]) && isset($changes['TABLES'][$table_name]['Add']) ) { $created_on_table = 1; } } if(!MDB::isError($result = $this->_dropSequence( $this->database_definition['SEQUENCES'][$sequence_name]['was']), '') && !MDB::isError($result = $this->_createSequence( $sequence_name, $this->database_definition['SEQUENCES'][$sequence_name], $created_on_table), '') ) { $alterations++; } } else { return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'changing sequences is not yet supported')); } } } if(MDB::isError($result)) { break; } } } if(!MDB::isError($result) && isset($changes['INDEXES']) && is_array($changes['INDEXES'])) { foreach($changes['INDEXES'] as $table_name => $indexes) { if(isset($indexes['ChangedIndexes'])) { $changedindexes = $indexes['ChangedIndexes']; foreach($changedindexes as $index_name => $index) { $result = $this->database->createIndex($table_name, $index_name, $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name]); if(MDB::isError($result)) { break; } $alterations++; } } if(!MDB::isError($result) && isset($indexes['AddedIndexes']) ) { $addedindexes = $indexes['AddedIndexes']; foreach($addedindexes as $index_name => $index) { $result = $this->database->createIndex($table_name, $index_name, $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name]); if(MDB::isError($result)) { break; } $alterations++; } } if(MDB::isError($result)) { break; } } } if($alterations && MDB::isError($result)) { if($support_transactions) { $res = $this->database->rollback(); if(MDB::isError($res)) $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'Could not rollback the partially created database alterations (' .$result->getMessage().' ('.$result->getUserinfo(),'))', 'MDB_Error', TRUE); } else { $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'the requested database alterations were only partially implemented (' .$result->getMessage().' ('.$result->getUserinfo(),'))', 'MDB_Error', TRUE); } } if($support_transactions) { $result = $this->database->autoCommit(TRUE); if(MDB::isError($result)) { $result = $this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'Could not end transaction after successfully implemented the requested database alterations (' .$result->getMessage().' ('.$result->getUserinfo(),'))', 'MDB_Error', TRUE); } } $this->database->setDatabase($previous_database_name); return($result); }
// }}} // {{{ _escapeSpecialCharacters()
/** * add escapecharacters to all special characters in a string * * @param string $string string that should be escaped * @return string escaped string * @access private */ function _escapeSpecialCharacters($string) { if(gettype($string) != 'string') { $string = strval($string); } for($escaped = '', $character = 0; $character < strlen($string); $character++) { switch($string[$character]) { case '\"': case '>': case '<': case '&': $code = ord($string[$character]); break; default: $code = ord($string[$character]); if($code < 32 || $code>127) { break; } $escaped .= $string[$character]; continue 2; } $escaped .= "&#$code;"; } return($escaped); }
// }}} // {{{ _dumpSequence()
/** * dump the structure of a sequence * * @param string $sequence_name * @param string $eol * @return mixed string with xml seqeunce definition on success, or a MDB error object * @access private */ function _dumpSequence($sequence_name, $eol, $dump = MDB_MANAGER_DUMP_ALL) { $sequence_definition = $this->database_definition['SEQUENCES'][$sequence_name]; $buffer = "$eol <sequence>$eol <name>$sequence_name</name>$eol"; if($dump == MDB_MANAGER_DUMP_ALL || $dump == MDB_MANAGER_DUMP_CONTENT) { if(isset($sequence_definition['start'])) { $start = $sequence_definition['start']; $buffer .= " <start>$start</start>$eol"; } } if(isset($sequence_definition['on'])) { $buffer .= " <on>$eol <table>".$sequence_definition['on']['table']."</table>$eol <field>".$sequence_definition['on']['field']."</field>$eol </on>$eol"; } $buffer .= " </sequence>$eol"; return($buffer); }
// }}} // {{{ parseDatabaseDefinitionFile()
/** * Parse a database definition file by creating a Metabase schema format * parser object and passing the file contents as parser input data stream. * * @param string $input_file the path of the database schema file. * @param array $variables an associative array that the defines the text * string values that are meant to be used to replace the variables that are * used in the schema description. * @param bool $fail_on_invalid_names (optional) make function fail on invalid * names * @return mixed MDB_OK on success, or a MDB error object * @access public */ function parseDatabaseDefinitionFile($input_file, $variables, $fail_on_invalid_names = 1) { $parser =& new MDB_Parser($variables, $fail_on_invalid_names); $result = $parser->setInputFile($input_file); if(MDB::isError($result)) { return($result); }; $result = $parser->parse(); if(MDB::isError($result)) { return($result); }; if(MDB::isError($parser->error)) { return($parser->error); } return($parser->database_definition); }
// }}} // {{{ _debugDatabaseChanges()
/** * Dump the changes between two database definitions. * * @param array $changes an associative array that specifies the list * of database definitions changes as returned by the _compareDefinitions * manager class function. * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _debugDatabaseChanges($changes) { if(isset($changes['TABLES'])) { foreach($changes['TABLES'] as $table_name => $table) { $this->database->debug("$table_name:"); if(isset($table['Add'])) { $this->database->debug("\tAdded table '$table_name'"); } elseif(isset($table['Remove'])) { $this->database->debug("\tRemoved table '$table_name'"); } else { if(isset($table['name'])) { $this->database->debug("\tRenamed table '$table_name' to '".$table['name']."'"); } if(isset($table['AddedFields'])) { foreach($table['AddedFields'] as $field_name => $field) { $this->database->debug("\tAdded field '".$field_name."'"); } } if(isset($table['RemovedFields'])) { foreach($table['RemovedFields'] as $field_name => $field) { $this->database->debug("\tRemoved field '".$field_name."'"); } } if(isset($table['RenamedFields'])) { foreach($table['RenamedFields'] as $field_name => $field) { $this->database->debug("\tRenamed field '".$field_name."' to '".$field['name']."'"); } } if(isset($table['ChangedFields'])) { foreach($table['ChangedFields'] as $field_name => $field) { if(isset($field['type'])) { $this->database->debug( "\tChanged field '$field_name' type to '".$field['type']."'"); } if(isset($field['unsigned'])) { $this->database->debug( "\tChanged field '$field_name' type to '". ($field['unsigned'] ? '' : 'not ')."unsigned'"); } if(isset($field['length'])) { $this->database->debug( "\tChanged field '$field_name' length to '". ($field['length'] == 0 ? 'no length' : $field['length'])."'"); } if(isset($field['ChangedDefault'])) { $this->database->debug( "\tChanged field '$field_name' default to ". (isset($field['default']) ? "'".$field['default']."'" : 'NULL')); } if(isset($field['ChangedNotNull'])) { $this->database->debug( "\tChanged field '$field_name' notnull to ".(isset($field['notnull']) ? "'1'" : '0')); } } } } } } if(isset($changes['SEQUENCES'])) { foreach($changes['SEQUENCES'] as $sequence_name => $sequence) { $this->database->debug("$sequence_name:"); if(isset($sequence['Add'])) { $this->database->debug("\tAdded sequence '$sequence_name'"); } elseif(isset($sequence['Remove'])) { $this->database->debug("\tRemoved sequence '$sequence_name'"); } else { if(isset($sequence['name'])) { $this->database->debug("\tRenamed sequence '$sequence_name' to '".$sequence['name']."'"); } if(isset($sequence['Change'])) { foreach($sequence['Change'] as $sequence_name => $sequence) { if(isset($sequence['start'])) { $this->database->debug( "\tChanged sequence '$sequence_name' start to '".$sequence['start']."'"); } } } } } } if(isset($changes['INDEXES'])) { foreach($changes['INDEXES'] as $table_name => $table) { $this->database->debug("$table_name:"); if(isset($table['AddedIndexes'])) { foreach($table['AddedIndexes'] as $index_name => $index) { $this->database->debug("\tAdded index '".$index_name."' of table '$table_name'"); } } if(isset($table['RemovedIndexes'])) { foreach($table['RemovedIndexes'] as $index_name => $index) { $this->database->debug("\tRemoved index '".$index_name."' of table '$table_name'"); } } if(isset($table['ChangedIndexes'])) { foreach($table['ChangedIndexes'] as $index_name => $index) { if(isset($index['name'])) { $this->database->debug( "\tRenamed index '".$index_name."' to '".$index['name']."' on table '$table_name'"); } if(isset($index['ChangedUnique'])) { $this->database->debug( "\tChanged index '".$index_name."' unique to '". isset($index['unique'])."' on table '$table_name'"); } if(isset($index['ChangedFields'])) { $this->database->debug("\tChanged index '".$index_name."' on table '$table_name'"); } } } } } return(MDB_OK); }
// }}} // {{{ _dumpDatabaseContents()
/** * Parse a database schema definition file and dump the respective structure * and contents. * * @param string $schema_file path of the database schema file. * @param mixed $setup_arguments an associative array that takes pairs of tag names and values * that define the setup arguments that are passed to the * MDB_Manager::connect function. * @param array $dump_arguments an associative array that takes pairs of tag names and values * that define dump options as defined for the MDB_Manager::DumpDatabase * function. * @param array $variables an associative array that the defines the text string values * that are meant to be used to replace the variables that are used in the * schema description as defined for the * MDB_Manager::parseDatabaseDefinitionFile function. * @return mixed MDB_OK on success, or a MDB error object * @access private */ function _dumpDatabaseContents($schema_file, $setup_arguments, $dump_arguments, $variables) { $database_definition = $this->parseDatabaseDefinitionFile($schema_file, $variables, $this->options['fail_on_invalid_names']); if(MDB::isError($database_definition)) { return($database_definition); } $this->database_definition = $database_definition; $result = $this->connect($setup_arguments); if(MDB::isError($result)) { return($result); } return($this->dumpDatabase($dump_arguments)); }
// }}} // {{{ getDefinitionFromDatabase()
/** * Attempt to reverse engineer a schema structure from an existing MDB * This method can be used if no xml schema file exists yet. * The resulting xml schema file may need some manual adjustments. * * @return mixed MDB_OK or array with all ambiguities on success, or a MDB error object * @access public */ function getDefinitionFromDatabase() { $database = $this->database->database_name; if(strlen($database) == 0) { return('it was not specified a valid database name'); } $this->database_definition = array( 'name' => $database, 'create' => 1, 'TABLES' => array() ); $tables = $this->database->listTables(); if(MDB::isError($tables)) { return($tables); } for($table = 0; $table < count($tables); $table++) { $table_name = $tables[$table]; $fields = $this->database->listTableFields($table_name); if(MDB::isError($fields)) { return($fields); } $this->database_definition['TABLES'][$table_name] = array('FIELDS' => array()); for($field = 0; $field < count($fields); $field++) { $field_name = $fields[$field]; $definition = $this->database->getTableFieldDefinition($table_name, $field_name); if(MDB::isError($definition)) { return($definition); } $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name] = $definition[0][0]; $field_choices = count($definition[0]); if($field_choices > 1) { $warning = "There are $field_choices type choices in the table $table_name field $field_name (#1 is the default): "; $field_choice_cnt = 1; $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name]['CHOICES'] = array(); foreach($definition[0] as $field_choice) { $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name]['CHOICES'][] = $field_choice; $warning .= 'choice #'.($field_choice_cnt).': '.serialize($field_choice); $field_choice_cnt++; } $this->warnings[] = $warning; } if(isset($definition[1])) { $sequence = $definition[1]['definition']; $sequence_name = $definition[1]['name']; $this->database->debug('Implicitly defining sequence: '.$sequence_name); if(!isset($this->database_definition['SEQUENCES'])) { $this->database_definition['SEQUENCES'] = array(); } $this->database_definition['SEQUENCES'][$sequence_name] = $sequence; } if(isset($definition[2])) { $index = $definition[2]['definition']; $index_name = $definition[2]['name']; $this->database->debug('Implicitly defining index: '.$index_name); if(!isset($this->database_definition['TABLES'][$table_name]['INDEXES'])) { $this->database_definition['TABLES'][$table_name]['INDEXES'] = array(); } $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name] = $index; } } $indexes = $this->database->listTableIndexes($table_name); if(MDB::isError($indexes)) { return($indexes); } if(is_array($indexes) && count($indexes) > 0 && !isset($this->database_definition['TABLES'][$table_name]['INDEXES'])) { $this->database_definition['TABLES'][$table_name]['INDEXES'] = array(); } for($index = 0, $index_cnt = count($indexes); $index < $index_cnt; $index++) { $index_name = $indexes[$index]; $definition = $this->database->getTableIndexDefinition($table_name, $index_name); if(MDB::isError($definition)) { return($definition); } $this->database_definition['TABLES'][$table_name]['INDEXES'][$index_name] = $definition; } // ensure that all fields that have an index on them are set to not null if(isset($this->database_definition['TABLES'][$table_name]['INDEXES']) && is_array($this->database_definition['TABLES'][$table_name]['INDEXES']) && count($this->database_definition['TABLES'][$table_name]['INDEXES']) > 0 ) { foreach($this->database_definition['TABLES'][$table_name]['INDEXES'] as $index_check_null) { foreach($index_check_null['FIELDS'] as $field_name_check_null => $field_check_null) { $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_name_check_null]['notnull'] = 1; } } } // ensure that all fields that are set to not null also have a default value if(is_array($this->database_definition['TABLES'][$table_name]['FIELDS']) && count($this->database_definition['TABLES'][$table_name]['FIELDS']) > 0 ) { foreach($this->database_definition['TABLES'][$table_name]['FIELDS'] as $field_set_default_name => $field_set_default) { if(isset($field_set_default['notnull']) && $field_set_default['notnull'] && !isset($field_set_default['default']) ) { if(isset($this->default_values[$field_set_default['type']])) { $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['default'] = $this->default_values[$field_set_default['type']]; } else { $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['default'] = 0; } } if(isset($field_set_default['CHOICES']) && is_array($field_set_default['CHOICES'])) { foreach($field_set_default['CHOICES'] as $field_choices_set_default_name => $field_choices_set_default) { if(isset($field_choices_set_default['notnull']) && $field_choices_set_default['notnull'] && !isset($field_choices_set_default['default']) ) { if(isset($this->default_values[$field_choices_set_default['type']])) { $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['CHOICES'] [$field_choices_set_default_name]['default'] = $this->default_values[$field_choices_set_default['type']]; } else { $this->database_definition['TABLES'][$table_name]['FIELDS'][$field_set_default_name]['CHOICES'] [$field_choices_set_default_name]['default'] = 0; } } } } } } } $sequences = $this->database->listSequences(); if(MDB::isError($sequences)) { return($sequences); } if(is_array($sequences) && count($sequences) > 0 && !isset($this->database_definition['SEQUENCES'])) { $this->database_definition['SEQUENCES'] = array(); } for($sequence = 0; $sequence < count($sequences); $sequence++) { $sequence_name = $sequences[$sequence]; $definition = $this->database->getSequenceDefinition($sequence_name); if(MDB::isError($definition)) { return($definition); } $this->database_definition['SEQUENCES'][$sequence_name] = $definition; } return(MDB_OK); }
// }}} // {{{ dumpDatabase()
/** * Dump a previously parsed database structure in the Metabase schema * XML based format suitable for the Metabase parser. This function * may optionally dump the database definition with initialization * commands that specify the data that is currently present in the tables. * * @param array $arguments an associative array that takes pairs of tag * names and values that define dump options. * array ( * 'Definition' => Boolean * TRUE : dump currently parsed definition * default: dump currently connected database * 'Output_Mode' => String * 'file' : dump into a file * default: dump using a function * 'Output' => String * depending on the 'Output_Mode' * name of the file * name of the function * 'EndOfLine' => String * end of line delimiter that should be used * default: "\n" * ); * @param integer $dump constant that determines what data to dump * MDB_MANAGER_DUMP_ALL : the entire db * MDB_MANAGER_DUMP_STRUCTURE : only the structure of the db * MDB_MANAGER_DUMP_CONTENT : only the content of the db * @return mixed MDB_OK on success, or a MDB error object * @access public */ function dumpDatabase($arguments, $dump = MDB_MANAGER_DUMP_ALL) { if(isset($arguments['Definition']) && $arguments['Definition']) { $dump_definition = TRUE; } else { if(!$this->database) { return($this->raiseError(MDB_ERROR_NODBSELECTED, NULL, NULL, 'please connect to a RDBMS first')); } $error = $this->getDefinitionFromDatabase(); if(MDB::isError($error)) { return($error); } $dump_definition = FALSE; } if(isset($arguments['Output'])) { if(isset($arguments['Output_Mode']) && $arguments['Output_Mode'] == 'file') { $fp = fopen($arguments['Output'], 'w'); $output = FALSE; } elseif(function_exists($arguments['Output'])) { $output = $arguments['Output']; } else { return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'no valid output function specified')); } } else { return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'no output method specified')); } if(isset($arguments['EndOfLine'])) { $eol = $arguments['EndOfLine']; } else { $eol = "\n"; } $sequences = array(); if(isset($this->database_definition['SEQUENCES']) && is_array($this->database_definition['SEQUENCES']) ) { foreach($this->database_definition['SEQUENCES'] as $sequence_name => $sequence) { if(isset($sequence['on'])) { $table = $sequence['on']['table']; } else { $table = ''; } $sequences[$table][] = $sequence_name; } } $previous_database_name = (strcmp($this->database_definition['name'], '') ? $this->database->setDatabase($this->database_definition['name']) : ''); $buffer = ('<?xml version="1.0" encoding="ISO-8859-1" ?>'.$eol); $buffer .= ("<database>$eol$eol <name>".$this->database_definition['name']."</name>$eol <create>".$this->database_definition['create']."</create>$eol"); if($output) { $output($buffer); } else { fwrite($fp, $buffer); } $buffer = ''; if(isset($this->database_definition['TABLES']) && is_array($this->database_definition['TABLES'])) { foreach($this->database_definition['TABLES'] as $table_name => $table) { $buffer = ("$eol <table>$eol$eol <name>$table_name</name>$eol"); if($dump == MDB_MANAGER_DUMP_ALL || $dump == MDB_MANAGER_DUMP_STRUCTURE) { $buffer .= ("$eol <declaration>$eol"); if(isset($table['FIELDS']) && is_array($table['FIELDS'])) { foreach($table['FIELDS'] as $field_name => $field) { if(!isset($field['type'])) { return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'it was not specified the type of the field "'.$field_name.'" of the table "'.$table_name)); } $buffer .=("$eol <field>$eol <name>$field_name</name>$eol <type>".$field['type']."</type>$eol"); if(in_array($field_name, array_keys($this->invalid_names))) { $this->warnings[] = "invalid field name: $field_name. You will need to set the class var \$fail_on_invalid_names to FALSE or change the field name."; } switch($field['type']) { case 'integer': if(isset($field['unsigned'])) { $buffer .=(" <unsigned>1</unsigned>$eol"); } break; case 'text': case 'clob': case 'blob': if(isset($field['length'])) { $buffer .=(' <length>'.$field['length']."</length>$eol"); } break; case 'boolean': case 'date': case 'timestamp': case 'time': case 'float': case 'decimal': break; default: return('type "'.$field['type'].'" is not yet supported'); } if(isset($field['notnull'])) { $buffer .=(" <notnull>1</notnull>$eol"); } if(isset($field['default'])) { $buffer .=(' <default>'.$this->_escapeSpecialCharacters($field['default'])."</default>$eol"); } $buffer .=(" </field>$eol"); } } if(isset($table['INDEXES']) && is_array($table['INDEXES'])) { foreach($table['INDEXES'] as $index_name => $index) { $buffer .=("$eol <index>$eol <name>$index_name</name>$eol"); if(isset($index['unique'])) { $buffer .=(" <unique>1</unique>$eol"); } foreach($index['FIELDS'] as $field_name => $field) { $buffer .=(" <field>$eol <name>$field_name</name>$eol"); if(is_array($field) && isset($field['sorting'])) { $buffer .=(' <sorting>'.$field['sorting']."</sorting>$eol"); } $buffer .=(" </field>$eol"); } $buffer .=(" </index>$eol"); } } $buffer .= ("$eol </declaration>$eol"); } if($output) { $output($buffer); } else { fwrite($fp, $buffer); } $buffer = ''; if($dump == MDB_MANAGER_DUMP_ALL || $dump == MDB_MANAGER_DUMP_CONTENT) { if($dump_definition) { if(isset($table['initialization']) && is_array($table['initialization'])) { $buffer = ("$eol <initialization>$eol"); foreach($table['initialization'] as $instruction_name => $instruction) { switch($instruction['type']) { case 'insert': $buffer .= ("$eol <insert>$eol"); foreach($instruction['FIELDS'] as $field_name => $field) { $buffer .= ("$eol <field>$eol <name>$field_name</name>$eol <value>".$this->_escapeSpecialCharacters($field)."</value>$eol </field>$eol"); } $buffer .= ("$eol </insert>$eol"); break; } } $buffer .= ("$eol </initialization>$eol"); } } else { $types = array(); foreach($table['FIELDS'] as $field) { $types[] = $field['type']; } $query = 'SELECT '.implode(',',array_keys($table['FIELDS']))." FROM $table_name"; $result = $this->database->queryAll($query, $types, MDB_FETCHMODE_ASSOC); if(MDB::isError($result)) { return($result); } $rows = count($result); if($rows > 0) { $buffer = ("$eol <initialization>$eol"); if($output) { $output($buffer); } else { fwrite($fp, $buffer); } for($row = 0; $row < $rows; $row++) { $buffer = ("$eol <insert>$eol"); $values = $result[$row]; if(!is_array($values)) { break; } else { foreach($values as $field_name => $field) { $buffer .= ("$eol <field>$eol <name>$field_name</name>$eol <value>"); $buffer .= $this->_escapeSpecialCharacters($values[$field_name]); $buffer .= ("</value>$eol </field>$eol"); } } $buffer .= ("$eol </insert>$eol"); if($output) { $output($buffer); } else { fwrite($fp, $buffer); } $buffer = ''; } $buffer = ("$eol </initialization>$eol"); if($output) { $output($buffer); } else { fwrite($fp, $buffer); } $buffer = ''; } } } $buffer .= ("$eol </table>$eol"); if($output) { $output($buffer); } else { fwrite($fp, $buffer); } if(isset($sequences[$table_name])) { for($sequence = 0, $j = count($sequences[$table_name]); $sequence < $j; $sequence++) { $result = $this->_dumpSequence($sequences[$table_name][$sequence], $eol, $dump); if(MDB::isError($result)) { return($result); } if($output) { $output($result); } else { fwrite($fp, $result); } } } } } if(isset($sequences[''])) { for($sequence = 0; $sequence < count($sequences['']); $sequence++) { $result = $this->_dumpSequence($sequences[''][$sequence], $eol, $dump); if(MDB::isError($result)) { return($result); } if($output) { $output($result); } else { fwrite($fp, $result); } } } $buffer = ("$eol</database>$eol"); if($output) { $output($buffer); } else { fwrite($fp, $buffer); fclose($fp); } if(strcmp($previous_database_name, '')) { $this->database->setDatabase($previous_database_name); } return(MDB_OK); }
// }}} // {{{ updateDatabase()
/** * Compare the correspondent files of two versions of a database schema * definition: the previously installed and the one that defines the schema * that is meant to update the database. * If the specified previous definition file does not exist, this function * will create the database from the definition specified in the current * schema file. * If both files exist, the function assumes that the database was previously * installed based on the previous schema file and will update it by just * applying the changes. * If this function succeeds, the contents of the current schema file are * copied to replace the previous schema file contents. Any subsequent schema * changes should only be done on the file specified by the $current_schema_file * to let this function make a consistent evaluation of the exact changes that * need to be applied. * * @param string $current_schema_file name of the updated database schema * definition file. * @param string $previous_schema_file name the previously installed database * schema definition file. * @param array $variables an associative array that is passed to the argument * of the same name to the parseDatabaseDefinitionFile function. (there third * param) * @return mixed MDB_OK on success, or a MDB error object * @access public */ function updateDatabase($current_schema_file, $previous_schema_file = FALSE, $variables = array()) { $database_definition = $this->parseDatabaseDefinitionFile($current_schema_file, $variables, $this->options['fail_on_invalid_names']); if(MDB::isError($database_definition)) { return($database_definition); } $this->database_definition = $database_definition; $copy = 0; /* $this->expectError(MDB_ERROR_UNSUPPORTED); $databases = $this->database->listDatabases(); $this->popExpect(); if((MDB::isError($databases) || (is_array($databases) && in_array($this->database_definition['name'], $databases))) && $previous_schema_file && file_exists($previous_schema_file)) { */ if($previous_schema_file && file_exists($previous_schema_file)) { $previous_definition = $this->parseDatabaseDefinitionFile($previous_schema_file, $variables, 0); if(MDB::isError($previous_definition)) { return($previous_definition); } $changes = $this->_compareDefinitions($previous_definition); if(MDB::isError($changes)) { return($changes); } if(isset($changes) && is_array($changes)) { $result = $this->_alterDatabase($previous_definition, $changes); if(MDB::isError($result)) { return($result); } $copy = 1; if($this->options['debug']) { $result = $this->_debugDatabaseChanges($changes); if(MDB::isError($result)) { return($result); } } } } else { $result = $this->_createDatabase(); if(MDB::isError($result)) { return($result); } $copy = 1; } if($copy && $previous_schema_file && !copy($current_schema_file, $previous_schema_file)) { return($this->raiseError(MDB_ERROR_MANAGER, NULL, NULL, 'Could not copy the new database definition file to the current file')); } return(MDB_OK); }
// }}} } ?>
|