/includes/pear/MDB2/Schema.php
PHP | 2767 lines | 1775 code | 283 blank | 709 comment | 494 complexity | bc5c139674e5780f76186b77308f126a MD5 | raw file
Possible License(s): GPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * PHP version 4, 5
- *
- * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
- * Stig. S. Bakken, Lukas Smith, Igor Feghali
- * All rights reserved.
- *
- * MDB2_Schema enables users to maintain RDBMS independant schema files
- * in XML that can be used to manipulate both data and database schemas
- * 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, Igor Feghali 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@pooteeweet.org>
- * Author: Igor Feghali <ifeghali@php.net>
- *
- * @category Database
- * @package MDB2_Schema
- * @author Lukas Smith <smith@pooteeweet.org>
- * @author Igor Feghali <ifeghali@php.net>
- * @license BSD http://www.opensource.org/licenses/bsd-license.php
- * @version CVS: $Id: Schema.php,v 1.132 2009/02/22 21:43:22 ifeghali Exp $
- * @link http://pear.php.net/packages/MDB2_Schema
- */
- require_once 'MDB2.php';
- define('MDB2_SCHEMA_DUMP_ALL', 0);
- define('MDB2_SCHEMA_DUMP_STRUCTURE', 1);
- define('MDB2_SCHEMA_DUMP_CONTENT', 2);
- /**
- * If you add an error code here, make sure you also add a textual
- * version of it in MDB2_Schema::errorMessage().
- */
- define('MDB2_SCHEMA_ERROR', -1);
- define('MDB2_SCHEMA_ERROR_PARSE', -2);
- define('MDB2_SCHEMA_ERROR_VALIDATE', -3);
- define('MDB2_SCHEMA_ERROR_UNSUPPORTED', -4); // Driver does not support this function
- define('MDB2_SCHEMA_ERROR_INVALID', -5); // Invalid attribute value
- define('MDB2_SCHEMA_ERROR_WRITER', -6);
- /**
- * The database manager is a class that provides a set of database
- * management services like installing, altering and dumping the data
- * structures of databases.
- *
- * @category Database
- * @package MDB2_Schema
- * @author Lukas Smith <smith@pooteeweet.org>
- * @license BSD http://www.opensource.org/licenses/bsd-license.php
- * @link http://pear.php.net/packages/MDB2_Schema
- */
- class MDB2_Schema extends PEAR
- {
- // {{{ properties
- var $db;
- var $warnings = array();
- var $options = array(
- 'fail_on_invalid_names' => true,
- 'dtd_file' => false,
- 'valid_types' => array(),
- 'force_defaults' => true,
- 'parser' => 'MDB2_Schema_Parser',
- 'writer' => 'MDB2_Schema_Writer',
- 'validate' => 'MDB2_Schema_Validate',
- 'drop_missing_tables' => false
- );
- // }}}
- // {{{ apiVersion()
- /**
- * Return the MDB2 API version
- *
- * @return string the MDB2 API version number
- * @access public
- */
- function apiVersion()
- {
- return '0.4.3';
- }
- // }}}
- // {{{ arrayMergeClobber()
- /**
- * Clobbers two arrays together
- *
- * @param array $a1 array that should be clobbered
- * @param array $a2 array that should be clobbered
- *
- * @return array|false array on success and false on error
- *
- * @access public
- * @author kc@hireability.com
- */
- function arrayMergeClobber($a1, $a2)
- {
- if (!is_array($a1) || !is_array($a2)) {
- return false;
- }
- foreach ($a2 as $key => $val) {
- if (is_array($val) && array_key_exists($key, $a1) && is_array($a1[$key])) {
- $a1[$key] = MDB2_Schema::arrayMergeClobber($a1[$key], $val);
- } else {
- $a1[$key] = $val;
- }
- }
- return $a1;
- }
- // }}}
- // {{{ resetWarnings()
- /**
- * reset the warning array
- *
- * @access public
- * @return void
- */
- 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()
- /**
- * Sets the option for the db class
- *
- * @param string $option option name
- * @param mixed $value value for the option
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- */
- function setOption($option, $value)
- {
- if (isset($this->options[$option])) {
- if (is_null($value)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
- 'may not set an option to value null');
- }
- $this->options[$option] = $value;
- return MDB2_OK;
- }
- return $this->raiseError(MDB2_SCHEMA_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(MDB2_SCHEMA_ERROR_UNSUPPORTED,
- null, null, "unknown option $option");
- }
- // }}}
- // {{{ factory()
- /**
- * Create a new MDB2 object for the specified database type
- * type
- *
- * @param string|array|MDB2_Driver_Common &$db 'data source name', see the
- * MDB2::parseDSN method for a description of the dsn format.
- * Can also be specified as an array of the
- * format returned by @see MDB2::parseDSN.
- * Finally you can also pass an existing db object to be used.
- * @param array $options An associative array of option names and their values.
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- * @see MDB2::parseDSN
- */
- function &factory(&$db, $options = array())
- {
- $obj =& new MDB2_Schema();
- $result = $obj->connect($db, $options);
- if (PEAR::isError($result)) {
- return $result;
- }
- return $obj;
- }
- // }}}
- // {{{ connect()
- /**
- * Create a new MDB2 connection object and connect to the specified
- * database
- *
- * @param string|array|MDB2_Driver_Common &$db 'data source name', see the
- * MDB2::parseDSN method for a description of the dsn format.
- * Can also be specified as an array of the
- * format returned by MDB2::parseDSN.
- * Finally you can also pass an existing db object to be used.
- * @param array $options An associative array of option names and their values.
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- * @see MDB2::parseDSN
- */
- function connect(&$db, $options = array())
- {
- $db_options = array();
- if (is_array($options)) {
- foreach ($options as $option => $value) {
- if (array_key_exists($option, $this->options)) {
- $result = $this->setOption($option, $value);
- if (PEAR::isError($result)) {
- return $result;
- }
- } else {
- $db_options[$option] = $value;
- }
- }
- }
- $this->disconnect();
- if (!MDB2::isConnection($db)) {
- $db =& MDB2::factory($db, $db_options);
- }
- if (PEAR::isError($db)) {
- return $db;
- }
- $this->db =& $db;
- $this->db->loadModule('Datatype');
- $this->db->loadModule('Manager');
- $this->db->loadModule('Reverse');
- $this->db->loadModule('Function');
- if (empty($this->options['valid_types'])) {
- $this->options['valid_types'] = $this->db->datatype->getValidTypes();
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ disconnect()
- /**
- * Log out and disconnect from the database.
- *
- * @access public
- * @return void
- */
- function disconnect()
- {
- if (MDB2::isConnection($this->db)) {
- $this->db->disconnect();
- unset($this->db);
- }
- }
- // }}}
- // {{{ parseDatabaseDefinition()
- /**
- * Parse a database definition from a file or an array
- *
- * @param string|array $schema the database schema array or file name
- * @param bool $skip_unreadable if non readable files should be skipped
- * @param array $variables 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 make function fail on invalid names
- * @param array $structure database structure definition
- *
- * @access public
- * @return array
- */
- function parseDatabaseDefinition($schema, $skip_unreadable = false, $variables = array(),
- $fail_on_invalid_names = true, $structure = false)
- {
- $database_definition = false;
- if (is_string($schema)) {
- // if $schema is not readable then we just skip it
- // and simply copy the $current_schema file to that file name
- if (is_readable($schema)) {
- $database_definition = $this->parseDatabaseDefinitionFile($schema, $variables, $fail_on_invalid_names, $structure);
- }
- } elseif (is_array($schema)) {
- $database_definition = $schema;
- }
- if (!$database_definition && !$skip_unreadable) {
- $database_definition = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
- 'invalid data type of schema or unreadable data source');
- }
- return $database_definition;
- }
- // }}}
- // {{{ parseDatabaseDefinitionFile()
- /**
- * Parse a database definition file by creating a schema format
- * parser object and passing the file contents as parser input data stream.
- *
- * @param string $input_file the database schema file.
- * @param array $variables 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 make function fail on invalid names
- * @param array $structure database structure definition
- *
- * @access public
- * @return array
- */
- function parseDatabaseDefinitionFile($input_file, $variables = array(),
- $fail_on_invalid_names = true, $structure = false)
- {
- $dtd_file = $this->options['dtd_file'];
- if ($dtd_file) {
- include_once 'XML/DTD/XmlValidator.php';
- $dtd =& new XML_DTD_XmlValidator;
- if (!$dtd->isValid($dtd_file, $input_file)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_PARSE, null, null, $dtd->getMessage());
- }
- }
- $class_name = $this->options['parser'];
- $result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
- if (PEAR::isError($result)) {
- return $result;
- }
- $parser =& new $class_name($variables, $fail_on_invalid_names, $structure, $this->options['valid_types'], $this->options['force_defaults']);
- $result = $parser->setInputFile($input_file);
- if (PEAR::isError($result)) {
- return $result;
- }
- $result = $parser->parse();
- if (PEAR::isError($result)) {
- return $result;
- }
- if (PEAR::isError($parser->error)) {
- return $parser->error;
- }
- return $parser->database_definition;
- }
- // }}}
- // {{{ getDefinitionFromDatabase()
- /**
- * Attempt to reverse engineer a schema structure from an existing MDB2
- * This method can be used if no xml schema file exists yet.
- * The resulting xml schema file may need some manual adjustments.
- *
- * @return array|MDB2_Error array with definition or error object
- * @access public
- */
- function getDefinitionFromDatabase()
- {
- $database = $this->db->database_name;
- if (empty($database)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
- 'it was not specified a valid database name');
- }
- $class_name = $this->options['validate'];
- $result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
- if (PEAR::isError($result)) {
- return $result;
- }
- $val =& new $class_name($this->options['fail_on_invalid_names'], $this->options['valid_types'], $this->options['force_defaults']);
- $database_definition = array(
- 'name' => $database,
- 'create' => true,
- 'overwrite' => false,
- 'charset' => 'utf8',
- 'description' => '',
- 'comments' => '',
- 'tables' => array(),
- 'sequences' => array(),
- );
- $tables = $this->db->manager->listTables();
- if (PEAR::isError($tables)) {
- return $tables;
- }
- foreach ($tables as $table_name) {
- $fields = $this->db->manager->listTableFields($table_name);
- if (PEAR::isError($fields)) {
- return $fields;
- }
- $database_definition['tables'][$table_name] = array(
- 'was' => '',
- 'description' => '',
- 'comments' => '',
- 'fields' => array(),
- 'indexes' => array(),
- 'constraints' => array(),
- 'initialization' => array()
- );
- $table_definition =& $database_definition['tables'][$table_name];
- foreach ($fields as $field_name) {
- $definition = $this->db->reverse->getTableFieldDefinition($table_name, $field_name);
- if (PEAR::isError($definition)) {
- return $definition;
- }
- if (!empty($definition[0]['autoincrement'])) {
- $definition[0]['default'] = '0';
- }
- $table_definition['fields'][$field_name] = $definition[0];
- $field_choices = count($definition);
- 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;
- $table_definition['fields'][$field_name]['choices'] = array();
- foreach ($definition as $field_choice) {
- $table_definition['fields'][$field_name]['choices'][] = $field_choice;
- $warning .= 'choice #'.($field_choice_cnt).': '.serialize($field_choice);
- $field_choice_cnt++;
- }
- $this->warnings[] = $warning;
- }
- /**
- * The first parameter is used to verify if there are duplicated
- * fields which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateField(array(), $table_definition['fields'][$field_name], $field_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- $keys = array();
- $indexes = $this->db->manager->listTableIndexes($table_name);
- if (PEAR::isError($indexes)) {
- return $indexes;
- }
- if (is_array($indexes)) {
- foreach ($indexes as $index_name) {
- $this->db->expectError(MDB2_ERROR_NOT_FOUND);
- $definition = $this->db->reverse->getTableIndexDefinition($table_name, $index_name);
- $this->db->popExpect();
- if (PEAR::isError($definition)) {
- if (PEAR::isError($definition, MDB2_ERROR_NOT_FOUND)) {
- continue;
- }
- return $definition;
- }
- $keys[$index_name] = $definition;
- }
- }
- $constraints = $this->db->manager->listTableConstraints($table_name);
- if (PEAR::isError($constraints)) {
- return $constraints;
- }
- if (is_array($constraints)) {
- foreach ($constraints as $constraint_name) {
- $this->db->expectError(MDB2_ERROR_NOT_FOUND);
- $definition = $this->db->reverse->getTableConstraintDefinition($table_name, $constraint_name);
- $this->db->popExpect();
- if (PEAR::isError($definition)) {
- if (PEAR::isError($definition, MDB2_ERROR_NOT_FOUND)) {
- continue;
- }
- return $definition;
- }
- $keys[$constraint_name] = $definition;
- }
- }
- foreach ($keys as $key_name => $definition) {
- if (array_key_exists('foreign', $definition)
- && $definition['foreign']
- ) {
- /**
- * The first parameter is used to verify if there are duplicated
- * foreign keys which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateConstraint(array(), $definition, $key_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- foreach ($definition['fields'] as $field_name => $field) {
- /**
- * The first parameter is used to verify if there are duplicated
- * referencing fields which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateConstraintField(array(), $field_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- $definition['fields'][$field_name] = '';
- }
- foreach ($definition['references']['fields'] as $field_name => $field) {
- /**
- * The first parameter is used to verify if there are duplicated
- * referenced fields which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateConstraintReferencedField(array(), $field_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- $definition['references']['fields'][$field_name] = '';
- }
- $table_definition['constraints'][$key_name] = $definition;
- } else {
- /**
- * The first parameter is used to verify if there are duplicated
- * indices which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateIndex(array(), $definition, $key_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- foreach ($definition['fields'] as $field_name => $field) {
- /**
- * The first parameter is used to verify if there are duplicated
- * index fields which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateIndexField(array(), $field, $field_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- $definition['fields'][$field_name] = $field;
- }
- $table_definition['indexes'][$key_name] = $definition;
- }
- }
- /**
- * The first parameter is used to verify if there are duplicated
- * tables which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateTable(array(), $table_definition, $table_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- $sequences = $this->db->manager->listSequences();
- if (PEAR::isError($sequences)) {
- return $sequences;
- }
- if (is_array($sequences)) {
- foreach ($sequences as $sequence_name) {
- $definition = $this->db->reverse->getSequenceDefinition($sequence_name);
- if (PEAR::isError($definition)) {
- return $definition;
- }
- if (isset($database_definition['tables'][$sequence_name])
- && isset($database_definition['tables'][$sequence_name]['indexes'])
- ) {
- foreach ($database_definition['tables'][$sequence_name]['indexes'] as $index) {
- if (isset($index['primary']) && $index['primary']
- && count($index['fields'] == 1)
- ) {
- $definition['on'] = array(
- 'table' => $sequence_name,
- 'field' => key($index['fields']),
- );
- break;
- }
- }
- }
- /**
- * The first parameter is used to verify if there are duplicated
- * sequences which we can guarantee that won't happen when reverse engineering
- */
- $result = $val->validateSequence(array(), $definition, $sequence_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- $database_definition['sequences'][$sequence_name] = $definition;
- }
- }
- $result = $val->validateDatabase($database_definition);
- if (PEAR::isError($result)) {
- return $result;
- }
- return $database_definition;
- }
- // }}}
- // {{{ createTableIndexes()
- /**
- * A method to create indexes for an existing table
- *
- * @param string $table_name Name of the table
- * @param array $indexes An array of indexes to be created
- * @param boolean $overwrite If the table/index should be overwritten if it already exists
- *
- * @return mixed MDB2_Error if there is an error creating an index, MDB2_OK otherwise
- * @access public
- */
- function createTableIndexes($table_name, $indexes, $overwrite = false)
- {
- if (!$this->db->supports('indexes')) {
- $this->db->debug('Indexes are not supported', __FUNCTION__);
- return MDB2_OK;
- }
- $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
- foreach ($indexes as $index_name => $index) {
- // Does the index already exist, and if so, should it be overwritten?
- $create_index = true;
- $this->db->expectError($errorcodes);
- if (!empty($index['primary']) || !empty($index['unique'])) {
- $current_indexes = $this->db->manager->listTableConstraints($table_name);
- } else {
- $current_indexes = $this->db->manager->listTableIndexes($table_name);
- }
- $this->db->popExpect();
- if (PEAR::isError($current_indexes)) {
- if (!MDB2::isError($current_indexes, $errorcodes)) {
- return $current_indexes;
- }
- } elseif (is_array($current_indexes) && in_array($index_name, $current_indexes)) {
- if (!$overwrite) {
- $this->db->debug('Index already exists: '.$index_name, __FUNCTION__);
- $create_index = false;
- } else {
- $this->db->debug('Preparing to overwrite index: '.$index_name, __FUNCTION__);
- $this->db->expectError(MDB2_ERROR_NOT_FOUND);
- if (!empty($index['primary']) || !empty($index['unique'])) {
- $result = $this->db->manager->dropConstraint($table_name, $index_name);
- } else {
- $result = $this->db->manager->dropIndex($table_name, $index_name);
- }
- $this->db->popExpect();
- if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_NOT_FOUND)) {
- return $result;
- }
- }
- }
- // Check if primary is being used and if it's supported
- if (!empty($index['primary']) && !$this->db->supports('primary_key')) {
- // Primary not supported so we fallback to UNIQUE and making the field NOT NULL
- $index['unique'] = true;
- $changes = array();
- foreach ($index['fields'] as $field => $empty) {
- $field_info = $this->db->reverse->getTableFieldDefinition($table_name, $field);
- if (PEAR::isError($field_info)) {
- return $field_info;
- }
- if (!$field_info[0]['notnull']) {
- $changes['change'][$field] = $field_info[0];
- $changes['change'][$field]['notnull'] = true;
- }
- }
- if (!empty($changes)) {
- $this->db->manager->alterTable($table_name, $changes, false);
- }
- }
- // Should the index be created?
- if ($create_index) {
- if (!empty($index['primary']) || !empty($index['unique'])) {
- $result = $this->db->manager->createConstraint($table_name, $index_name, $index);
- } else {
- $result = $this->db->manager->createIndex($table_name, $index_name, $index);
- }
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ createTableConstraints()
- /**
- * A method to create foreign keys for an existing table
- *
- * @param string $table_name Name of the table
- * @param array $constraints An array of foreign keys to be created
- * @param boolean $overwrite If the foreign key should be overwritten if it already exists
- *
- * @return mixed MDB2_Error if there is an error creating a foreign key, MDB2_OK otherwise
- * @access public
- */
- function createTableConstraints($table_name, $constraints, $overwrite = false)
- {
- if (!$this->db->supports('indexes')) {
- $this->db->debug('Indexes are not supported', __FUNCTION__);
- return MDB2_OK;
- }
- $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
- foreach ($constraints as $constraint_name => $constraint) {
- // Does the foreign key already exist, and if so, should it be overwritten?
- $create_constraint = true;
- $this->db->expectError($errorcodes);
- $current_constraints = $this->db->manager->listTableConstraints($table_name);
- $this->db->popExpect();
- if (PEAR::isError($current_constraints)) {
- if (!MDB2::isError($current_constraints, $errorcodes)) {
- return $current_constraints;
- }
- } elseif (is_array($current_constraints) && in_array($constraint_name, $current_constraints)) {
- if (!$overwrite) {
- $this->db->debug('Foreign key already exists: '.$constraint_name, __FUNCTION__);
- $create_constraint = false;
- } else {
- $this->db->debug('Preparing to overwrite foreign key: '.$constraint_name, __FUNCTION__);
- $result = $this->db->manager->dropConstraint($table_name, $constraint_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- }
- // Should the foreign key be created?
- if ($create_constraint) {
- $result = $this->db->manager->createConstraint($table_name, $constraint_name, $constraint);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ 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 contains the
- * structure and optional data of the table
- * @param bool $overwrite if the table/index should be overwritten if it already exists
- * @param array $options an array of options to be passed to the database specific driver
- * version of MDB2_Driver_Manager_Common::createTable().
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- */
- function createTable($table_name, $table, $overwrite = false, $options = array())
- {
- $create = true;
- $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
- $this->db->expectError($errorcodes);
- $tables = $this->db->manager->listTables();
- $this->db->popExpect();
- if (PEAR::isError($tables)) {
- if (!MDB2::isError($tables, $errorcodes)) {
- return $tables;
- }
- } elseif (is_array($tables) && in_array($table_name, $tables)) {
- if (!$overwrite) {
- $create = false;
- $this->db->debug('Table already exists: '.$table_name, __FUNCTION__);
- } else {
- $result = $this->db->manager->dropTable($table_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- $this->db->debug('Overwritting table: '.$table_name, __FUNCTION__);
- }
- }
- if ($create) {
- $result = $this->db->manager->createTable($table_name, $table['fields'], $options);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- if (!empty($table['initialization']) && is_array($table['initialization'])) {
- $result = $this->initializeTable($table_name, $table);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- if (!empty($table['indexes']) && is_array($table['indexes'])) {
- $result = $this->createTableIndexes($table_name, $table['indexes'], $overwrite);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- if (!empty($table['constraints']) && is_array($table['constraints'])) {
- $result = $this->createTableConstraints($table_name, $table['constraints'], $overwrite);
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ initializeTable()
- /**
- * Inititialize the table with data
- *
- * @param string $table_name name of the table
- * @param array $table multi dimensional array that contains the
- * structure and optional data of the table
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- */
- function initializeTable($table_name, $table)
- {
- $query_insertselect = 'INSERT INTO %s (%s) (SELECT %s FROM %s %s)';
- $query_insert = 'INSERT INTO %s (%s) VALUES (%s)';
- $query_update = 'UPDATE %s SET %s %s';
- $query_delete = 'DELETE FROM %s %s';
- $table_name = $this->db->quoteIdentifier($table_name, true);
- $result = MDB2_OK;
- $support_transactions = $this->db->supports('transactions');
- foreach ($table['initialization'] as $instruction) {
- $query = '';
- switch ($instruction['type']) {
- case 'insert':
- if (!isset($instruction['data']['select'])) {
- $data = $this->getInstructionFields($instruction['data'], $table['fields']);
- if (!empty($data)) {
- $fields = implode(', ', array_keys($data));
- $values = implode(', ', array_values($data));
- $query = sprintf($query_insert, $table_name, $fields, $values);
- }
- } else {
- $data = $this->getInstructionFields($instruction['data']['select'], $table['fields']);
- $where = $this->getInstructionWhere($instruction['data']['select'], $table['fields']);
- $select_table_name = $this->db->quoteIdentifier($instruction['data']['select']['table'], true);
- if (!empty($data)) {
- $fields = implode(', ', array_keys($data));
- $values = implode(', ', array_values($data));
- $query = sprintf($query_insertselect, $table_name, $fields, $values, $select_table_name, $where);
- }
- }
- break;
- case 'update':
- $data = $this->getInstructionFields($instruction['data'], $table['fields']);
- $where = $this->getInstructionWhere($instruction['data'], $table['fields']);
- if (!empty($data)) {
- array_walk($data, array($this, 'buildFieldValue'));
- $fields_values = implode(', ', $data);
- $query = sprintf($query_update, $table_name, $fields_values, $where);
- }
- break;
- case 'delete':
- $where = $this->getInstructionWhere($instruction['data'], $table['fields']);
- $query = sprintf($query_delete, $table_name, $where);
- break;
- }
- if ($query) {
- if ($support_transactions && PEAR::isError($res = $this->db->beginNestedTransaction())) {
- return $res;
- }
- $result = $this->db->exec($query);
- if (PEAR::isError($result)) {
- return $result;
- }
- if ($support_transactions && PEAR::isError($res = $this->db->completeNestedTransaction())) {
- return $res;
- }
- }
- }
- return $result;
- }
- // }}}
- // {{{ buildFieldValue()
- /**
- * Appends the contents of second argument + '=' to the beginning of first
- * argument.
- *
- * Used with array_walk() in initializeTable() for UPDATEs.
- *
- * @param string &$element value of array's element
- * @param string $key key of array's element
- *
- * @return void
- *
- * @access public
- * @see MDB2_Schema::initializeTable()
- */
- function buildFieldValue(&$element, $key)
- {
- $element = $key."=$element";
- }
- // }}}
- // {{{ getExpression()
- /**
- * Generates a string that represents a value that would be associated
- * with a column in a DML instruction.
- *
- * @param array $element multi dimensional array that contains the
- * structure of the current DML instruction.
- * @param array $fields_definition multi dimensional array that contains the
- * definition for current table's fields
- * @param string $type type of given field
- *
- * @return string
- *
- * @access public
- * @see MDB2_Schema::getInstructionFields(), MDB2_Schema::getInstructionWhere()
- */
- function getExpression($element, $fields_definition = array(), $type = null)
- {
- $str = '';
- switch ($element['type']) {
- case 'null':
- $str .= 'NULL';
- break;
- case 'value':
- $str .= $this->db->quote($element['data'], $type);
- break;
- case 'column':
- $str .= $this->db->quoteIdentifier($element['data'], true);
- break;
- case 'function':
- $arguments = array();
- if (!empty($element['data']['arguments'])
- && is_array($element['data']['arguments'])
- ) {
- foreach ($element['data']['arguments'] as $v) {
- $arguments[] = $this->getExpression($v, $fields_definition);
- }
- }
- if (method_exists($this->db->function, $element['data']['name'])) {
- $user_func = array(&$this->db->function, $element['data']['name']);
- $str .= call_user_func_array($user_func, $arguments);
- } else {
- $str .= $element['data']['name'].'(';
- $str .= implode(', ', $arguments);
- $str .= ')';
- }
- break;
- case 'expression':
- $type0 = $type1 = null;
- if ($element['data']['operants'][0]['type'] == 'column'
- && array_key_exists($element['data']['operants'][0]['data'], $fields_definition)
- ) {
- $type0 = $fields_definition[$element['data']['operants'][0]['data']]['type'];
- }
- if ($element['data']['operants'][1]['type'] == 'column'
- && array_key_exists($element['data']['operants'][1]['data'], $fields_definition)
- ) {
- $type1 = $fields_definition[$element['data']['operants'][1]['data']]['type'];
- }
- $str .= '(';
- $str .= $this->getExpression($element['data']['operants'][0], $fields_definition, $type1);
- $str .= $this->getOperator($element['data']['operator']);
- $str .= $this->getExpression($element['data']['operants'][1], $fields_definition, $type0);
- $str .= ')';
- break;
- }
- return $str;
- }
- // }}}
- // {{{ getOperator()
- /**
- * Returns the matching SQL operator
- *
- * @param string $op parsed descriptive operator
- *
- * @return string matching SQL operator
- *
- * @access public
- * @static
- * @see MDB2_Schema::getExpression()
- */
- function getOperator($op)
- {
- switch ($op) {
- case 'PLUS':
- return ' + ';
- case 'MINUS':
- return ' - ';
- case 'TIMES':
- return ' * ';
- case 'DIVIDED':
- return ' / ';
- case 'EQUAL':
- return ' = ';
- case 'NOT EQUAL':
- return ' != ';
- case 'LESS THAN':
- return ' < ';
- case 'GREATER THAN':
- return ' > ';
- case 'LESS THAN OR EQUAL':
- return ' <= ';
- case 'GREATER THAN OR EQUAL':
- return ' >= ';
- default:
- return ' '.$op.' ';
- }
- }
- // }}}
- // {{{ getInstructionFields()
- /**
- * Walks the parsed DML instruction array, field by field,
- * storing them and their processed values inside a new array.
- *
- * @param array $instruction multi dimensional array that contains the
- * structure of the current DML instruction.
- * @param array $fields_definition multi dimensional array that contains the
- * definition for current table's fields
- *
- * @return array array of strings in the form 'field_name' => 'value'
- *
- * @access public
- * @static
- * @see MDB2_Schema::initializeTable()
- */
- function getInstructionFields($instruction, $fields_definition = array())
- {
- $fields = array();
- if (!empty($instruction['field']) && is_array($instruction['field'])) {
- foreach ($instruction['field'] as $field) {
- $field_name = $this->db->quoteIdentifier($field['name'], true);
- $fields[$field_name] = $this->getExpression($field['group'], $fields_definition);
- }
- }
- return $fields;
- }
- // }}}
- // {{{ getInstructionWhere()
- /**
- * Translates the parsed WHERE expression of a DML instruction
- * (array structure) to a SQL WHERE clause (string).
- *
- * @param array $instruction multi dimensional array that contains the
- * structure of the current DML instruction.
- * @param array $fields_definition multi dimensional array that contains the
- * definition for current table's fields.
- *
- * @return string SQL WHERE clause
- *
- * @access public
- * @static
- * @see MDB2_Schema::initializeTable()
- */
- function getInstructionWhere($instruction, $fields_definition = array())
- {
- $where = '';
- if (!empty($instruction['where'])) {
- $where = 'WHERE '.$this->getExpression($instruction['where'], $fields_definition);
- }
- return $where;
- }
- // }}}
- // {{{ createSequence()
- /**
- * Create a sequence
- *
- * @param string $sequence_name name of the sequence to be created
- * @param array $sequence multi dimensional array that contains the
- * structure and optional data of the table
- * @param bool $overwrite if the sequence should be overwritten if it already exists
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- */
- function createSequence($sequence_name, $sequence, $overwrite = false)
- {
- if (!$this->db->supports('sequences')) {
- $this->db->debug('Sequences are not supported', __FUNCTION__);
- return MDB2_OK;
- }
- $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
- $this->db->expectError($errorcodes);
- $sequences = $this->db->manager->listSequences();
- $this->db->popExpect();
- if (PEAR::isError($sequences)) {
- if (!MDB2::isError($sequences, $errorcodes)) {
- return $sequences;
- }
- } elseif (is_array($sequence) && in_array($sequence_name, $sequences)) {
- if (!$overwrite) {
- $this->db->debug('Sequence already exists: '.$sequence_name, __FUNCTION__);
- return MDB2_OK;
- }
- $result = $this->db->manager->dropSequence($sequence_name);
- if (PEAR::isError($result)) {
- return $result;
- }
- $this->db->debug('Overwritting sequence: '.$sequence_name, __FUNCTION__);
- }
- $start = 1;
- $field = '';
- if (!empty($sequence['on'])) {
- $table = $sequence['on']['table'];
- $field = $sequence['on']['field'];
- $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
- $this->db->expectError($errorcodes);
- $tables = $this->db->manager->listTables();
- $this->db->popExpect();
- if (PEAR::isError($tables) && !MDB2::isError($tables, $errorcodes)) {
- return $tables;
- }
- if (!PEAR::isError($tables) && is_array($tables) && in_array($table, $tables)) {
- if ($this->db->supports('summary_functions')) {
- $query = "SELECT MAX($field) FROM ".$this->db->quoteIdentifier($table, true);
- } else {
- $query = "SELECT $field FROM ".$this->db->quoteIdentifier($table, true)." ORDER BY $field DESC";
- }
- $start = $this->db->queryOne($query, 'integer');
- if (PEAR::isError($start)) {
- return $start;
- }
- ++$start;
- } else {
- $this->warnings[] = 'Could not sync sequence: '.$sequence_name;
- }
- } elseif (!empty($sequence['start']) && is_numeric($sequence['start'])) {
- $start = $sequence['start'];
- $table = '';
- }
- $result = $this->db->manager->createSequence($sequence_name, $start);
- if (PEAR::isError($result)) {
- return $result;
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ 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.
- *
- * @param array $database_definition multi dimensional array that contains the current definition
- * @param array $options an array of options to be passed to the
- * database specific driver version of
- * MDB2_Driver_Manager_Common::createTable().
- *
- * @return bool|MDB2_Error MDB2_OK or error object
- * @access public
- */
- function createDatabase($database_definition, $options = array())
- {
- if (!isset($database_definition['name']) || !$database_definition['name']) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
- 'no valid database name specified');
- }
- $create = (isset($database_definition['create']) && $database_definition['create']);
- $overwrite = (isset($database_definition['overwrite']) && $database_definition['overwrite']);
- /**
- *
- * We need to clean up database name before any query to prevent
- * database driver from using a inexistent database
- *
- */
- $previous_database_name = $this->db->setDatabase('');
- // Lower / Upper case the db name if the portability deems so.
- if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
- $func = $this->db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper';
- $db_name = $func($database_definition['name']);
- } else {
- $db_name = $database_definition['name'];
- }
- if ($create) {
- $dbExists = $this->db->databaseExists($db_name);
- if (PEAR::isError($dbExists)) {
- return $dbExists;
- }
- if ($dbExists && $overwrite) {
- $this->db->expectError(MDB2_ERROR_CANNOT_DROP);
- $result = $this->db->manager->dropDatabase($db_name);
- $this->db->popExpect();
- if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_CANNOT_DROP)) {
- return $result;
- }
- $dbExists = false;
- $this->db->debug('Overwritting database: ' . $db_name, __FUNCTION__);
- }
- $dbOptions = array();
- if (array_key_exists('charset', $database_definition)
- && !empty($database_definition['charset'])) {
- $dbOptions['charset'] = $database_definition['charset'];
- }
- if ($dbExists) {
- $this->db->debug('Database already exists: ' . $db_name, __…
Large files files are truncated, but you can click here to view the full file