diff options
author | Robin Appelman <icewind1991@gmail.com> | 2010-06-18 20:08:24 +0200 |
---|---|---|
committer | Robin Appelman <icewind1991@gmail.com> | 2010-06-18 20:08:24 +0200 |
commit | 7503ad139b53de41e3567d723419e49166de574f (patch) | |
tree | 4adf6db30eebcd1e72aff598deb5c5f935540cc0 | |
parent | 02ad83b04d79079eac4e0783a83db32ed0dba88b (diff) | |
download | nextcloud-server-7503ad139b53de41e3567d723419e49166de574f.tar.gz nextcloud-server-7503ad139b53de41e3567d723419e49166de574f.zip |
use MDB2_Schema for the creation of the initial database
-rwxr-xr-x | db_structure.xml | 381 | ||||
-rwxr-xr-x | inc/MDB2/Driver/Datatype/Common.php | 2 | ||||
-rwxr-xr-x | inc/MDB2/Driver/Datatype/mysql.php | 2 | ||||
-rwxr-xr-x | inc/MDB2/Driver/Datatype/pgsql.php | 2 | ||||
-rwxr-xr-x | inc/MDB2/Driver/Datatype/sqlite.php | 2 | ||||
-rwxr-xr-x | inc/MDB2/Driver/mysql.php | 2 | ||||
-rwxr-xr-x | inc/MDB2/LOB.php | 2 | ||||
-rw-r--r-- | inc/MDB2/Schema.php | 2763 | ||||
-rw-r--r-- | inc/MDB2/Schema/Parser.php | 819 | ||||
-rw-r--r-- | inc/MDB2/Schema/Parser2.php | 624 | ||||
-rw-r--r-- | inc/MDB2/Schema/Reserved/ibase.php | 436 | ||||
-rw-r--r-- | inc/MDB2/Schema/Reserved/mssql.php | 258 | ||||
-rw-r--r-- | inc/MDB2/Schema/Reserved/mysql.php | 284 | ||||
-rw-r--r-- | inc/MDB2/Schema/Reserved/oci8.php | 171 | ||||
-rw-r--r-- | inc/MDB2/Schema/Reserved/pgsql.php | 147 | ||||
-rw-r--r-- | inc/MDB2/Schema/Tool.php | 560 | ||||
-rw-r--r-- | inc/MDB2/Schema/Tool/ParameterException.php | 6 | ||||
-rw-r--r-- | inc/MDB2/Schema/Validate.php | 922 | ||||
-rw-r--r-- | inc/MDB2/Schema/Writer.php | 581 | ||||
-rwxr-xr-x | inc/XML/Parser.php | 9 | ||||
-rwxr-xr-x | inc/lib_base.php | 40 | ||||
-rwxr-xr-x | inc/lib_config.php | 213 |
22 files changed, 8048 insertions, 178 deletions
diff --git a/db_structure.xml b/db_structure.xml new file mode 100755 index 00000000000..72c63761bc9 --- /dev/null +++ b/db_structure.xml @@ -0,0 +1,381 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<database> + + <name>owncloud</name> + <create>true</create> + <overwrite>false</overwrite> + + <charset>latin1</charset> + + <table> + + <name>groups</name> + + <declaration> + + <field> + <name>group_id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <length>4</length> + </field> + + <field> + <name>group_name</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>64</length> + </field> + + <index> + <name>group_name</name> + <unique>true</unique> + <field> + <name>group_name</name> + <sorting>ascending</sorting> + </field> + </index> + + </declaration> + + </table> + + <table> + + <name>locks</name> + + <declaration> + + <field> + <name>token</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>255</length> + </field> + + <field> + <name>path</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>200</length> + </field> + + <field> + <name>created</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <length>4</length> + </field> + + <field> + <name>modified</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <length>4</length> + </field> + + <field> + <name>expires</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <length>4</length> + </field> + + <field> + <name>owner</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>200</length> + </field> + + <field> + <name>recursive</name> + <type>integer</type> + <default>0</default> + <notnull>false</notnull> + <length>4</length> + </field> + + <field> + <name>writelock</name> + <type>integer</type> + <default>0</default> + <notnull>false</notnull> + <length>4</length> + </field> + + <field> + <name>exclusivelock</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <length>4</length> + </field> + + <index> + <name>path_2</name> + <field> + <name>path</name> + <sorting>ascending</sorting> + </field> + </index> + + <index> + <name>path_3</name> + <field> + <name>path</name> + <sorting>ascending</sorting> + </field> + <field> + <name>token</name> + <sorting>ascending</sorting> + </field> + </index> + + <index> + <name>expires</name> + <field> + <name>expires</name> + <sorting>ascending</sorting> + </field> + </index> + + <index> + <name>locks_pKey</name> + <primary>true</primary> + <field> + <name>token</name> + <sorting>ascending</sorting> + </field> + </index> + + <index> + <name>token</name> + <unique>true</unique> + <field> + <name>token</name> + <sorting>ascending</sorting> + </field> + </index> + + </declaration> + + </table> + + <table> + + <name>log</name> + + <declaration> + + <field> + <name>id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <length>4</length> + </field> + + <field> + <name>timestamp</name> + <type>integer</type> + <default></default> + <notnull>true</notnull> + <length>4</length> + </field> + + <field> + <name>user</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>250</length> + </field> + + <field> + <name>type</name> + <type>integer</type> + <default></default> + <notnull>true</notnull> + <length>4</length> + </field> + + <field> + <name>message</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>250</length> + </field> + + </declaration> + + </table> + + <table> + + <name>properties</name> + + <declaration> + + <field> + <name>path</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>255</length> + </field> + + <field> + <name>name</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>120</length> + </field> + + <field> + <name>ns</name> + <type>text</type> + <default>DAV:</default> + <notnull>true</notnull> + <length>120</length> + </field> + + <field> + <name>value</name> + <type>clob</type> + <notnull>false</notnull> + </field> + + <index> + <name>path</name> + <field> + <name>path</name> + <sorting>ascending</sorting> + </field> + </index> + + <index> + <name>properties_pKey</name> + <primary>true</primary> + <field> + <name>path</name> + <sorting>ascending</sorting> + </field> + <field> + <name>name</name> + <sorting>ascending</sorting> + </field> + <field> + <name>ns</name> + <sorting>ascending</sorting> + </field> + </index> + + </declaration> + + </table> + + <table> + + <name>user_group</name> + + <declaration> + + <field> + <name>user_group_id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <length>4</length> + </field> + + <field> + <name>user_id</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>64</length> + </field> + + <field> + <name>group_id</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>64</length> + </field> + + </declaration> + + </table> + + <table> + + <name>users</name> + + <declaration> + + <field> + <name>user_id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <length>4</length> + </field> + + <field> + <name>user_name</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>64</length> + </field> + + <field> + <name>user_name_clean</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>64</length> + </field> + + <field> + <name>user_password</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>340</length> + </field> + + <index> + <name>user_name</name> + <unique>true</unique> + <field> + <name>user_name</name> + <sorting>ascending</sorting> + </field> + <field> + <name>user_name_clean</name> + <sorting>ascending</sorting> + </field> + </index> + + </declaration> + + </table> + +</database> diff --git a/inc/MDB2/Driver/Datatype/Common.php b/inc/MDB2/Driver/Datatype/Common.php index 329bd60768d..750dbb24772 100755 --- a/inc/MDB2/Driver/Datatype/Common.php +++ b/inc/MDB2/Driver/Datatype/Common.php @@ -44,7 +44,7 @@ // // $Id: Common.php,v 1.139 2008/12/04 11:50:42 afz Exp $ -require_once 'MDB2/LOB.php'; +oc_require_once('MDB2/LOB.php'); /** * @package MDB2 diff --git a/inc/MDB2/Driver/Datatype/mysql.php b/inc/MDB2/Driver/Datatype/mysql.php index aeec86aefac..944248f57c3 100755 --- a/inc/MDB2/Driver/Datatype/mysql.php +++ b/inc/MDB2/Driver/Datatype/mysql.php @@ -46,7 +46,7 @@ // $Id: mysql.php,v 1.65 2008/02/22 19:23:49 quipo Exp $ // -require_once 'MDB2/Driver/Datatype/Common.php'; +oc_require_once('MDB2/Driver/Datatype/Common.php'); /** * MDB2 MySQL driver diff --git a/inc/MDB2/Driver/Datatype/pgsql.php b/inc/MDB2/Driver/Datatype/pgsql.php index 29796804902..fe18729c84f 100755 --- a/inc/MDB2/Driver/Datatype/pgsql.php +++ b/inc/MDB2/Driver/Datatype/pgsql.php @@ -44,7 +44,7 @@ // // $Id: pgsql.php,v 1.93 2008/08/28 20:32:57 afz Exp $ -require_once 'MDB2/Driver/Datatype/Common.php'; +oc_require_once('MDB2/Driver/Datatype/Common.php'); /** * MDB2 PostGreSQL driver diff --git a/inc/MDB2/Driver/Datatype/sqlite.php b/inc/MDB2/Driver/Datatype/sqlite.php index 270c1396841..533d0e9510b 100755 --- a/inc/MDB2/Driver/Datatype/sqlite.php +++ b/inc/MDB2/Driver/Datatype/sqlite.php @@ -46,7 +46,7 @@ // $Id: sqlite.php,v 1.67 2008/02/22 19:58:06 quipo Exp $ // -require_once 'MDB2/Driver/Datatype/Common.php'; +oc_require_once('MDB2/Driver/Datatype/Common.php'); /** * MDB2 SQLite driver diff --git a/inc/MDB2/Driver/mysql.php b/inc/MDB2/Driver/mysql.php index 5c8df3b6b68..d7b5719f68b 100755 --- a/inc/MDB2/Driver/mysql.php +++ b/inc/MDB2/Driver/mysql.php @@ -1406,7 +1406,7 @@ class MDB2_Result_mysql extends MDB2_Result_Common if ($object_class == 'stdClass') {
$row = (object) $row;
} else {
- $row = &new $object_class($row);
+ $row = new $object_class($row);
}
}
++$this->rownum;
diff --git a/inc/MDB2/LOB.php b/inc/MDB2/LOB.php index 69db267d69e..2cdf67afa92 100755 --- a/inc/MDB2/LOB.php +++ b/inc/MDB2/LOB.php @@ -50,7 +50,7 @@ * @author Lukas Smith <smith@pooteeweet.org> */ -require_once 'MDB2.php'; +oc_require_once('MDB2.php'); /** * MDB2_LOB: user land stream wrapper implementation for LOB support diff --git a/inc/MDB2/Schema.php b/inc/MDB2/Schema.php new file mode 100644 index 00000000000..44518b32658 --- /dev/null +++ b/inc/MDB2/Schema.php @@ -0,0 +1,2763 @@ +<?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, __FUNCTION__); + if (!empty($dbOptions)) { + $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NO_PERMISSION); + $this->db->expectError($errorcodes); + $result = $this->db->manager->alterDatabase($db_name, $dbOptions); + $this->db->popExpect(); + if (PEAR::isError($result) && !MDB2::isError($result, $errorcodes)) { + return $result; + } + } + $create = false; + } else { + $this->db->expectError(MDB2_ERROR_UNSUPPORTED); + $result = $this->db->manager->createDatabase($db_name, $dbOptions); + $this->db->popExpect(); + if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_UNSUPPORTED)) { + return $result; + } + $this->db->debug('Creating database: ' . $db_name, __FUNCTION__); + } + } + + $this->db->setDatabase($db_name); + if (($support_transactions = $this->db->supports('transactions')) + && PEAR::isError($result = $this->db->beginNestedTransaction()) + ) { + return $result; + } + + $created_objects = 0; + if (isset($database_definition['tables']) + && is_array($database_definition['tables']) + ) { + foreach ($database_definition['tables'] as $table_name => $table) { + $result = $this->createTable($table_name, $table, $overwrite, $options); + if (PEAR::isError($result)) { + break; + } + $created_objects++; + } + } + if (!PEAR::isError($result) + && isset($database_definition['sequences']) + && is_array($database_definition['sequences']) + ) { + foreach ($database_definition['sequences'] as $sequence_name => $sequence) { + $result = $this->createSequence($sequence_name, $sequence, false, $overwrite); + + if (PEAR::isError($result)) { + break; + } + $created_objects++; + } + } + + if ($support_transactions) { + $res = $this->db->completeNestedTransaction(); + if (PEAR::isError($res)) { + $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'Could not end transaction ('. + $res->getMessage().' ('.$res->getUserinfo().'))'); + } + } elseif (PEAR::isError($result) && $created_objects) { + $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'the database was only partially created ('. + $result->getMessage().' ('.$result->getUserinfo().'))'); + } + + $this->db->setDatabase($previous_database_name); + + if (PEAR::isError($result) && $create + && PEAR::isError($result2 = $this->db->manager->dropDatabase($db_name)) + ) { + if (!MDB2::isError($result2, MDB2_ERROR_UNSUPPORTED)) { + return $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'Could not drop the created database after unsuccessful creation attempt ('. + $result2->getMessage().' ('.$result2->getUserinfo().'))'); + } + } + + return $result; + } + + // }}} + // {{{ compareDefinitions() + + /** + * Compare a previous definition with the currently parsed definition + * + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * + * @return array|MDB2_Error array of changes on success, or a error object + * @access public + */ + function compareDefinitions($current_definition, $previous_definition) + { + $changes = array(); + + if (!empty($current_definition['tables']) && is_array($current_definition['tables'])) { + $changes['tables'] = $defined_tables = array(); + foreach ($current_definition['tables'] as $table_name => $table) { + $previous_tables = array(); + if (!empty($previous_definition) && is_array($previous_definition)) { + $previous_tables = $previous_definition['tables']; + } + $change = $this->compareTableDefinitions($table_name, $table, $previous_tables, $defined_tables); + if (PEAR::isError($change)) { + return $change; + } + if (!empty($change)) { + $changes['tables'] = MDB2_Schema::arrayMergeClobber($changes['tables'], $change); + } + } + + if (!empty($previous_definition['tables']) + && is_array($previous_definition['tables'])) { + foreach ($previous_definition['tables'] as $table_name => $table) { + if (empty($defined_tables[$table_name])) { + $changes['tables']['remove'][$table_name] = true; + } + } + } + } + if (!empty($current_definition['sequences']) && is_array($current_definition['sequences'])) { + $changes['sequences'] = $defined_sequences = array(); + foreach ($current_definition['sequences'] as $sequence_name => $sequence) { + $previous_sequences = array(); + if (!empty($previous_definition) && is_array($previous_definition)) { + $previous_sequences = $previous_definition['sequences']; + } + + $change = $this->compareSequenceDefinitions($sequence_name, + $sequence, + $previous_sequences, + $defined_sequences); + if (PEAR::isError($change)) { + return $change; + } + if (!empty($change)) { + $changes['sequences'] = MDB2_Schema::arrayMergeClobber($changes['sequences'], $change); + } + } + if (!empty($previous_definition['sequences']) && is_array($previous_definition['sequences'])) { + foreach ($previous_definition['sequences'] as $sequence_name => $sequence) { + if (empty($defined_sequences[$sequence_name])) { + $changes['sequences']['remove'][$sequence_name] = true; + } + } + } + } + return $changes; + } + + // }}} + // {{{ compareTableFieldsDefinitions() + + /** + * Compare a previous definition with the currently parsed definition + * + * @param string $table_name name of the table + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * + * @return array|MDB2_Error array of changes on success, or a error object + * @access public + */ + function compareTableFieldsDefinitions($table_name, $current_definition, + $previous_definition) + { + $changes = $defined_fields = array(); + + if (is_array($current_definition)) { + foreach ($current_definition as $field_name => $field) { + $was_field_name = $field['was']; + if (!empty($previous_definition[$field_name]) + && ( + (isset($previous_definition[$field_name]['was']) + && $previous_definition[$field_name]['was'] == $was_field_name) + || !isset($previous_definition[$was_field_name]) + )) { + $was_field_name = $field_name; + } + + if (!empty($previous_definition[$was_field_name])) { + if ($was_field_name != $field_name) { + $changes['rename'][$was_field_name] = array('name' => $field_name, 'definition' => $field); + } + + if (!empty($defined_fields[$was_field_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null, + 'the field "'.$was_field_name. + '" was specified for more than one field of table'); + } + + $defined_fields[$was_field_name] = true; + + $change = $this->db->compareDefinition($field, $previous_definition[$was_field_name]); + if (PEAR::isError($change)) { + return $change; + } + + if (!empty($change)) { + if (array_key_exists('default', $change) + && $change['default'] + && !array_key_exists('default', $field)) { + $field['default'] = null; + } + + $change['definition'] = $field; + + $changes['change'][$field_name] = $change; + } + } else { + if ($field_name != $was_field_name) { + return $this->raiseError(MDB2_SCHEMA_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'); + } + + $changes['add'][$field_name] = $field; + } + } + } + + if (isset($previous_definition) && is_array($previous_definition)) { + foreach ($previous_definition as $field_previous_name => $field_previous) { + if (empty($defined_fields[$field_previous_name])) { + $changes['remove'][$field_previous_name] = true; + } + } + } + + return $changes; + } + + // }}} + // {{{ compareTableIndexesDefinitions() + + /** + * Compare a previous definition with the currently parsed definition + * + * @param string $table_name name of the table + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * + * @return array|MDB2_Error array of changes on success, or a error object + * @access public + */ + function compareTableIndexesDefinitions($table_name, $current_definition, + $previous_definition) + { + $changes = $defined_indexes = array(); + + if (is_array($current_definition)) { + foreach ($current_definition as $index_name => $index) { + $was_index_name = $index['was']; + if (!empty($previous_definition[$index_name]) + && isset($previous_definition[$index_name]['was']) + && $previous_definition[$index_name]['was'] == $was_index_name + ) { + $was_index_name = $index_name; + } + if (!empty($previous_definition[$was_index_name])) { + $change = array(); + if ($was_index_name != $index_name) { + $change['name'] = $was_index_name; + } + + if (!empty($defined_indexes[$was_index_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null, + 'the index "'.$was_index_name.'" was specified for'. + ' more than one index of table "'.$table_name.'"'); + } + $defined_indexes[$was_index_name] = true; + + $previous_unique = array_key_exists('unique', $previous_definition[$was_index_name]) + ? $previous_definition[$was_index_name]['unique'] : false; + + $unique = array_key_exists('unique', $index) ? $index['unique'] : false; + if ($previous_unique != $unique) { + $change['unique'] = $unique; + } + + $previous_primary = array_key_exists('primary', $previous_definition[$was_index_name]) + ? $previous_definition[$was_index_name]['primary'] : false; + + $primary = array_key_exists('primary', $index) ? $index['primary'] : false; + if ($previous_primary != $primary) { + $change['primary'] = $primary; + } + + $defined_fields = array(); + $previous_fields = $previous_definition[$was_index_name]['fields']; + if (!empty($index['fields']) && is_array($index['fields'])) { + foreach ($index['fields'] as $field_name => $field) { + if (!empty($previous_fields[$field_name])) { + $defined_fields[$field_name] = true; + + $previous_sorting = array_key_exists('sorting', $previous_fields[$field_name]) + ? $previous_fields[$field_name]['sorting'] : ''; + + $sorting = array_key_exists('sorting', $field) ? $field['sorting'] : ''; + if ($previous_sorting != $sorting) { + $change['change'] = true; + } + } else { + $change['change'] = true; + } + } + } + if (isset($previous_fields) && is_array($previous_fields)) { + foreach ($previous_fields as $field_name => $field) { + if (empty($defined_fields[$field_name])) { + $change['change'] = true; + } + } + } + if (!empty($change)) { + $changes['change'][$index_name] = $current_definition[$index_name]; + } + } else { + if ($index_name != $was_index_name) { + return $this->raiseError(MDB2_SCHEMA_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'); + } + $changes['add'][$index_name] = $current_definition[$index_name]; + } + } + } + foreach ($previous_definition as $index_previous_name => $index_previous) { + if (empty($defined_indexes[$index_previous_name])) { + $changes['remove'][$index_previous_name] = $index_previous; + } + } + return $changes; + } + + // }}} + // {{{ compareTableDefinitions() + + /** + * Compare a previous definition with the currently parsed definition + * + * @param string $table_name name of the table + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * @param array &$defined_tables table names in the schema + * + * @return array|MDB2_Error array of changes on success, or a error object + * @access public + */ + function compareTableDefinitions($table_name, $current_definition, + $previous_definition, &$defined_tables) + { + $changes = array(); + + if (is_array($current_definition)) { + $was_table_name = $table_name; + if (!empty($current_definition['was'])) { + $was_table_name = $current_definition['was']; + } + if (!empty($previous_definition[$was_table_name])) { + $changes['change'][$was_table_name] = array(); + if ($was_table_name != $table_name) { + $changes['change'][$was_table_name] = array('name' => $table_name); + } + if (!empty($defined_tables[$was_table_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null, + 'the table "'.$was_table_name. + '" was specified for more than one table of the database'); + } + $defined_tables[$was_table_name] = true; + if (!empty($current_definition['fields']) && is_array($current_definition['fields'])) { + $previous_fields = array(); + if (isset($previous_definition[$was_table_name]['fields']) + && is_array($previous_definition[$was_table_name]['fields'])) { + $previous_fields = $previous_definition[$was_table_name]['fields']; + } + + $change = $this->compareTableFieldsDefinitions($table_name, + $current_definition['fields'], + $previous_fields); + + if (PEAR::isError($change)) { + return $change; + } + if (!empty($change)) { + $changes['change'][$was_table_name] = + MDB2_Schema::arrayMergeClobber($changes['change'][$was_table_name], $change); + } + } + if (!empty($current_definition['indexes']) && is_array($current_definition['indexes'])) { + $previous_indexes = array(); + if (isset($previous_definition[$was_table_name]['indexes']) + && is_array($previous_definition[$was_table_name]['indexes'])) { + $previous_indexes = $previous_definition[$was_table_name]['indexes']; + } + $change = $this->compareTableIndexesDefinitions($table_name, + $current_definition['indexes'], + $previous_indexes); + + if (PEAR::isError($change)) { + return $change; + } + if (!empty($change)) { + $changes['change'][$was_table_name]['indexes'] = $change; + } + } + if (empty($changes['change'][$was_table_name])) { + unset($changes['change'][$was_table_name]); + } + if (empty($changes['change'])) { + unset($changes['change']); + } + } else { + if ($table_name != $was_table_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null, + 'it was specified a previous table name ("'.$was_table_name. + '") for table "'.$table_name.'" that does not exist'); + } + $changes['add'][$table_name] = true; + } + } + + return $changes; + } + + // }}} + // {{{ compareSequenceDefinitions() + + /** + * Compare a previous definition with the currently parsed definition + * + * @param string $sequence_name name of the sequence + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * @param array &$defined_sequences names in the schema + * + * @return array|MDB2_Error array of changes on success, or a error object + * @access public + */ + function compareSequenceDefinitions($sequence_name, $current_definition, + $previous_definition, &$defined_sequences) + { + $changes = array(); + + if (is_array($current_definition)) { + $was_sequence_name = $sequence_name; + if (!empty($previous_definition[$sequence_name]) + && isset($previous_definition[$sequence_name]['was']) + && $previous_definition[$sequence_name]['was'] == $was_sequence_name + ) { + $was_sequence_name = $sequence_name; + } elseif (!empty($current_definition['was'])) { + $was_sequence_name = $current_definition['was']; + } + if (!empty($previous_definition[$was_sequence_name])) { + if ($was_sequence_name != $sequence_name) { + $changes['change'][$was_sequence_name]['name'] = $sequence_name; + } + + if (!empty($defined_sequences[$was_sequence_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null, + 'the sequence "'.$was_sequence_name.'" was specified as base'. + ' of more than of sequence of the database'); + } + + $defined_sequences[$was_sequence_name] = true; + + $change = array(); + if (!empty($current_definition['start']) + && isset($previous_definition[$was_sequence_name]['start']) + && $current_definition['start'] != $previous_definition[$was_sequence_name]['start'] + ) { + $change['start'] = $previous_definition[$sequence_name]['start']; + } + if (isset($current_definition['on']['table']) + && isset($previous_definition[$was_sequence_name]['on']['table']) + && $current_definition['on']['table'] != $previous_definition[$was_sequence_name]['on']['table'] + && isset($current_definition['on']['field']) + && isset($previous_definition[$was_sequence_name]['on']['field']) + && $current_definition['on']['field'] != $previous_definition[$was_sequence_name]['on']['field'] + ) { + $change['on'] = $current_definition['on']; + } + if (!empty($change)) { + $changes['change'][$was_sequence_name][$sequence_name] = $change; + } + } else { + if ($sequence_name != $was_sequence_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null, + 'it was specified a previous sequence name ("'.$was_sequence_name. + '") for sequence "'.$sequence_name.'" that does not exist'); + } + $changes['add'][$sequence_name] = true; + } + } + return $changes; + } + // }}} + // {{{ verifyAlterDatabase() + + /** + * Verify that the changes requested are supported + * + * @param array $changes associative array that contains the definition of the changes + * that are meant to be applied to the database structure. + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function verifyAlterDatabase($changes) + { + if (!empty($changes['tables']['change']) && is_array($changes['tables']['change'])) { + foreach ($changes['tables']['change'] as $table_name => $table) { + if (!empty($table['indexes']) && is_array($table['indexes'])) { + if (!$this->db->supports('indexes')) { + return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null, + 'indexes are not supported'); + } + $table_changes = count($table['indexes']); + if (!empty($table['indexes']['add'])) { + $table_changes--; + } + if (!empty($table['indexes']['remove'])) { + $table_changes--; + } + if (!empty($table['indexes']['change'])) { + $table_changes--; + } + if ($table_changes) { + return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null, + 'index alteration not yet supported: '.implode(', ', array_keys($table['indexes']))); + } + } + unset($table['indexes']); + $result = $this->db->manager->alterTable($table_name, $table, true); + if (PEAR::isError($result)) { + return $result; + } + } + } + if (!empty($changes['sequences']) && is_array($changes['sequences'])) { + if (!$this->db->supports('sequences')) { + return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null, + 'sequences are not supported'); + } + $sequence_changes = count($changes['sequences']); + if (!empty($changes['sequences']['add'])) { + $sequence_changes--; + } + if (!empty($changes['sequences']['remove'])) { + $sequence_changes--; + } + if (!empty($changes['sequences']['change'])) { + $sequence_changes--; + } + if ($sequence_changes) { + return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null, + 'sequence alteration not yet supported: '.implode(', ', array_keys($changes['sequences']))); + } + } + return MDB2_OK; + } + + // }}} + // {{{ alterDatabaseIndexes() + + /** + * Execute the necessary actions to implement the requested changes + * in the indexes inside a database structure. + * + * @param string $table_name name of the table + * @param array $changes associative array that contains the definition of the changes + * that are meant to be applied to the database structure. + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function alterDatabaseIndexes($table_name, $changes) + { + $alterations = 0; + if (empty($changes)) { + return $alterations; + } + + if (!empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $index_name => $index) { + $this->db->expectError(MDB2_ERROR_NOT_FOUND); + if (!empty($index['primary']) || !empty($index['unique'])) { + $result = $this->db->manager->dropConstraint($table_name, $index_name, !empty($index['primary'])); + } 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; + } + $alterations++; + } + } + if (!empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $index_name => $index) { + /** + * Drop existing index/constraint first. + * Since $changes doesn't tell us whether it's an index or a constraint before the change, + * we have to find out and call the appropriate method. + */ + if (in_array($index_name, $this->db->manager->listTableIndexes($table_name))) { + $result = $this->db->manager->dropIndex($table_name, $index_name); + } elseif (in_array($index_name, $this->db->manager->listTableConstraints($table_name))) { + $result = $this->db->manager->dropConstraint($table_name, $index_name); + } + if (!empty($result) && PEAR::isError($result)) { + return $result; + } + + 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; + } + $alterations++; + } + } + if (!empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $index_name => $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; + } + $alterations++; + } + } + + return $alterations; + } + + // }}} + // {{{ alterDatabaseTables() + + /** + * Execute the necessary actions to implement the requested changes + * in the tables inside a database structure. + * + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * @param array $changes associative array that contains the definition of the changes + * that are meant to be applied to the database structure. + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function alterDatabaseTables($current_definition, $previous_definition, $changes) + { + /* FIXME: tables marked to be added are initialized by createTable(), others don't */ + $alterations = 0; + if (empty($changes)) { + return $alterations; + } + + if (!empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $table_name => $table) { + $result = $this->createTable($table_name, $current_definition[$table_name]); + if (PEAR::isError($result)) { + return $result; + } + $alterations++; + } + } + + if ($this->options['drop_missing_tables'] + && !empty($changes['remove']) + && is_array($changes['remove'])) { + foreach ($changes['remove'] as $table_name => $table) { + $result = $this->db->manager->dropTable($table_name); + if (PEAR::isError($result)) { + return $result; + } + $alterations++; + } + } + + if (!empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $table_name => $table) { + $indexes = array(); + if (!empty($table['indexes'])) { + $indexes = $table['indexes']; + unset($table['indexes']); + } + if (!empty($indexes['remove'])) { + $result = $this->alterDatabaseIndexes($table_name, array('remove' => $indexes['remove'])); + if (PEAR::isError($result)) { + return $result; + } + unset($indexes['remove']); + $alterations += $result; + } + $result = $this->db->manager->alterTable($table_name, $table, false); + if (PEAR::isError($result)) { + return $result; + } + $alterations++; + + // table may be renamed at this point + if (!empty($table['name'])) { + $table_name = $table['name']; + } + + if (!empty($indexes)) { + $result = $this->alterDatabaseIndexes($table_name, $indexes); + if (PEAR::isError($result)) { + return $result; + } + $alterations += $result; + } + } + } + + return $alterations; + } + + // }}} + // {{{ alterDatabaseSequences() + + /** + * Execute the necessary actions to implement the requested changes + * in the sequences inside a database structure. + * + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * @param array $changes associative array that contains the definition of the changes + * that are meant to be applied to the database structure. + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function alterDatabaseSequences($current_definition, $previous_definition, $changes) + { + $alterations = 0; + if (empty($changes)) { + return $alterations; + } + + if (!empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $sequence_name => $sequence) { + $result = $this->createSequence($sequence_name, $current_definition[$sequence_name]); + if (PEAR::isError($result)) { + return $result; + } + $alterations++; + } + } + + if (!empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $sequence_name => $sequence) { + $result = $this->db->manager->dropSequence($sequence_name); + if (PEAR::isError($result)) { + return $result; + } + $alterations++; + } + } + + if (!empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $sequence_name => $sequence) { + $result = $this->db->manager->dropSequence($previous_definition[$sequence_name]['was']); + if (PEAR::isError($result)) { + return $result; + } + $result = $this->createSequence($sequence_name, $sequence); + if (PEAR::isError($result)) { + return $result; + } + $alterations++; + } + } + + return $alterations; + } + + // }}} + // {{{ alterDatabase() + + /** + * Execute the necessary actions to implement the requested changes + * in a database structure. + * + * @param array $current_definition multi dimensional array that contains the current definition + * @param array $previous_definition multi dimensional array that contains the previous definition + * @param array $changes associative array that contains the definition of the changes + * that are meant to be applied to the database structure. + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function alterDatabase($current_definition, $previous_definition, $changes) + { + $alterations = 0; + if (empty($changes)) { + return $alterations; + } + + $result = $this->verifyAlterDatabase($changes); + if (PEAR::isError($result)) { + return $result; + } + + if (!empty($current_definition['name'])) { + $previous_database_name = $this->db->setDatabase($current_definition['name']); + } + + if (($support_transactions = $this->db->supports('transactions')) + && PEAR::isError($result = $this->db->beginNestedTransaction()) + ) { + return $result; + } + + if (!empty($changes['tables']) && !empty($current_definition['tables'])) { + $current_tables = isset($current_definition['tables']) ? $current_definition['tables'] : array(); + $previous_tables = isset($previous_definition['tables']) ? $previous_definition['tables'] : array(); + + $result = $this->alterDatabaseTables($current_tables, $previous_tables, $changes['tables']); + if (is_numeric($result)) { + $alterations += $result; + } + } + + if (!PEAR::isError($result) && !empty($changes['sequences'])) { + $current_sequences = isset($current_definition['sequences']) ? $current_definition['sequences'] : array(); + $previous_sequences = isset($previous_definition['sequences']) ? $previous_definition['sequences'] : array(); + + $result = $this->alterDatabaseSequences($current_sequences, $previous_sequences, $changes['sequences']); + if (is_numeric($result)) { + $alterations += $result; + } + } + + if ($support_transactions) { + $res = $this->db->completeNestedTransaction(); + if (PEAR::isError($res)) { + $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'Could not end transaction ('. + $res->getMessage().' ('.$res->getUserinfo().'))'); + } + } elseif (PEAR::isError($result) && $alterations) { + $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'the requested database alterations were only partially implemented ('. + $result->getMessage().' ('.$result->getUserinfo().'))'); + } + + if (isset($previous_database_name)) { + $this->db->setDatabase($previous_database_name); + } + return $result; + } + + // }}} + // {{{ dumpDatabaseChanges() + + /** + * Dump the changes between two database definitions. + * + * @param array $changes associative array that specifies the list of database + * definitions changes as returned by the _compareDefinitions + * manager class function. + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function dumpDatabaseChanges($changes) + { + if (!empty($changes['tables'])) { + if (!empty($changes['tables']['add']) && is_array($changes['tables']['add'])) { + foreach ($changes['tables']['add'] as $table_name => $table) { + $this->db->debug("$table_name:", __FUNCTION__); + $this->db->debug("\tAdded table '$table_name'", __FUNCTION__); + } + } + + if (!empty($changes['tables']['remove']) && is_array($changes['tables']['remove'])) { + if ($this->options['drop_missing_tables']) { + foreach ($changes['tables']['remove'] as $table_name => $table) { + $this->db->debug("$table_name:", __FUNCTION__); + $this->db->debug("\tRemoved table '$table_name'", __FUNCTION__); + } + } else { + foreach ($changes['tables']['remove'] as $table_name => $table) { + $this->db->debug("\tObsolete table '$table_name' left as is", __FUNCTION__); + } + } + } + + if (!empty($changes['tables']['change']) && is_array($changes['tables']['change'])) { + foreach ($changes['tables']['change'] as $table_name => $table) { + if (array_key_exists('name', $table)) { + $this->db->debug("\tRenamed table '$table_name' to '".$table['name']."'", __FUNCTION__); + } + if (!empty($table['add']) && is_array($table['add'])) { + foreach ($table['add'] as $field_name => $field) { + $this->db->debug("\tAdded field '".$field_name."'", __FUNCTION__); + } + } + if (!empty($table['remove']) && is_array($table['remove'])) { + foreach ($table['remove'] as $field_name => $field) { + $this->db->debug("\tRemoved field '".$field_name."'", __FUNCTION__); + } + } + if (!empty($table['rename']) && is_array($table['rename'])) { + foreach ($table['rename'] as $field_name => $field) { + $this->db->debug("\tRenamed field '".$field_name."' to '".$field['name']."'", __FUNCTION__); + } + } + if (!empty($table['change']) && is_array($table['change'])) { + foreach ($table['change'] as $field_name => $field) { + $field = $field['definition']; + if (array_key_exists('type', $field)) { + $this->db->debug("\tChanged field '$field_name' type to '".$field['type']."'", __FUNCTION__); + } + + if (array_key_exists('unsigned', $field)) { + $this->db->debug("\tChanged field '$field_name' type to '". + (!empty($field['unsigned']) && $field['unsigned'] ? '' : 'not ')."unsigned'", + __FUNCTION__); + } + + if (array_key_exists('length', $field)) { + $this->db->debug("\tChanged field '$field_name' length to '". + (!empty($field['length']) ? $field['length']: 'no length')."'", __FUNCTION__); + } + if (array_key_exists('default', $field)) { + $this->db->debug("\tChanged field '$field_name' default to ". + (isset($field['default']) ? "'".$field['default']."'" : 'NULL'), __FUNCTION__); + } + + if (array_key_exists('notnull', $field)) { + $this->db->debug("\tChanged field '$field_name' notnull to ". + (!empty($field['notnull']) && $field['notnull'] ? 'true' : 'false'), + __FUNCTION__); + } + } + } + if (!empty($table['indexes']) && is_array($table['indexes'])) { + if (!empty($table['indexes']['add']) && is_array($table['indexes']['add'])) { + foreach ($table['indexes']['add'] as $index_name => $index) { + $this->db->debug("\tAdded index '".$index_name. + "' of table '$table_name'", __FUNCTION__); + } + } + if (!empty($table['indexes']['remove']) && is_array($table['indexes']['remove'])) { + foreach ($table['indexes']['remove'] as $index_name => $index) { + $this->db->debug("\tRemoved index '".$index_name. + "' of table '$table_name'", __FUNCTION__); + } + } + if (!empty($table['indexes']['change']) && is_array($table['indexes']['change'])) { + foreach ($table['indexes']['change'] as $index_name => $index) { + if (array_key_exists('name', $index)) { + $this->db->debug("\tRenamed index '".$index_name."' to '".$index['name']. + "' on table '$table_name'", __FUNCTION__); + } + if (array_key_exists('unique', $index)) { + $this->db->debug("\tChanged index '".$index_name."' unique to '". + !empty($index['unique'])."' on table '$table_name'", __FUNCTION__); + } + if (array_key_exists('primary', $index)) { + $this->db->debug("\tChanged index '".$index_name."' primary to '". + !empty($index['primary'])."' on table '$table_name'", __FUNCTION__); + } + if (array_key_exists('change', $index)) { + $this->db->debug("\tChanged index '".$index_name. + "' on table '$table_name'", __FUNCTION__); + } + } + } + } + } + } + } + if (!empty($changes['sequences'])) { + if (!empty($changes['sequences']['add']) && is_array($changes['sequences']['add'])) { + foreach ($changes['sequences']['add'] as $sequence_name => $sequence) { + $this->db->debug("$sequence_name:", __FUNCTION__); + $this->db->debug("\tAdded sequence '$sequence_name'", __FUNCTION__); + } + } + if (!empty($changes['sequences']['remove']) && is_array($changes['sequences']['remove'])) { + foreach ($changes['sequences']['remove'] as $sequence_name => $sequence) { + $this->db->debug("$sequence_name:", __FUNCTION__); + $this->db->debug("\tAdded sequence '$sequence_name'", __FUNCTION__); + } + } + if (!empty($changes['sequences']['change']) && is_array($changes['sequences']['change'])) { + foreach ($changes['sequences']['change'] as $sequence_name => $sequence) { + if (array_key_exists('name', $sequence)) { + $this->db->debug("\tRenamed sequence '$sequence_name' to '". + $sequence['name']."'", __FUNCTION__); + } + if (!empty($sequence['change']) && is_array($sequence['change'])) { + foreach ($sequence['change'] as $sequence_name => $sequence) { + if (array_key_exists('start', $sequence)) { + $this->db->debug("\tChanged sequence '$sequence_name' start to '". + $sequence['start']."'", __FUNCTION__); + } + } + } + } + } + } + return MDB2_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 $database_definition multi dimensional array that contains the current definition + * @param array $arguments associative array that takes pairs of tag + * names and values that define dump options. + * <pre>array ( + * '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 + * 'end_of_line' => String + * end of line delimiter that should be used + * default: "\n" + * );</pre> + * @param int $dump Int that determines what data to dump + * + MDB2_SCHEMA_DUMP_ALL : the entire db + * + MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db + * + MDB2_SCHEMA_DUMP_CONTENT : only the content of the db + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function dumpDatabase($database_definition, $arguments, $dump = MDB2_SCHEMA_DUMP_ALL) + { + $class_name = $this->options['writer']; + + $result = MDB2::loadClass($class_name, $this->db->getOption('debug')); + if (PEAR::isError($result)) { + return $result; + } + + // get initialization data + if (isset($database_definition['tables']) && is_array($database_definition['tables']) + && $dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT + ) { + foreach ($database_definition['tables'] as $table_name => $table) { + $fields = array(); + $fieldsq = array(); + foreach ($table['fields'] as $field_name => $field) { + $fields[$field_name] = $field['type']; + + $fieldsq[] = $this->db->quoteIdentifier($field_name, true); + } + + $query = 'SELECT '.implode(', ', $fieldsq).' FROM '; + $query .= $this->db->quoteIdentifier($table_name, true); + + $data = $this->db->queryAll($query, $fields, MDB2_FETCHMODE_ASSOC); + + if (PEAR::isError($data)) { + return $data; + } + + if (!empty($data)) { + $initialization = array(); + $lob_buffer_length = $this->db->getOption('lob_buffer_length'); + foreach ($data as $row) { + $rows = array(); + foreach ($row as $key => $lob) { + if (is_resource($lob)) { + $value = ''; + while (!feof($lob)) { + $value .= fread($lob, $lob_buffer_length); + } + $row[$key] = $value; + } + $rows[] = array('name' => $key, 'group' => array('type' => 'value', 'data' => $row[$key])); + } + $initialization[] = array('type' => 'insert', 'data' => array('field' => $rows)); + } + $database_definition['tables'][$table_name]['initialization'] = $initialization; + } + } + } + + $writer = new $class_name($this->options['valid_types']); + return $writer->dumpDatabase($database_definition, $arguments, $dump); + } + + // }}} + // {{{ writeInitialization() + + /** + * Write initialization and sequences + * + * @param string|array $data data file or data array + * @param string|array $structure structure file or array + * @param array $variables associative array that is passed to the argument + * of the same name to the parseDatabaseDefinitionFile function. (there third + * param) + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function writeInitialization($data, $structure = false, $variables = array()) + { + if ($structure) { + $structure = $this->parseDatabaseDefinition($structure, false, $variables); + if (PEAR::isError($structure)) { + return $structure; + } + } + + $data = $this->parseDatabaseDefinition($data, false, $variables, false, $structure); + if (PEAR::isError($data)) { + return $data; + } + + $previous_database_name = null; + if (!empty($data['name'])) { + $previous_database_name = $this->db->setDatabase($data['name']); + } elseif (!empty($structure['name'])) { + $previous_database_name = $this->db->setDatabase($structure['name']); + } + + if (!empty($data['tables']) && is_array($data['tables'])) { + foreach ($data['tables'] as $table_name => $table) { + if (empty($table['initialization'])) { + continue; + } + $result = $this->initializeTable($table_name, $table); + if (PEAR::isError($result)) { + return $result; + } + } + } + + if (!empty($structure['sequences']) && is_array($structure['sequences'])) { + foreach ($structure['sequences'] as $sequence_name => $sequence) { + if (isset($data['sequences'][$sequence_name]) + || !isset($sequence['on']['table']) + || !isset($data['tables'][$sequence['on']['table']]) + ) { + continue; + } + $result = $this->createSequence($sequence_name, $sequence, true); + if (PEAR::isError($result)) { + return $result; + } + } + } + if (!empty($data['sequences']) && is_array($data['sequences'])) { + foreach ($data['sequences'] as $sequence_name => $sequence) { + $result = $this->createSequence($sequence_name, $sequence, true); + if (PEAR::isError($result)) { + return $result; + } + } + } + + if (isset($previous_database_name)) { + $this->db->setDatabase($previous_database_name); + } + + return MDB2_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|array $current_schema filename or array of the updated database schema definition. + * @param string|array $previous_schema filename or array of the previously installed database schema definition. + * @param array $variables associative array that is passed to the argument of the same + * name to the parseDatabaseDefinitionFile function. (there third param) + * @param bool $disable_query determines if the disable_query option should be set to true + * for the alterDatabase() or createDatabase() call + * @param bool $overwrite_old_schema_file Overwrite? + * + * @return bool|MDB2_Error MDB2_OK or error object + * @access public + */ + function updateDatabase($current_schema, $previous_schema = false, + $variables = array(), $disable_query = false, + $overwrite_old_schema_file = false) + { + $current_definition = $this->parseDatabaseDefinition($current_schema, false, $variables, + $this->options['fail_on_invalid_names']); + + if (PEAR::isError($current_definition)) { + return $current_definition; + } + + $previous_definition = false; + if ($previous_schema) { + $previous_definition = $this->parseDatabaseDefinition($previous_schema, true, $variables, + $this->options['fail_on_invalid_names']); + if (PEAR::isError($previous_definition)) { + return $previous_definition; + } + } + + if ($previous_definition) { + $dbExists = $this->db->databaseExists($current_definition['name']); + if (PEAR::isError($dbExists)) { + return $dbExists; + } + + if (!$dbExists) { + return $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'database to update does not exist: '.$current_definition['name']); + } + + $changes = $this->compareDefinitions($current_definition, $previous_definition); + if (PEAR::isError($changes)) { + return $changes; + } + + if (is_array($changes)) { + $this->db->setOption('disable_query', $disable_query); + $result = $this->alterDatabase($current_definition, $previous_definition, $changes); + $this->db->setOption('disable_query', false); + if (PEAR::isError($result)) { + return $result; + } + $copy = true; + if ($this->db->options['debug']) { + $result = $this->dumpDatabaseChanges($changes); + if (PEAR::isError($result)) { + return $result; + } + } + } + } else { + $this->db->setOption('disable_query', $disable_query); + $result = $this->createDatabase($current_definition); + $this->db->setOption('disable_query', false); + if (PEAR::isError($result)) { + return $result; + } + } + + if ($overwrite_old_schema_file + && !$disable_query + && is_string($previous_schema) && is_string($current_schema) + && !copy($current_schema, $previous_schema)) { + + return $this->raiseError(MDB2_SCHEMA_ERROR, null, null, + 'Could not copy the new database definition file to the current file'); + } + + return MDB2_OK; + } + // }}} + // {{{ errorMessage() + + /** + * Return a textual error message for a MDB2 error code + * + * @param int|array $value integer error code, <code>null</code> to get the + * current error code-message map, + * or an array with a new error code-message map + * + * @return string error message, or false if the error code was not recognized + * @access public + */ + function errorMessage($value = null) + { + static $errorMessages; + if (is_array($value)) { + $errorMessages = $value; + return MDB2_OK; + } elseif (!isset($errorMessages)) { + $errorMessages = array( + MDB2_SCHEMA_ERROR => 'unknown error', + MDB2_SCHEMA_ERROR_PARSE => 'schema parse error', + MDB2_SCHEMA_ERROR_VALIDATE => 'schema validation error', + MDB2_SCHEMA_ERROR_INVALID => 'invalid', + MDB2_SCHEMA_ERROR_UNSUPPORTED => 'not supported', + MDB2_SCHEMA_ERROR_WRITER => 'schema writer error', + ); + } + + if (is_null($value)) { + return $errorMessages; + } + + if (PEAR::isError($value)) { + $value = $value->getCode(); + } + + return !empty($errorMessages[$value]) ? + $errorMessages[$value] : $errorMessages[MDB2_SCHEMA_ERROR]; + } + + // }}} + // {{{ 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 int|PEAR_Error $code integer error code or and PEAR_Error instance + * @param int $mode error mode, see PEAR_Error docs + * 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 array $options Options, depending on the mode, @see PEAR::setErrorHandling + * @param string $userinfo Extra debug information. Defaults to the last + * query and native error code. + * + * @return object a PEAR error object + * @access public + * @see PEAR_Error + */ + function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) + { + $err =& PEAR::raiseError(null, $code, $mode, $options, + $userinfo, 'MDB2_Schema_Error', true); + return $err; + } + + // }}} + // {{{ isError() + + /** + * Tell whether a value is an MDB2_Schema error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true only if $code is + * a string and $db->getMessage() == $code or + * $code is an integer and $db->getCode() == $code + * + * @return bool true if parameter is an error + * @access public + */ + function isError($data, $code = null) + { + if (is_a($data, 'MDB2_Schema_Error')) { + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() === $code; + } else { + $code = (array)$code; + return in_array($data->getCode(), $code); + } + } + return false; + } + + // }}} +} + +/** + * MDB2_Schema_Error implements a class for reporting portable database error + * messages. + * + * @category Database + * @package MDB2_Schema + * @author Stig Bakken <ssb@fast.no> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/packages/MDB2_Schema + */ +class MDB2_Schema_Error extends PEAR_Error +{ + /** + * MDB2_Schema_Error constructor. + * + * @param mixed $code error code, or string with error message. + * @param int $mode what 'error mode' to operate in + * @param int $level what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query + * + * @access public + */ + function MDB2_Schema_Error($code = MDB2_SCHEMA_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) + { + $this->PEAR_Error('MDB2_Schema Error: ' . MDB2_Schema::errorMessage($code), $code, + $mode, $level, $debuginfo); + } +} +?> diff --git a/inc/MDB2/Schema/Parser.php b/inc/MDB2/Schema/Parser.php new file mode 100644 index 00000000000..ed31ba03bdf --- /dev/null +++ b/inc/MDB2/Schema/Parser.php @@ -0,0 +1,819 @@ +<?php +/** + * PHP versions 4 and 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: Christian Dickmann <dickmann@php.net> + * Author: Igor Feghali <ifeghali@php.net> + * + * $Id: Parser.php,v 1.68 2008/11/30 03:34:00 clockwerx Exp $ + * + * @category Database + * @package MDB2_Schema + * @author Christian Dickmann <dickmann@php.net> + * @author Igor Feghali <ifeghali@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @version CVS: $Id: Parser.php,v 1.68 2008/11/30 03:34:00 clockwerx Exp $ + * @link http://pear.php.net/packages/MDB2_Schema + */ + + +oc_require_once('XML/Parser.php'); +oc_require_once('MDB2/Schema/Validate.php'); + +/** + * Parses an XML schema file + * + * @category Database + * @package MDB2_Schema + * @author Christian Dickmann <dickmann@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/packages/MDB2_Schema + */ +class MDB2_Schema_Parser extends XML_Parser +{ + var $database_definition = array(); + + var $elements = array(); + + var $element = ''; + + var $count = 0; + + var $table = array(); + + var $table_name = ''; + + var $field = array(); + + var $field_name = ''; + + var $init = array(); + + var $init_function = array(); + + var $init_expression = array(); + + var $init_field = array(); + + var $index = array(); + + var $index_name = ''; + + var $constraint = array(); + + var $constraint_name = ''; + + var $var_mode = false; + + var $variables = array(); + + var $sequence = array(); + + var $sequence_name = ''; + + var $error; + + var $structure = false; + + var $val; + + function __construct($variables, $fail_on_invalid_names = true, + $structure = false, $valid_types = array(), + $force_defaults = true) + { + // force ISO-8859-1 due to different defaults for PHP4 and PHP5 + // todo: this probably needs to be investigated some more andcleaned up + parent::XML_Parser('ISO-8859-1'); + + $this->variables = $variables; + $this->structure = $structure; + $this->val =new MDB2_Schema_Validate($fail_on_invalid_names, $valid_types, $force_defaults); + } + + function MDB2_Schema_Parser($variables, $fail_on_invalid_names = true, + $structure = false, $valid_types = array(), + $force_defaults = true) + { + $this->__construct($variables, $fail_on_invalid_names, $structure, $valid_types, $force_defaults); + } + + function startHandler($xp, $element, $attribs) + { + if (strtolower($element) == 'variable') { + $this->var_mode = true; + return; + } + + $this->elements[$this->count++] = strtolower($element); + + $this->element = implode('-', $this->elements); + + switch ($this->element) { + /* Initialization */ + case 'database-table-initialization': + $this->table['initialization'] = array(); + break; + + /* Insert */ + /* insert: field+ */ + case 'database-table-initialization-insert': + $this->init = array('type' => 'insert', 'data' => array('field' => array())); + break; + /* insert-select: field+, table, where? */ + case 'database-table-initialization-insert-select': + $this->init['data']['table'] = ''; + break; + + /* Update */ + /* update: field+, where? */ + case 'database-table-initialization-update': + $this->init = array('type' => 'update', 'data' => array('field' => array())); + break; + + /* Delete */ + /* delete: where */ + case 'database-table-initialization-delete': + $this->init = array('type' => 'delete', 'data' => array('where' => array())); + break; + + /* Insert and Update */ + case 'database-table-initialization-insert-field': + case 'database-table-initialization-insert-select-field': + case 'database-table-initialization-update-field': + $this->init_field = array('name' => '', 'group' => array()); + break; + case 'database-table-initialization-insert-field-value': + case 'database-table-initialization-insert-select-field-value': + case 'database-table-initialization-update-field-value': + /* if value tag is empty cdataHandler is not called so we must force value element creation here */ + $this->init_field['group'] = array('type' => 'value', 'data' => ''); + break; + case 'database-table-initialization-insert-field-null': + case 'database-table-initialization-insert-select-field-null': + case 'database-table-initialization-update-field-null': + $this->init_field['group'] = array('type' => 'null'); + break; + case 'database-table-initialization-insert-field-function': + case 'database-table-initialization-insert-select-field-function': + case 'database-table-initialization-update-field-function': + $this->init_function = array('name' => ''); + break; + case 'database-table-initialization-insert-field-expression': + case 'database-table-initialization-insert-select-field-expression': + case 'database-table-initialization-update-field-expression': + $this->init_expression = array(); + break; + + /* All */ + case 'database-table-initialization-insert-select-where': + case 'database-table-initialization-update-where': + case 'database-table-initialization-delete-where': + $this->init['data']['where'] = array('type' => '', 'data' => array()); + break; + case 'database-table-initialization-insert-select-where-expression': + case 'database-table-initialization-update-where-expression': + case 'database-table-initialization-delete-where-expression': + $this->init_expression = array(); + break; + + /* One level simulation of expression-function recursion */ + case 'database-table-initialization-insert-field-expression-function': + case 'database-table-initialization-insert-select-field-expression-function': + case 'database-table-initialization-insert-select-where-expression-function': + case 'database-table-initialization-update-field-expression-function': + case 'database-table-initialization-update-where-expression-function': + case 'database-table-initialization-delete-where-expression-function': + $this->init_function = array('name' => ''); + break; + + /* One level simulation of function-expression recursion */ + case 'database-table-initialization-insert-field-function-expression': + case 'database-table-initialization-insert-select-field-function-expression': + case 'database-table-initialization-insert-select-where-function-expression': + case 'database-table-initialization-update-field-function-expression': + case 'database-table-initialization-update-where-function-expression': + case 'database-table-initialization-delete-where-function-expression': + $this->init_expression = array(); + break; + + /* Definition */ + case 'database': + $this->database_definition = array( + 'name' => '', + 'create' => '', + 'overwrite' => '', + 'charset' => '', + 'description' => '', + 'comments' => '', + 'tables' => array(), + 'sequences' => array() + ); + break; + case 'database-table': + $this->table_name = ''; + + $this->table = array( + 'was' => '', + 'description' => '', + 'comments' => '', + 'fields' => array(), + 'indexes' => array(), + 'constraints' => array(), + 'initialization' => array() + ); + break; + case 'database-table-declaration-field': + case 'database-table-declaration-foreign-field': + case 'database-table-declaration-foreign-references-field': + $this->field_name = ''; + + $this->field = array(); + break; + case 'database-table-declaration-index-field': + $this->field_name = ''; + + $this->field = array('sorting' => '', 'length' => ''); + break; + /* force field attributes to be initialized when the tag is empty in the XML */ + case 'database-table-declaration-field-was': + $this->field['was'] = ''; + break; + case 'database-table-declaration-field-type': + $this->field['type'] = ''; + break; + case 'database-table-declaration-field-fixed': + $this->field['fixed'] = ''; + break; + case 'database-table-declaration-field-default': + $this->field['default'] = ''; + break; + case 'database-table-declaration-field-notnull': + $this->field['notnull'] = ''; + break; + case 'database-table-declaration-field-autoincrement': + $this->field['autoincrement'] = ''; + break; + case 'database-table-declaration-field-unsigned': + $this->field['unsigned'] = ''; + break; + case 'database-table-declaration-field-length': + $this->field['length'] = ''; + break; + case 'database-table-declaration-field-description': + $this->field['description'] = ''; + break; + case 'database-table-declaration-field-comments': + $this->field['comments'] = ''; + break; + case 'database-table-declaration-index': + $this->index_name = ''; + + $this->index = array( + 'was' => '', + 'unique' =>'', + 'primary' => '', + 'fields' => array() + ); + break; + case 'database-table-declaration-foreign': + $this->constraint_name = ''; + + $this->constraint = array( + 'was' => '', + 'match' => '', + 'ondelete' => '', + 'onupdate' => '', + 'deferrable' => '', + 'initiallydeferred' => '', + 'foreign' => true, + 'fields' => array(), + 'references' => array('table' => '', 'fields' => array()) + ); + break; + case 'database-sequence': + $this->sequence_name = ''; + + $this->sequence = array( + 'was' => '', + 'start' => '', + 'description' => '', + 'comments' => '', + 'on' => array('table' => '', 'field' => '') + ); + break; + } + } + + function endHandler($xp, $element) + { + if (strtolower($element) == 'variable') { + $this->var_mode = false; + return; + } + + switch ($this->element) { + /* Initialization */ + + /* Insert */ + case 'database-table-initialization-insert-select': + $this->init['data'] = array('select' => $this->init['data']); + break; + + /* Insert and Delete */ + case 'database-table-initialization-insert-field': + case 'database-table-initialization-insert-select-field': + case 'database-table-initialization-update-field': + $result = $this->val->validateDataField($this->table['fields'], $this->init['data']['field'], $this->init_field); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->init['data']['field'][] = $this->init_field; + } + break; + case 'database-table-initialization-insert-field-function': + case 'database-table-initialization-insert-select-field-function': + case 'database-table-initialization-update-field-function': + $this->init_field['group'] = array('type' => 'function', 'data' => $this->init_function); + break; + case 'database-table-initialization-insert-field-expression': + case 'database-table-initialization-insert-select-field-expression': + case 'database-table-initialization-update-field-expression': + $this->init_field['group'] = array('type' => 'expression', 'data' => $this->init_expression); + break; + + /* All */ + case 'database-table-initialization-insert-select-where-expression': + case 'database-table-initialization-update-where-expression': + case 'database-table-initialization-delete-where-expression': + $this->init['data']['where']['type'] = 'expression'; + $this->init['data']['where']['data'] = $this->init_expression; + break; + case 'database-table-initialization-insert': + case 'database-table-initialization-delete': + case 'database-table-initialization-update': + $this->table['initialization'][] = $this->init; + break; + + /* One level simulation of expression-function recursion */ + case 'database-table-initialization-insert-field-expression-function': + case 'database-table-initialization-insert-select-field-expression-function': + case 'database-table-initialization-insert-select-where-expression-function': + case 'database-table-initialization-update-field-expression-function': + case 'database-table-initialization-update-where-expression-function': + case 'database-table-initialization-delete-where-expression-function': + $this->init_expression['operants'][] = array('type' => 'function', 'data' => $this->init_function); + break; + + /* One level simulation of function-expression recursion */ + case 'database-table-initialization-insert-field-function-expression': + case 'database-table-initialization-insert-select-field-function-expression': + case 'database-table-initialization-insert-select-where-function-expression': + case 'database-table-initialization-update-field-function-expression': + case 'database-table-initialization-update-where-function-expression': + case 'database-table-initialization-delete-where-function-expression': + $this->init_function['arguments'][] = array('type' => 'expression', 'data' => $this->init_expression); + break; + + /* Table definition */ + case 'database-table': + $result = $this->val->validateTable($this->database_definition['tables'], $this->table, $this->table_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->database_definition['tables'][$this->table_name] = $this->table; + } + break; + case 'database-table-name': + if (isset($this->structure['tables'][$this->table_name])) { + $this->table = $this->structure['tables'][$this->table_name]; + } + break; + + /* Field declaration */ + case 'database-table-declaration-field': + $result = $this->val->validateField($this->table['fields'], $this->field, $this->field_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->table['fields'][$this->field_name] = $this->field; + } + break; + + /* Index declaration */ + case 'database-table-declaration-index': + $result = $this->val->validateIndex($this->table['indexes'], $this->index, $this->index_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->table['indexes'][$this->index_name] = $this->index; + } + break; + case 'database-table-declaration-index-field': + $result = $this->val->validateIndexField($this->index['fields'], $this->field, $this->field_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->index['fields'][$this->field_name] = $this->field; + } + break; + + /* Foreign Key declaration */ + case 'database-table-declaration-foreign': + $result = $this->val->validateConstraint($this->table['constraints'], $this->constraint, $this->constraint_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->table['constraints'][$this->constraint_name] = $this->constraint; + } + break; + case 'database-table-declaration-foreign-field': + $result = $this->val->validateConstraintField($this->constraint['fields'], $this->field_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->constraint['fields'][$this->field_name] = ''; + } + break; + case 'database-table-declaration-foreign-references-field': + $result = $this->val->validateConstraintReferencedField($this->constraint['references']['fields'], $this->field_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->constraint['references']['fields'][$this->field_name] = ''; + } + break; + + /* Sequence declaration */ + case 'database-sequence': + $result = $this->val->validateSequence($this->database_definition['sequences'], $this->sequence, $this->sequence_name); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } else { + $this->database_definition['sequences'][$this->sequence_name] = $this->sequence; + } + break; + + /* End of File */ + case 'database': + $result = $this->val->validateDatabase($this->database_definition); + if (PEAR::isError($result)) { + $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode()); + } + break; + } + + unset($this->elements[--$this->count]); + $this->element = implode('-', $this->elements); + } + + function &raiseError($msg = null, $xmlecode = 0, $xp = null, $ecode = MDB2_SCHEMA_ERROR_PARSE) + { + if (is_null($this->error)) { + $error = ''; + if (is_resource($msg)) { + $error .= 'Parser error: '.xml_error_string(xml_get_error_code($msg)); + $xp = $msg; + } else { + $error .= 'Parser error: '.$msg; + if (!is_resource($xp)) { + $xp = $this->parser; + } + } + + if ($error_string = xml_error_string($xmlecode)) { + $error .= ' - '.$error_string; + } + + if (is_resource($xp)) { + $byte = @xml_get_current_byte_index($xp); + $line = @xml_get_current_line_number($xp); + $column = @xml_get_current_column_number($xp); + $error .= " - Byte: $byte; Line: $line; Col: $column"; + } + + $error .= "\n"; + + $this->error =& MDB2_Schema::raiseError($ecode, null, null, $error); + } + return $this->error; + } + + function cdataHandler($xp, $data) + { + if ($this->var_mode == true) { + if (!isset($this->variables[$data])) { + $this->raiseError('variable "'.$data.'" not found', null, $xp); + return; + } + $data = $this->variables[$data]; + } + + switch ($this->element) { + /* Initialization */ + + /* Insert */ + case 'database-table-initialization-insert-select-table': + $this->init['data']['table'] = $data; + break; + + /* Insert and Update */ + case 'database-table-initialization-insert-field-name': + case 'database-table-initialization-insert-select-field-name': + case 'database-table-initialization-update-field-name': + $this->init_field['name'] .= $data; + break; + case 'database-table-initialization-insert-field-value': + case 'database-table-initialization-insert-select-field-value': + case 'database-table-initialization-update-field-value': + $this->init_field['group']['data'] .= $data; + break; + case 'database-table-initialization-insert-field-function-name': + case 'database-table-initialization-insert-select-field-function-name': + case 'database-table-initialization-update-field-function-name': + $this->init_function['name'] .= $data; + break; + case 'database-table-initialization-insert-field-function-value': + case 'database-table-initialization-insert-select-field-function-value': + case 'database-table-initialization-update-field-function-value': + $this->init_function['arguments'][] = array('type' => 'value', 'data' => $data); + break; + case 'database-table-initialization-insert-field-function-column': + case 'database-table-initialization-insert-select-field-function-column': + case 'database-table-initialization-update-field-function-column': + $this->init_function['arguments'][] = array('type' => 'column', 'data' => $data); + break; + case 'database-table-initialization-insert-field-column': + case 'database-table-initialization-insert-select-field-column': + case 'database-table-initialization-update-field-column': + $this->init_field['group'] = array('type' => 'column', 'data' => $data); + break; + + /* All */ + case 'database-table-initialization-insert-field-expression-operator': + case 'database-table-initialization-insert-select-field-expression-operator': + case 'database-table-initialization-insert-select-where-expression-operator': + case 'database-table-initialization-update-field-expression-operator': + case 'database-table-initialization-update-where-expression-operator': + case 'database-table-initialization-delete-where-expression-operator': + $this->init_expression['operator'] = $data; + break; + case 'database-table-initialization-insert-field-expression-value': + case 'database-table-initialization-insert-select-field-expression-value': + case 'database-table-initialization-insert-select-where-expression-value': + case 'database-table-initialization-update-field-expression-value': + case 'database-table-initialization-update-where-expression-value': + case 'database-table-initialization-delete-where-expression-value': + $this->init_expression['operants'][] = array('type' => 'value', 'data' => $data); + break; + case 'database-table-initialization-insert-field-expression-column': + case 'database-table-initialization-insert-select-field-expression-column': + case 'database-table-initialization-insert-select-where-expression-column': + case 'database-table-initialization-update-field-expression-column': + case 'database-table-initialization-update-where-expression-column': + case 'database-table-initialization-delete-where-expression-column': + $this->init_expression['operants'][] = array('type' => 'column', 'data' => $data); + break; + + case 'database-table-initialization-insert-field-function-function': + case 'database-table-initialization-insert-field-function-expression': + case 'database-table-initialization-insert-field-expression-expression': + case 'database-table-initialization-update-field-function-function': + case 'database-table-initialization-update-field-function-expression': + case 'database-table-initialization-update-field-expression-expression': + case 'database-table-initialization-update-where-expression-expression': + case 'database-table-initialization-delete-where-expression-expression': + /* Recursion to be implemented yet */ + break; + + /* One level simulation of expression-function recursion */ + case 'database-table-initialization-insert-field-expression-function-name': + case 'database-table-initialization-insert-select-field-expression-function-name': + case 'database-table-initialization-insert-select-where-expression-function-name': + case 'database-table-initialization-update-field-expression-function-name': + case 'database-table-initialization-update-where-expression-function-name': + case 'database-table-initialization-delete-where-expression-function-name': + $this->init_function['name'] .= $data; + break; + case 'database-table-initialization-insert-field-expression-function-value': + case 'database-table-initialization-insert-select-field-expression-function-value': + case 'database-table-initialization-insert-select-where-expression-function-value': + case 'database-table-initialization-update-field-expression-function-value': + case 'database-table-initialization-update-where-expression-function-value': + case 'database-table-initialization-delete-where-expression-function-value': + $this->init_function['arguments'][] = array('type' => 'value', 'data' => $data); + break; + case 'database-table-initialization-insert-field-expression-function-column': + case 'database-table-initialization-insert-select-field-expression-function-column': + case 'database-table-initialization-insert-select-where-expression-function-column': + case 'database-table-initialization-update-field-expression-function-column': + case 'database-table-initialization-update-where-expression-function-column': + case 'database-table-initialization-delete-where-expression-function-column': + $this->init_function['arguments'][] = array('type' => 'column', 'data' => $data); + break; + + /* One level simulation of function-expression recursion */ + case 'database-table-initialization-insert-field-function-expression-operator': + case 'database-table-initialization-insert-select-field-function-expression-operator': + case 'database-table-initialization-update-field-function-expression-operator': + $this->init_expression['operator'] = $data; + break; + case 'database-table-initialization-insert-field-function-expression-value': + case 'database-table-initialization-insert-select-field-function-expression-value': + case 'database-table-initialization-update-field-function-expression-value': + $this->init_expression['operants'][] = array('type' => 'value', 'data' => $data); + break; + case 'database-table-initialization-insert-field-function-expression-column': + case 'database-table-initialization-insert-select-field-function-expression-column': + case 'database-table-initialization-update-field-function-expression-column': + $this->init_expression['operants'][] = array('type' => 'column', 'data' => $data); + break; + + /* Database */ + case 'database-name': + $this->database_definition['name'] .= $data; + break; + case 'database-create': + $this->database_definition['create'] .= $data; + break; + case 'database-overwrite': + $this->database_definition['overwrite'] .= $data; + break; + case 'database-charset': + $this->database_definition['charset'] .= $data; + break; + case 'database-description': + $this->database_definition['description'] .= $data; + break; + case 'database-comments': + $this->database_definition['comments'] .= $data; + break; + + /* Table declaration */ + case 'database-table-name': + $this->table_name .= $data; + break; + case 'database-table-was': + $this->table['was'] .= $data; + break; + case 'database-table-description': + $this->table['description'] .= $data; + break; + case 'database-table-comments': + $this->table['comments'] .= $data; + break; + + /* Field declaration */ + case 'database-table-declaration-field-name': + $this->field_name .= $data; + break; + case 'database-table-declaration-field-was': + $this->field['was'] .= $data; + break; + case 'database-table-declaration-field-type': + $this->field['type'] .= $data; + break; + case 'database-table-declaration-field-fixed': + $this->field['fixed'] .= $data; + break; + case 'database-table-declaration-field-default': + $this->field['default'] .= $data; + break; + case 'database-table-declaration-field-notnull': + $this->field['notnull'] .= $data; + break; + case 'database-table-declaration-field-autoincrement': + $this->field['autoincrement'] .= $data; + break; + case 'database-table-declaration-field-unsigned': + $this->field['unsigned'] .= $data; + break; + case 'database-table-declaration-field-length': + $this->field['length'] .= $data; + break; + case 'database-table-declaration-field-description': + $this->field['description'] .= $data; + break; + case 'database-table-declaration-field-comments': + $this->field['comments'] .= $data; + break; + + /* Index declaration */ + case 'database-table-declaration-index-name': + $this->index_name .= $data; + break; + case 'database-table-declaration-index-was': + $this->index['was'] .= $data; + break; + case 'database-table-declaration-index-unique': + $this->index['unique'] .= $data; + break; + case 'database-table-declaration-index-primary': + $this->index['primary'] .= $data; + break; + case 'database-table-declaration-index-field-name': + $this->field_name .= $data; + break; + case 'database-table-declaration-index-field-sorting': + $this->field['sorting'] .= $data; + break; + /* Add by Leoncx */ + case 'database-table-declaration-index-field-length': + $this->field['length'] .= $data; + break; + + /* Foreign Key declaration */ + case 'database-table-declaration-foreign-name': + $this->constraint_name .= $data; + break; + case 'database-table-declaration-foreign-was': + $this->constraint['was'] .= $data; + break; + case 'database-table-declaration-foreign-match': + $this->constraint['match'] .= $data; + break; + case 'database-table-declaration-foreign-ondelete': + $this->constraint['ondelete'] .= $data; + break; + case 'database-table-declaration-foreign-onupdate': + $this->constraint['onupdate'] .= $data; + break; + case 'database-table-declaration-foreign-deferrable': + $this->constraint['deferrable'] .= $data; + break; + case 'database-table-declaration-foreign-initiallydeferred': + $this->constraint['initiallydeferred'] .= $data; + break; + case 'database-table-declaration-foreign-field': + $this->field_name .= $data; + break; + case 'database-table-declaration-foreign-references-table': + $this->constraint['references']['table'] .= $data; + break; + case 'database-table-declaration-foreign-references-field': + $this->field_name .= $data; + break; + + /* Sequence declaration */ + case 'database-sequence-name': + $this->sequence_name .= $data; + break; + case 'database-sequence-was': + $this->sequence['was'] .= $data; + break; + case 'database-sequence-start': + $this->sequence['start'] .= $data; + break; + case 'database-sequence-description': + $this->sequence['description'] .= $data; + break; + case 'database-sequence-comments': + $this->sequence['comments'] .= $data; + break; + case 'database-sequence-on-table': + $this->sequence['on']['table'] .= $data; + break; + case 'database-sequence-on-field': + $this->sequence['on']['field'] .= $data; + break; + } + } +} + +?> diff --git a/inc/MDB2/Schema/Parser2.php b/inc/MDB2/Schema/Parser2.php new file mode 100644 index 00000000000..01318473fdd --- /dev/null +++ b/inc/MDB2/Schema/Parser2.php @@ -0,0 +1,624 @@ +<?php +/** + * PHP versions 4 and 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: Igor Feghali <ifeghali@php.net> + * + * @category Database + * @package MDB2_Schema + * @author Igor Feghali <ifeghali@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @version CVS: $Id: Parser2.php,v 1.12 2008/11/30 03:34:00 clockwerx Exp $ + * @link http://pear.php.net/packages/MDB2_Schema + */ + +require_once 'XML/Unserializer.php'; +require_once 'MDB2/Schema/Validate.php'; + +/** + * Parses an XML schema file + * + * @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 + * @link http://pear.php.net/packages/MDB2_Schema + */ +class MDB2_Schema_Parser2 extends XML_Unserializer +{ + var $database_definition = array(); + + var $database_loaded = array(); + + var $variables = array(); + + var $error; + + var $structure = false; + + var $val; + + var $options = array(); + + var $table = array(); + + var $table_name = ''; + + var $field = array(); + + var $field_name = ''; + + var $index = array(); + + var $index_name = ''; + + var $constraint = array(); + + var $constraint_name = ''; + + var $sequence = array(); + + var $sequence_name = ''; + + var $init = array(); + + function __construct($variables, $fail_on_invalid_names = true, $structure = false, $valid_types = array(), $force_defaults = true) + { + // force ISO-8859-1 due to different defaults for PHP4 and PHP5 + // todo: this probably needs to be investigated some more and cleaned up + $this->options['encoding'] = 'ISO-8859-1'; + + $this->options['XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE'] = true; + $this->options['XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY'] = false; + + $this->options['forceEnum'] = array('table', 'field', 'index', 'foreign', 'insert', 'update', 'delete', 'sequence'); + + /* + * todo: find a way to force the following items not to be parsed as arrays + * as it cause problems in functions with multiple arguments + */ + //$this->options['forceNEnum'] = array('value', 'column'); + $this->variables = $variables; + $this->structure = $structure; + + $this->val =& new MDB2_Schema_Validate($fail_on_invalid_names, $valid_types, $force_defaults); + parent::XML_Unserializer($this->options); + } + + function MDB2_Schema_Parser2($variables, $fail_on_invalid_names = true, $structure = false, $valid_types = array(), $force_defaults = true) + { + $this->__construct($variables, $fail_on_invalid_names, $structure, $valid_types, $force_defaults); + } + + function parse() + { + $result = $this->unserialize($this->filename, true); + + if (PEAR::isError($result)) { + return $result; + } else { + $this->database_loaded = $this->getUnserializedData(); + return $this->fixDatabaseKeys($this->database_loaded); + } + } + + function setInputFile($filename) + { + $this->filename = $filename; + return MDB2_OK; + } + + function renameKey(&$arr, $oKey, $nKey) + { + $arr[$nKey] = &$arr[$oKey]; + unset($arr[$oKey]); + } + + function fixDatabaseKeys($database) + { + $this->database_definition = array( + 'name' => '', + 'create' => '', + 'overwrite' => '', + 'charset' => '', + 'description' => '', + 'comments' => '', + 'tables' => array(), + 'sequences' => array() + ); + + if (!empty($database['name'])) { + $this->database_definition['name'] = $database['name']; + } + if (!empty($database['create'])) { + $this->database_definition['create'] = $database['create']; + } + if (!empty($database['overwrite'])) { + $this->database_definition['overwrite'] = $database['overwrite']; + } + if (!empty($database['charset'])) { + $this->database_definition['charset'] = $database['charset']; + } + if (!empty($database['description'])) { + $this->database_definition['description'] = $database['description']; + } + if (!empty($database['comments'])) { + $this->database_definition['comments'] = $database['comments']; + } + + if (!empty($database['table']) && is_array($database['table'])) { + foreach ($database['table'] as $table) { + $this->fixTableKeys($table); + } + } + + if (!empty($database['sequence']) && is_array($database['sequence'])) { + foreach ($database['sequence'] as $sequence) { + $this->fixSequenceKeys($sequence); + } + } + + $result = $this->val->validateDatabase($this->database_definition); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } + + return MDB2_OK; + } + + function fixTableKeys($table) + { + $this->table = array( + 'was' => '', + 'description' => '', + 'comments' => '', + 'fields' => array(), + 'indexes' => array(), + 'constraints' => array(), + 'initialization' => array() + ); + + if (!empty($table['name'])) { + $this->table_name = $table['name']; + } else { + $this->table_name = ''; + } + if (!empty($table['was'])) { + $this->table['was'] = $table['was']; + } + if (!empty($table['description'])) { + $this->table['description'] = $table['description']; + } + if (!empty($table['comments'])) { + $this->table['comments'] = $table['comments']; + } + + if (!empty($table['declaration']) && is_array($table['declaration'])) { + if (!empty($table['declaration']['field']) && is_array($table['declaration']['field'])) { + foreach ($table['declaration']['field'] as $field) { + $this->fixTableFieldKeys($field); + } + } + + if (!empty($table['declaration']['index']) && is_array($table['declaration']['index'])) { + foreach ($table['declaration']['index'] as $index) { + $this->fixTableIndexKeys($index); + } + } + + if (!empty($table['declaration']['foreign']) && is_array($table['declaration']['foreign'])) { + foreach ($table['declaration']['foreign'] as $constraint) { + $this->fixTableConstraintKeys($constraint); + } + } + } + + if (!empty($table['initialization']) && is_array($table['initialization'])) { + if (!empty($table['initialization']['insert']) && is_array($table['initialization']['insert'])) { + foreach ($table['initialization']['insert'] as $init) { + $this->fixTableInitializationKeys($init, 'insert'); + } + } + if (!empty($table['initialization']['update']) && is_array($table['initialization']['update'])) { + foreach ($table['initialization']['update'] as $init) { + $this->fixTableInitializationKeys($init, 'update'); + } + } + if (!empty($table['initialization']['delete']) && is_array($table['initialization']['delete'])) { + foreach ($table['initialization']['delete'] as $init) { + $this->fixTableInitializationKeys($init, 'delete'); + } + } + } + + $result = $this->val->validateTable($this->database_definition['tables'], $this->table, $this->table_name); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } else { + $this->database_definition['tables'][$this->table_name] = $this->table; + } + + return MDB2_OK; + } + + function fixTableFieldKeys($field) + { + $this->field = array(); + if (!empty($field['name'])) { + $this->field_name = $field['name']; + } else { + $this->field_name = ''; + } + if (!empty($field['was'])) { + $this->field['was'] = $field['was']; + } + if (!empty($field['type'])) { + $this->field['type'] = $field['type']; + } + if (!empty($field['fixed'])) { + $this->field['fixed'] = $field['fixed']; + } + if (isset($field['default'])) { + $this->field['default'] = $field['default']; + } + if (!empty($field['notnull'])) { + $this->field['notnull'] = $field['notnull']; + } + if (!empty($field['autoincrement'])) { + $this->field['autoincrement'] = $field['autoincrement']; + } + if (!empty($field['unsigned'])) { + $this->field['unsigned'] = $field['unsigned']; + } + if (!empty($field['length'])) { + $this->field['length'] = $field['length']; + } + if (!empty($field['description'])) { + $this->field['description'] = $field['description']; + } + if (!empty($field['comments'])) { + $this->field['comments'] = $field['comments']; + } + + $result = $this->val->validateField($this->table['fields'], $this->field, $this->field_name); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } else { + $this->table['fields'][$this->field_name] = $this->field; + } + + return MDB2_OK; + } + + function fixTableIndexKeys($index) + { + $this->index = array( + 'was' => '', + 'unique' =>'', + 'primary' => '', + 'fields' => array() + ); + + if (!empty($index['name'])) { + $this->index_name = $index['name']; + } else { + $this->index_name = ''; + } + if (!empty($index['was'])) { + $this->index['was'] = $index['was']; + } + if (!empty($index['unique'])) { + $this->index['unique'] = $index['unique']; + } + if (!empty($index['primary'])) { + $this->index['primary'] = $index['primary']; + } + if (!empty($index['field'])) { + foreach ($index['field'] as $field) { + if (!empty($field['name'])) { + $this->field_name = $field['name']; + } else { + $this->field_name = ''; + } + $this->field = array( + 'sorting' => '', + 'length' => '' + ); + + if (!empty($field['sorting'])) { + $this->field['sorting'] = $field['sorting']; + } + if (!empty($field['length'])) { + $this->field['length'] = $field['length']; + } + + $result = $this->val->validateIndexField($this->index['fields'], $this->field, $this->field_name); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } + + $this->index['fields'][$this->field_name] = $this->field; + } + } + + $result = $this->val->validateIndex($this->table['indexes'], $this->index, $this->index_name); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } else { + $this->table['indexes'][$this->index_name] = $this->index; + } + + return MDB2_OK; + } + + function fixTableConstraintKeys($constraint) + { + $this->constraint = array( + 'was' => '', + 'match' => '', + 'ondelete' => '', + 'onupdate' => '', + 'deferrable' => '', + 'initiallydeferred' => '', + 'foreign' => true, + 'fields' => array(), + 'references' => array('table' => '', 'fields' => array()) + ); + + if (!empty($constraint['name'])) { + $this->constraint_name = $constraint['name']; + } else { + $this->constraint_name = ''; + } + if (!empty($constraint['was'])) { + $this->constraint['was'] = $constraint['was']; + } + if (!empty($constraint['match'])) { + $this->constraint['match'] = $constraint['match']; + } + if (!empty($constraint['ondelete'])) { + $this->constraint['ondelete'] = $constraint['ondelete']; + } + if (!empty($constraint['onupdate'])) { + $this->constraint['onupdate'] = $constraint['onupdate']; + } + if (!empty($constraint['deferrable'])) { + $this->constraint['deferrable'] = $constraint['deferrable']; + } + if (!empty($constraint['initiallydeferred'])) { + $this->constraint['initiallydeferred'] = $constraint['initiallydeferred']; + } + if (!empty($constraint['field']) && is_array($constraint['field'])) { + foreach ($constraint['field'] as $field) { + $result = $this->val->validateConstraintField($this->constraint['fields'], $field); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } + + $this->constraint['fields'][$field] = ''; + } + } + + if (!empty($constraint['references']) && is_array($constraint['references'])) { + /** + * As we forced 'table' to be enumerated + * we have to fix it on the foreign-references-table context + */ + if (!empty($constraint['references']['table']) && is_array($constraint['references']['table'])) { + $this->constraint['references']['table'] = $constraint['references']['table'][0]; + } + + if (!empty($constraint['references']['field']) && is_array($constraint['references']['field'])) { + foreach ($constraint['references']['field'] as $field) { + $result = $this->val->validateConstraintReferencedField($this->constraint['references']['fields'], $field); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } + + $this->constraint['references']['fields'][$field] = ''; + } + } + } + + $result = $this->val->validateConstraint($this->table['constraints'], $this->constraint, $this->constraint_name); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } else { + $this->table['constraints'][$this->constraint_name] = $this->constraint; + } + + return MDB2_OK; + } + + function fixTableInitializationKeys($element, $type = '') + { + if (!empty($element['select']) && is_array($element['select'])) { + $this->fixTableInitializationDataKeys($element['select']); + $this->init = array( 'select' => $this->init ); + } else { + $this->fixTableInitializationDataKeys($element); + } + + $this->table['initialization'][] = array( 'type' => $type, 'data' => $this->init ); + } + + function fixTableInitializationDataKeys($element) + { + $this->init = array(); + if (!empty($element['field']) && is_array($element['field'])) { + foreach ($element['field'] as $field) { + $name = $field['name']; + unset($field['name']); + + $this->setExpression($field); + $this->init['field'][] = array( 'name' => $name, 'group' => $field ); + } + } + /** + * As we forced 'table' to be enumerated + * we have to fix it on the insert-select context + */ + if (!empty($element['table']) && is_array($element['table'])) { + $this->init['table'] = $element['table'][0]; + } + if (!empty($element['where']) && is_array($element['where'])) { + $this->init['where'] = $element['where']; + $this->setExpression($this->init['where']); + } + } + + function setExpression(&$arr) + { + $element = each($arr); + + $arr = array( 'type' => $element['key'] ); + + $element = $element['value']; + + switch ($arr['type']) { + case 'null': + break; + case 'value': + case 'column': + $arr['data'] = $element; + break; + case 'function': + if (!empty($element) + && is_array($element) + ) { + $arr['data'] = array( 'name' => $element['name'] ); + unset($element['name']); + + foreach ($element as $type => $value) { + if (!empty($value)) { + if (is_array($value)) { + foreach ($value as $argument) { + $argument = array( $type => $argument ); + $this->setExpression($argument); + $arr['data']['arguments'][] = $argument; + } + } else { + $arr['data']['arguments'][] = array( 'type' => $type, 'data' => $value ); + } + } + } + } + break; + case 'expression': + $arr['data'] = array( 'operants' => array(), 'operator' => $element['operator'] ); + unset($element['operator']); + + foreach ($element as $k => $v) { + $argument = array( $k => $v ); + $this->setExpression($argument); + $arr['data']['operants'][] = $argument; + } + break; + } + } + + function fixSequenceKeys($sequence) + { + $this->sequence = array( + 'was' => '', + 'start' => '', + 'description' => '', + 'comments' => '', + 'on' => array('table' => '', 'field' => '') + ); + + if (!empty($sequence['name'])) { + $this->sequence_name = $sequence['name']; + } else { + $this->sequence_name = ''; + } + if (!empty($sequence['was'])) { + $this->sequence['was'] = $sequence['was']; + } + if (!empty($sequence['start'])) { + $this->sequence['start'] = $sequence['start']; + } + if (!empty($sequence['description'])) { + $this->sequence['description'] = $sequence['description']; + } + if (!empty($sequence['comments'])) { + $this->sequence['comments'] = $sequence['comments']; + } + if (!empty($sequence['on']) && is_array($sequence['on'])) { + /** + * As we forced 'table' to be enumerated + * we have to fix it on the sequence-on-table context + */ + if (!empty($sequence['on']['table']) && is_array($sequence['on']['table'])) { + $this->sequence['on']['table'] = $sequence['on']['table'][0]; + } + + /** + * As we forced 'field' to be enumerated + * we have to fix it on the sequence-on-field context + */ + if (!empty($sequence['on']['field']) && is_array($sequence['on']['field'])) { + $this->sequence['on']['field'] = $sequence['on']['field'][0]; + } + } + + $result = $this->val->validateSequence($this->database_definition['sequences'], $this->sequence, $this->sequence_name); + if (PEAR::isError($result)) { + return $this->raiseError($result->getUserinfo()); + } else { + $this->database_definition['sequences'][$this->sequence_name] = $this->sequence; + } + + return MDB2_OK; + } + + function &raiseError($msg = null, $ecode = MDB2_SCHEMA_ERROR_PARSE) + { + if (is_null($this->error)) { + $error = 'Parser error: '.$msg."\n"; + + $this->error =& MDB2_Schema::raiseError($ecode, null, null, $error); + } + return $this->error; + } +} + +?> diff --git a/inc/MDB2/Schema/Reserved/ibase.php b/inc/MDB2/Schema/Reserved/ibase.php new file mode 100644 index 00000000000..b208abc83a3 --- /dev/null +++ b/inc/MDB2/Schema/Reserved/ibase.php @@ -0,0 +1,436 @@ +<?php +// {{{ Disclaimer, Licence, copyrights +// +----------------------------------------------------------------------+ +// | PHP versions 4 and 5 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Stig. S. Bakken, Lukas Smith | +// | All rights reserved. | +// +----------------------------------------------------------------------+ +// | MDB2 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: Lorenzo Alberton <l.alberton@quipo.it> | +// +----------------------------------------------------------------------+ +// +// }}} +// {{{ $GLOBALS['_MDB2_Schema_Reserved']['ibase'] +/** + * Has a list of reserved words of Interbase/Firebird + * + * @package MDB2_Schema + * @category Database + * @access protected + * @author Lorenzo Alberton <l.alberton@quipo.it> + */ +$GLOBALS['_MDB2_Schema_Reserved']['ibase'] = array( + 'ABS', + 'ABSOLUTE', + 'ACTION', + 'ACTIVE', + 'ADD', + 'ADMIN', + 'AFTER', + 'ALL', + 'ALLOCATE', + 'ALTER', + 'AND', + 'ANY', + 'ARE', + 'AS', + 'ASC', + 'ASCENDING', + 'ASSERTION', + 'AT', + 'AUTHORIZATION', + 'AUTO', + 'AUTODDL', + 'AVG', + 'BACKUP', + 'BASE_NAME', + 'BASED', + 'BASENAME', + 'BEFORE', + 'BEGIN', + 'BETWEEN', + 'BIGINT', + 'BIT', + 'BIT_LENGTH', + 'BLOB', + 'BLOCK', + 'BLOBEDIT', + 'BOOLEAN', + 'BOTH', + 'BOTH', + 'BREAK', + 'BUFFER', + 'BY', + 'CACHE', + 'CASCADE', + 'CASCADED', + 'CASE', + 'CASE', + 'CAST', + 'CATALOG', + 'CHAR', + 'CHAR_LENGTH', + 'CHARACTER', + 'CHARACTER_LENGTH', + 'CHECK', + 'CHECK_POINT_LEN', + 'CHECK_POINT_LENGTH', + 'CLOSE', + 'COALESCE', + 'COLLATE', + 'COLLATION', + 'COLUMN', + 'COMMENT', + 'COMMIT', + 'COMMITTED', + 'COMPILETIME', + 'COMPUTED', + 'CONDITIONAL', + 'CONNECT', + 'CONNECTION', + 'CONSTRAINT', + 'CONSTRAINTS', + 'CONTAINING', + 'CONTINUE', + 'CONVERT', + 'CORRESPONDING', + 'COUNT', + 'CREATE', + 'CROSS', + 'CSTRING', + 'CURRENT', + 'CURRENT_CONNECTION', + 'CURRENT_DATE', + 'CURRENT_ROLE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_TRANSACTION', + 'CURRENT_USER', + 'DATABASE', + 'DATE', + 'DAY', + 'DB_KEY', + 'DEALLOCATE', + 'DEBUG', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DEFERRABLE', + 'DEFERRED', + 'DELETE', + 'DELETING', + 'DESC', + 'DESCENDING', + 'DESCRIBE', + 'DESCRIPTOR', + 'DIAGNOSTICS', + 'DIFFERENCE', + 'DISCONNECT', + 'DISPLAY', + 'DISTINCT', + 'DO', + 'DOMAIN', + 'DOUBLE', + 'DROP', + 'ECHO', + 'EDIT', + 'ELSE', + 'END', + 'END-EXEC', + 'ENTRY_POINT', + 'ESCAPE', + 'EVENT', + 'EXCEPT', + 'EXCEPTION', + 'EXEC', + 'EXECUTE', + 'EXISTS', + 'EXIT', + 'EXTERN', + 'EXTERNAL', + 'EXTRACT', + 'FALSE', + 'FETCH', + 'FILE', + 'FILTER', + 'FIRST', + 'FLOAT', + 'FOR', + 'FOREIGN', + 'FOUND', + 'FREE_IT', + 'FROM', + 'FULL', + 'FUNCTION', + 'GDSCODE', + 'GEN_ID', + 'GENERATOR', + 'GET', + 'GLOBAL', + 'GO', + 'GOTO', + 'GRANT', + 'GROUP', + 'GROUP_COMMIT_WAIT', + 'GROUP_COMMIT_WAIT_TIME', + 'HAVING', + 'HELP', + 'HOUR', + 'IDENTITY', + 'IF', + 'IIF', + 'IMMEDIATE', + 'IN', + 'INACTIVE', + 'INDEX', + 'INDICATOR', + 'INIT', + 'INITIALLY', + 'INNER', + 'INPUT', + 'INPUT_TYPE', + 'INSENSITIVE', + 'INSERT', + 'INSERTING', + 'INT', + 'INTEGER', + 'INTERSECT', + 'INTERVAL', + 'INTO', + 'IS', + 'ISOLATION', + 'ISQL', + 'JOIN', + 'KEY', + 'LANGUAGE', + 'LAST', + 'LC_MESSAGES', + 'LC_TYPE', + 'LEADING', + 'LEADING', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LENGTH', + 'LEV', + 'LEVEL', + 'LIKE', + 'LOCAL', + 'LOCK', + 'LOG_BUF_SIZE', + 'LOG_BUFFER_SIZE', + 'LOGFILE', + 'LONG', + 'LOWER', + 'MANUAL', + 'MATCH', + 'MAX', + 'MAX_SEGMENT', + 'MAXIMUM', + 'MAXIMUM_SEGMENT', + 'MERGE', + 'MESSAGE', + 'MIN', + 'MINIMUM', + 'MINUTE', + 'MODULE', + 'MODULE_NAME', + 'MONTH', + 'NAMES', + 'NATIONAL', + 'NATURAL', + 'NCHAR', + 'NEXT', + 'NO', + 'NOAUTO', + 'NOT', + 'NULL', + 'NULLIF', + 'NULLS', + 'NUM_LOG_BUFFERS', + 'NUM_LOG_BUFS', + 'NUMERIC', + 'OCTET_LENGTH', + 'OF', + 'ON', + 'ONLY', + 'OPEN', + 'OPTION', + 'OR', + 'ORDER', + 'OUTER', + 'OUTPUT', + 'OUTPUT_TYPE', + 'OVERFLOW', + 'OVERLAPS', + 'PAD', + 'PAGE', + 'PAGE_SIZE', + 'PAGELENGTH', + 'PAGES', + 'PARAMETER', + 'PARTIAL', + 'PASSWORD', + 'PERCENT', + 'PLAN', + 'POSITION', + 'POST_EVENT', + 'PRECISION', + 'PREPARE', + 'PRESERVE', + 'PRIMARY', + 'PRIOR', + 'PRIVILEGES', + 'PROCEDURE', + 'PUBLIC', + 'QUIT', + 'RAW_PARTITIONS', + 'RDB$DB_KEY', + 'READ', + 'REAL', + 'RECORD_VERSION', + 'RECREATE', + 'RECREATE ROW_COUNT', + 'REFERENCES', + 'RELATIVE', + 'RELEASE', + 'RESERV', + 'RESERVING', + 'RESTART', + 'RESTRICT', + 'RETAIN', + 'RETURN', + 'RETURNING', + 'RETURNING_VALUES', + 'RETURNS', + 'REVOKE', + 'RIGHT', + 'ROLE', + 'ROLLBACK', + 'ROW_COUNT', + 'ROWS', + 'RUNTIME', + 'SAVEPOINT', + 'SCALAR_ARRAY', + 'SCHEMA', + 'SCROLL', + 'SECOND', + 'SECTION', + 'SELECT', + 'SEQUENCE', + 'SESSION', + 'SESSION_USER', + 'SET', + 'SHADOW', + 'SHARED', + 'SHELL', + 'SHOW', + 'SINGULAR', + 'SIZE', + 'SKIP', + 'SMALLINT', + 'SNAPSHOT', + 'SOME', + 'SORT', + 'SPACE', + 'SQL', + 'SQLCODE', + 'SQLERROR', + 'SQLSTATE', + 'SQLWARNING', + 'STABILITY', + 'STARTING', + 'STARTS', + 'STATEMENT', + 'STATIC', + 'STATISTICS', + 'SUB_TYPE', + 'SUBSTRING', + 'SUM', + 'SUSPEND', + 'SYSTEM_USER', + 'TABLE', + 'TEMPORARY', + 'TERMINATOR', + 'THEN', + 'TIES', + 'TIME', + 'TIMESTAMP', + 'TIMEZONE_HOUR', + 'TIMEZONE_MINUTE', + 'TO', + 'TRAILING', + 'TRANSACTION', + 'TRANSLATE', + 'TRANSLATION', + 'TRIGGER', + 'TRIM', + 'TRUE', + 'TYPE', + 'UNCOMMITTED', + 'UNION', + 'UNIQUE', + 'UNKNOWN', + 'UPDATE', + 'UPDATING', + 'UPPER', + 'USAGE', + 'USER', + 'USING', + 'VALUE', + 'VALUES', + 'VARCHAR', + 'VARIABLE', + 'VARYING', + 'VERSION', + 'VIEW', + 'WAIT', + 'WEEKDAY', + 'WHEN', + 'WHENEVER', + 'WHERE', + 'WHILE', + 'WITH', + 'WORK', + 'WRITE', + 'YEAR', + 'YEARDAY', + 'ZONE', +); +// }}} +?>
\ No newline at end of file diff --git a/inc/MDB2/Schema/Reserved/mssql.php b/inc/MDB2/Schema/Reserved/mssql.php new file mode 100644 index 00000000000..74ac6885780 --- /dev/null +++ b/inc/MDB2/Schema/Reserved/mssql.php @@ -0,0 +1,258 @@ +<?php +// {{{ Disclaimer, Licence, copyrights +// +----------------------------------------------------------------------+ +// | PHP versions 4 and 5 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Stig. S. Bakken, Lukas Smith | +// | All rights reserved. | +// +----------------------------------------------------------------------+ +// | MDB2 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: David Coallier <davidc@php.net> | +// +----------------------------------------------------------------------+ +// }}} +// {{{ $GLOBALS['_MDB2_Schema_Reserved']['mssql'] +/** + * Has a list of all the reserved words for mssql. + * + * @package MDB2_Schema + * @category Database + * @access protected + * @author David Coallier <davidc@php.net> + */ +$GLOBALS['_MDB2_Schema_Reserved']['mssql'] = array( + 'ADD', + 'CURRENT_TIMESTAMP', + 'GROUP', + 'OPENQUERY', + 'SERIALIZABLE', + 'ALL', + 'CURRENT_USER', + 'HAVING', + 'OPENROWSET', + 'SESSION_USER', + 'ALTER', + 'CURSOR', + 'HOLDLOCK', + 'OPTION', + 'SET', + 'AND', + 'DATABASE', + 'IDENTITY', + 'OR', + 'SETUSER', + 'ANY', + 'DBCC', + 'IDENTITYCOL', + 'ORDER', + 'SHUTDOWN', + 'AS', + 'DEALLOCATE', + 'IDENTITY_INSERT', + 'OUTER', + 'SOME', + 'ASC', + 'DECLARE', + 'IF', + 'OVER', + 'STATISTICS', + 'AUTHORIZATION', + 'DEFAULT', + 'IN', + 'PERCENT', + 'SUM', + 'AVG', + 'DELETE', + 'INDEX', + 'PERM', + 'SYSTEM_USER', + 'BACKUP', + 'DENY', + 'INNER', + 'PERMANENT', + 'TABLE', + 'BEGIN', + 'DESC', + 'INSERT', + 'PIPE', + 'TAPE', + 'BETWEEN', + 'DISK', + 'INTERSECT', + 'PLAN', + 'TEMP', + 'BREAK', + 'DISTINCT', + 'INTO', + 'PRECISION', + 'TEMPORARY', + 'BROWSE', + 'DISTRIBUTED', + 'IS', + 'PREPARE', + 'TEXTSIZE', + 'BULK', + 'DOUBLE', + 'ISOLATION', + 'PRIMARY', + 'THEN', + 'BY', + 'DROP', + 'JOIN', + 'PRINT', + 'TO', + 'CASCADE', + 'DUMMY', + 'KEY', + 'PRIVILEGES', + 'TOP', + 'CASE', + 'DUMP', + 'KILL', + 'PROC', + 'TRAN', + 'CHECK', + 'ELSE', + 'LEFT', + 'PROCEDURE', + 'TRANSACTION', + 'CHECKPOINT', + 'END', + 'LEVEL', + 'PROCESSEXIT', + 'TRIGGER', + 'CLOSE', + 'ERRLVL', + 'LIKE', + 'PUBLIC', + 'TRUNCATE', + 'CLUSTERED', + 'ERROREXIT', + 'LINENO', + 'RAISERROR', + 'TSEQUAL', + 'COALESCE', + 'ESCAPE', + 'LOAD', + 'READ', + 'UNCOMMITTED', + 'COLUMN', + 'EXCEPT', + 'MAX', + 'READTEXT', + 'UNION', + 'COMMIT', + 'EXEC', + 'MIN', + 'RECONFIGURE', + 'UNIQUE', + 'COMMITTED', + 'EXECUTE', + 'MIRROREXIT', + 'REFERENCES', + 'UPDATE', + 'COMPUTE', + 'EXISTS', + 'NATIONAL', + 'REPEATABLE', + 'UPDATETEXT', + 'CONFIRM', + 'EXIT', + 'NOCHECK', + 'REPLICATION', + 'USE', + 'CONSTRAINT', + 'FETCH', + 'NONCLUSTERED', + 'RESTORE', + 'USER', + 'CONTAINS', + 'FILE', + 'NOT', + 'RESTRICT', + 'VALUES', + 'CONTAINSTABLE', + 'FILLFACTOR', + 'NULL', + 'RETURN', + 'VARYING', + 'CONTINUE', + 'FLOPPY', + 'NULLIF', + 'REVOKE', + 'VIEW', + 'CONTROLROW', + 'FOR', + 'OF', + 'RIGHT', + 'WAITFOR', + 'CONVERT', + 'FOREIGN', + 'OFF', + 'ROLLBACK', + 'WHEN', + 'COUNT', + 'FREETEXT', + 'OFFSETS', + 'ROWCOUNT', + 'WHERE', + 'CREATE', + 'FREETEXTTABLE', + 'ON', + 'ROWGUIDCOL', + 'WHILE', + 'CROSS', + 'FROM', + 'ONCE', + 'RULE', + 'WITH', + 'CURRENT', + 'FULL', + 'ONLY', + 'SAVE', + 'WORK', + 'CURRENT_DATE', + 'GOTO', + 'OPEN', + 'SCHEMA', + 'WRITETEXT', + 'CURRENT_TIME', + 'GRANT', + 'OPENDATASOURCE', + 'SELECT', +); +//}}} + +?> diff --git a/inc/MDB2/Schema/Reserved/mysql.php b/inc/MDB2/Schema/Reserved/mysql.php new file mode 100644 index 00000000000..4f0575e0bb1 --- /dev/null +++ b/inc/MDB2/Schema/Reserved/mysql.php @@ -0,0 +1,284 @@ +<?php +// {{{ Disclaimer, Licence, copyrights +// +----------------------------------------------------------------------+ +// | PHP versions 4 and 5 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Stig. S. Bakken, Lukas Smith | +// | All rights reserved. | +// +----------------------------------------------------------------------+ +// | MDB2 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: David Coallier <davidc@php.net> | +// +----------------------------------------------------------------------+ +// +// $Id: mysql.php,v 1.3 2006/03/01 12:16:40 lsmith Exp $ +// }}} +// {{{ $GLOBALS['_MDB2_Schema_Reserved']['mysql'] +/** + * Has a list of reserved words of mysql + * + * @package MDB2_Schema + * @category Database + * @access protected + * @author David Coalier <davidc@php.net> + */ +$GLOBALS['_MDB2_Schema_Reserved']['mysql'] = array( + 'ADD', + 'ALL', + 'ALTER', + 'ANALYZE', + 'AND', + 'AS', + 'ASC', + 'ASENSITIVE', + 'BEFORE', + 'BETWEEN', + 'BIGINT', + 'BINARY', + 'BLOB', + 'BOTH', + 'BY', + 'CALL', + 'CASCADE', + 'CASE', + 'CHANGE', + 'CHAR', + 'CHARACTER', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONDITION', + 'CONNECTION', + 'CONSTRAINT', + 'CONTINUE', + 'CONVERT', + 'CREATE', + 'CROSS', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURSOR', + 'DATABASE', + 'DATABASES', + 'DAY_HOUR', + 'DAY_MICROSECOND', + 'DAY_MINUTE', + 'DAY_SECOND', + 'DEC', + 'DECIMAL', + 'DECLARE', + 'DEFAULT', + 'DELAYED', + 'DELETE', + 'DESC', + 'DESCRIBE', + 'DETERMINISTIC', + 'DISTINCT', + 'DISTINCTROW', + 'DIV', + 'DOUBLE', + 'DROP', + 'DUAL', + 'EACH', + 'ELSE', + 'ELSEIF', + 'ENCLOSED', + 'ESCAPED', + 'EXISTS', + 'EXIT', + 'EXPLAIN', + 'FALSE', + 'FETCH', + 'FLOAT', + 'FLOAT4', + 'FLOAT8', + 'FOR', + 'FORCE', + 'FOREIGN', + 'FROM', + 'FULLTEXT', + 'GOTO', + 'GRANT', + 'GROUP', + 'HAVING', + 'HIGH_PRIORITY', + 'HOUR_MICROSECOND', + 'HOUR_MINUTE', + 'HOUR_SECOND', + 'IF', + 'IGNORE', + 'IN', + 'INDEX', + 'INFILE', + 'INNER', + 'INOUT', + 'INSENSITIVE', + 'INSERT', + 'INT', + 'INT1', + 'INT2', + 'INT3', + 'INT4', + 'INT8', + 'INTEGER', + 'INTERVAL', + 'INTO', + 'IS', + 'ITERATE', + 'JOIN', + 'KEY', + 'KEYS', + 'KILL', + 'LABEL', + 'LEADING', + 'LEAVE', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LINES', + 'LOAD', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCK', + 'LONG', + 'LONGBLOB', + 'LONGTEXT', + 'LOOP', + 'LOW_PRIORITY', + 'MATCH', + 'MEDIUMBLOB', + 'MEDIUMINT', + 'MEDIUMTEXT', + 'MIDDLEINT', + 'MINUTE_MICROSECOND', + 'MINUTE_SECOND', + 'MOD', + 'MODIFIES', + 'NATURAL', + 'NOT', + 'NO_WRITE_TO_BINLOG', + 'NULL', + 'NUMERIC', + 'ON', + 'OPTIMIZE', + 'OPTION', + 'OPTIONALLY', + 'OR', + 'ORDER', + 'OUT', + 'OUTER', + 'OUTFILE', + 'PRECISION', + 'PRIMARY', + 'PROCEDURE', + 'PURGE', + 'RAID0', + 'READ', + 'READS', + 'REAL', + 'REFERENCES', + 'REGEXP', + 'RELEASE', + 'RENAME', + 'REPEAT', + 'REPLACE', + 'REQUIRE', + 'RESTRICT', + 'RETURN', + 'REVOKE', + 'RIGHT', + 'RLIKE', + 'SCHEMA', + 'SCHEMAS', + 'SECOND_MICROSECOND', + 'SELECT', + 'SENSITIVE', + 'SEPARATOR', + 'SET', + 'SHOW', + 'SMALLINT', + 'SONAME', + 'SPATIAL', + 'SPECIFIC', + 'SQL', + 'SQLEXCEPTION', + 'SQLSTATE', + 'SQLWARNING', + 'SQL_BIG_RESULT', + 'SQL_CALC_FOUND_ROWS', + 'SQL_SMALL_RESULT', + 'SSL', + 'STARTING', + 'STRAIGHT_JOIN', + 'TABLE', + 'TERMINATED', + 'THEN', + 'TINYBLOB', + 'TINYINT', + 'TINYTEXT', + 'TO', + 'TRAILING', + 'TRIGGER', + 'TRUE', + 'UNDO', + 'UNION', + 'UNIQUE', + 'UNLOCK', + 'UNSIGNED', + 'UPDATE', + 'USAGE', + 'USE', + 'USING', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'VALUES', + 'VARBINARY', + 'VARCHAR', + 'VARCHARACTER', + 'VARYING', + 'WHEN', + 'WHERE', + 'WHILE', + 'WITH', + 'WRITE', + 'X509', + 'XOR', + 'YEAR_MONTH', + 'ZEROFILL', + ); + // }}} +?> diff --git a/inc/MDB2/Schema/Reserved/oci8.php b/inc/MDB2/Schema/Reserved/oci8.php new file mode 100644 index 00000000000..57fe12ddcab --- /dev/null +++ b/inc/MDB2/Schema/Reserved/oci8.php @@ -0,0 +1,171 @@ +<?php +// {{{ Disclaimer, Licence, copyrights +// +----------------------------------------------------------------------+ +// | PHP versions 4 and 5 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Stig. S. Bakken, Lukas Smith | +// | All rights reserved. | +// +----------------------------------------------------------------------+ +// | MDB2 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: David Coallier <davidc@php.net> | +// +----------------------------------------------------------------------+ +// }}} +// {{{ $GLOBALS['_MDB2_Schema_Reserved']['oci8'] +/** + * Has a list of all the reserved words for oracle. + * + * @package MDB2_Schema + * @category Database + * @access protected + * @author David Coallier <davidc@php.net> + */ +$GLOBALS['_MDB2_Schema_Reserved']['oci8'] = array( + 'ACCESS', + 'ELSE', + 'MODIFY', + 'START', + 'ADD', + 'EXCLUSIVE', + 'NOAUDIT', + 'SELECT', + 'ALL', + 'EXISTS', + 'NOCOMPRESS', + 'SESSION', + 'ALTER', + 'FILE', + 'NOT', + 'SET', + 'AND', + 'FLOAT', + 'NOTFOUND ', + 'SHARE', + 'ANY', + 'FOR', + 'NOWAIT', + 'SIZE', + 'ARRAYLEN', + 'FROM', + 'NULL', + 'SMALLINT', + 'AS', + 'GRANT', + 'NUMBER', + 'SQLBUF', + 'ASC', + 'GROUP', + 'OF', + 'SUCCESSFUL', + 'AUDIT', + 'HAVING', + 'OFFLINE ', + 'SYNONYM', + 'BETWEEN', + 'IDENTIFIED', + 'ON', + 'SYSDATE', + 'BY', + 'IMMEDIATE', + 'ONLINE', + 'TABLE', + 'CHAR', + 'IN', + 'OPTION', + 'THEN', + 'CHECK', + 'INCREMENT', + 'OR', + 'TO', + 'CLUSTER', + 'INDEX', + 'ORDER', + 'TRIGGER', + 'COLUMN', + 'INITIAL', + 'PCTFREE', + 'UID', + 'COMMENT', + 'INSERT', + 'PRIOR', + 'UNION', + 'COMPRESS', + 'INTEGER', + 'PRIVILEGES', + 'UNIQUE', + 'CONNECT', + 'INTERSECT', + 'PUBLIC', + 'UPDATE', + 'CREATE', + 'INTO', + 'RAW', + 'USER', + 'CURRENT', + 'IS', + 'RENAME', + 'VALIDATE', + 'DATE', + 'LEVEL', + 'RESOURCE', + 'VALUES', + 'DECIMAL', + 'LIKE', + 'REVOKE', + 'VARCHAR', + 'DEFAULT', + 'LOCK', + 'ROW', + 'VARCHAR2', + 'DELETE', + 'LONG', + 'ROWID', + 'VIEW', + 'DESC', + 'MAXEXTENTS', + 'ROWLABEL', + 'WHENEVER', + 'DISTINCT', + 'MINUS', + 'ROWNUM', + 'WHERE', + 'DROP', + 'MODE', + 'ROWS', + 'WITH', +); +// }}} + +?> diff --git a/inc/MDB2/Schema/Reserved/pgsql.php b/inc/MDB2/Schema/Reserved/pgsql.php new file mode 100644 index 00000000000..d358e9c12f0 --- /dev/null +++ b/inc/MDB2/Schema/Reserved/pgsql.php @@ -0,0 +1,147 @@ +<?php +// {{{ Disclaimer, Licence, copyrights +// +----------------------------------------------------------------------+ +// | PHP versions 4 and 5 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | +// | Stig. S. Bakken, Lukas Smith | +// | All rights reserved. | +// +----------------------------------------------------------------------+ +// | MDB2 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: Marcelo Santos Araujo <msaraujo@php.net> | +// +----------------------------------------------------------------------+ +// +// }}} +// {{{ $GLOBALS['_MDB2_Schema_Reserved']['pgsql'] +/** + * Has a list of reserved words of pgsql + * + * @package MDB2_Schema + * @category Database + * @access protected + * @author Marcelo Santos Araujo <msaraujo@php.net> + */ +$GLOBALS['_MDB2_Schema_Reserved']['pgsql'] = array( + 'ALL', + 'ANALYSE', + 'ANALYZE', + 'AND', + 'ANY', + 'AS', + 'ASC', + 'AUTHORIZATION', + 'BETWEEN', + 'BINARY', + 'BOTH', + 'CASE', + 'CAST', + 'CHECK', + 'COLLATE', + 'COLUMN', + 'CONSTRAINT', + 'CREATE', + 'CURRENT_DATE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'DEFAULT', + 'DEFERRABLE', + 'DESC', + 'DISTINCT', + 'DO', + 'ELSE', + 'END', + 'EXCEPT', + 'FALSE', + 'FOR', + 'FOREIGN', + 'FREEZE', + 'FROM', + 'FULL', + 'GRANT', + 'GROUP', + 'HAVING', + 'ILIKE', + 'IN', + 'INITIALLY', + 'INNER', + 'INTERSECT', + 'INTO', + 'IS', + 'ISNULL', + 'JOIN', + 'LEADING', + 'LEFT', + 'LIKE', + 'LIMIT', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'NATURAL', + 'NEW', + 'NOT', + 'NOTNULL', + 'NULL', + 'OFF', + 'OFFSET', + 'OLD', + 'ON', + 'ONLY', + 'OR', + 'ORDER', + 'OUTER', + 'OVERLAPS', + 'PLACING', + 'PRIMARY', + 'REFERENCES', + 'SELECT', + 'SESSION_USER', + 'SIMILAR', + 'SOME', + 'TABLE', + 'THEN', + 'TO', + 'TRAILING', + 'TRUE', + 'UNION', + 'UNIQUE', + 'USER', + 'USING', + 'VERBOSE', + 'WHEN', + 'WHERE' +); +// }}} +?> + diff --git a/inc/MDB2/Schema/Tool.php b/inc/MDB2/Schema/Tool.php new file mode 100644 index 00000000000..9689a0f6d73 --- /dev/null +++ b/inc/MDB2/Schema/Tool.php @@ -0,0 +1,560 @@ +<?php +/** + * PHP versions 4 and 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: Christian Weiske <cweiske@php.net> + * $Id: Tool.php,v 1.6 2008/12/13 00:26:07 clockwerx Exp $ + * + * @category Database + * @package MDB2_Schema + * @author Christian Weiske <cweiske@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @version CVS: $Id: Tool.php,v 1.6 2008/12/13 00:26:07 clockwerx Exp $ + * @link http://pear.php.net/packages/MDB2_Schema + */ + +require_once 'MDB2/Schema.php'; +require_once 'MDB2/Schema/Tool/ParameterException.php'; + +/** +* Command line tool to work with database schemas +* +* Functionality: +* - dump a database schema to stdout +* - import schema into database +* - create a diff between two schemas +* - apply diff to database +* + * @category Database + * @package MDB2_Schema + * @author Christian Weiske <cweiske@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/packages/MDB2_Schema + */ +class MDB2_Schema_Tool +{ + /** + * Run the schema tool + * + * @param array $args Array of command line arguments + */ + public function __construct($args) + { + $strAction = $this->getAction($args); + try { + $this->{'do' . ucfirst($strAction)}($args); + } catch (MDB2_Schema_Tool_ParameterException $e) { + $this->{'doHelp' . ucfirst($strAction)}($e->getMessage()); + } + }//public function __construct($args) + + + + /** + * Runs the tool with command line arguments + * + * @return void + */ + public static function run() + { + $args = $GLOBALS['argv']; + array_shift($args); + + try { + $tool = new MDB2_Schema_Tool($args); + } catch (Exception $e) { + self::toStdErr($e->getMessage() . "\n"); + } + }//public static function run() + + + + /** + * Reads the first parameter from the argument array and + * returns the action. + * + * @param array &$args Command line parameters + * + * @return string Action to execute + */ + protected function getAction(&$args) + { + if (count($args) == 0) { + return 'help'; + } + $arg = array_shift($args); + switch ($arg) { + case 'h': + case 'help': + case '-h': + case '--help': + return 'help'; + case 'd': + case 'dump': + case '-d': + case '--dump': + return 'dump'; + case 'l': + case 'load': + case '-l': + case '--load': + return 'load'; + case 'i': + case 'diff': + case '-i': + case '--diff': + return 'diff'; + case 'a': + case 'apply': + case '-a': + case '--apply': + return 'apply'; + case 'n': + case 'init': + case '-i': + case '--init': + return 'init'; + default: + throw new MDB2_Schema_Tool_ParameterException("Unknown mode \"$arg\""); + } + }//protected function getAction(&$args) + + + + /** + * Writes the message to stderr + * + * @param string $msg Message to print + * + * @return void + */ + protected static function toStdErr($msg) + { + file_put_contents('php://stderr', $msg); + }//protected static function toStdErr($msg) + + + + /** + * Displays generic help to stdout + * + * @return void + */ + protected function doHelp() + { + self::toStdErr(<<<EOH +Usage: mdb2_schematool mode parameters + +Works with database schemas + +mode: (- and -- are optional) + h, help Show this help screen + d, dump Dump a schema to stdout + l, load Load a schema into database + i, diff Create a diff between two schemas and dump it to stdout + a, apply Apply a diff to a database + n, init Initialize a database with data + +EOH + ); + }//protected function doHelp() + + + + /** + * Displays the help screen for "dump" command + * + * @return void + */ + protected function doHelpDump() + { + self::toStdErr( <<<EOH +Usage: mdb2_schematool dump [all|data|schema] [-p] DSN + +Dumps a database schema to stdout + +If dump type is not specified, defaults to "schema". + +DSN: Data source name in the form of + driver://user:password@host/database + +User and password may be omitted. +Using -p reads password from stdin which is more secure than passing it in the parameter. + +EOH + ); + }//protected function doHelpDump() + + + + /** + * Displays the help screen for "init" command + * + * @return void + */ + protected function doHelpInit() + { + self::toStdErr( <<<EOH +Usage: mdb2_schematool init source [-p] destination + +Initializes a database with data + (Inserts data on a previous created database at destination) + +source should be a schema file containing data, +destination should be a DSN + +DSN: Data source name in the form of + driver://user:password@host/database + +User and password may be omitted. +Using -p reads password from stdin which is more secure than passing it in the parameter. + +EOH + ); + }//protected function doHelpInit() + + + + /** + * Displays the help screen for "load" command + * + * @return void + */ + protected function doHelpLoad() + { + self::toStdErr( <<<EOH +Usage: mdb2_schematool load [-p] source [-p] destination + +Loads a database schema from source to destination + (Creates the database schema at destination) + +source can be a DSN or a schema file, +destination should be a DSN + +DSN: Data source name in the form of + driver://user:password@host/database + +User and password may be omitted. +Using -p reads password from stdin which is more secure than passing it in the parameter. + +EOH + ); + }//protected function doHelpLoad() + + + + /** + * Returns an array of options for MDB2_Schema constructor + * + * @return array Options for MDB2_Schema constructor + */ + protected function getSchemaOptions() + { + $options = array( + 'log_line_break' => '<br>', + 'idxname_format' => '%s', + 'debug' => true, + 'quote_identifier' => true, + 'force_defaults' => false, + 'portability' => true, + 'use_transactions' => false, + ); + return $options; + }//protected function getSchemaOptions() + + + + /** + * Checks if the passed parameter is a PEAR_Error object + * and throws an exception in that case. + * + * @param mixed $object Some variable to check + * @param string $location Where the error occured + * + * @return void + */ + protected function throwExceptionOnError($object, $location = '') + { + if (PEAR::isError($object)) { + //FIXME: exception class + //debug_print_backtrace(); + throw new Exception('Error ' . $location + . "\n" . $object->getMessage() + . "\n" . $object->getUserInfo() + ); + } + }//protected function throwExceptionOnError($object, $location = '') + + + + /** + * Loads a file or a dsn from the arguments + * + * @param array &$args Array of arguments to the program + * + * @return array Array of ('file'|'dsn', $value) + */ + protected function getFileOrDsn(&$args) + { + if (count($args) == 0) { + throw new MDB2_Schema_Tool_ParameterException('File or DSN expected'); + } + + $arg = array_shift($args); + if ($arg == '-p') { + $bAskPassword = true; + $arg = array_shift($args); + } else { + $bAskPassword = false; + } + + if (strpos($arg, '://') === false) { + if (file_exists($arg)) { + //File + return array('file', $arg); + } else { + throw new Exception('Schema file does not exist'); + } + } + + //read password if necessary + if ($bAskPassword) { + $password = $this->readPasswordFromStdin($arg); + $arg = self::setPasswordIntoDsn($arg, $password); + self::toStdErr($arg); + } + return array('dsn', $arg); + }//protected function getFileOrDsn(&$args) + + + + /** + * Takes a DSN data source name and integrates the given + * password into it. + * + * @param string $dsn Data source name + * @param string $password Password + * + * @return string DSN with password + */ + protected function setPasswordIntoDsn($dsn, $password) + { + //simple try to integrate password + if (strpos($dsn, '@') === false) { + //no @ -> no user and no password + return str_replace('://', '://:' . $password . '@', $dsn); + } else if (preg_match('|://[^:]+@|', $dsn)) { + //user only, no password + return str_replace('@', ':' . $password . '@', $dsn); + } else if (strpos($dsn, ':@') !== false) { + //abstract version + return str_replace(':@', ':' . $password . '@', $dsn); + } + + return $dsn; + }//protected function setPasswordIntoDsn($dsn, $password) + + + + /** + * Reads a password from stdin + * + * @param string $dsn DSN name to put into the message + * + * @return string Password + */ + protected function readPasswordFromStdin($dsn) + { + $stdin = fopen('php://stdin', 'r'); + self::toStdErr('Please insert password for ' . $dsn . "\n"); + $password = ''; + $breakme = false; + while (false !== ($char = fgetc($stdin))) { + if (ord($char) == 10 || $char == "\n" || $char == "\r") { + break; + } + $password .= $char; + } + fclose($stdin); + + return trim($password); + }//protected function readPasswordFromStdin() + + + + /** + * Creates a database schema dump and sends it to stdout + * + * @param array $args Command line arguments + * + * @return void + */ + protected function doDump($args) + { + $dump_what = MDB2_SCHEMA_DUMP_STRUCTURE; + $arg = ''; + if (count($args)) { + $arg = $args[0]; + } + + switch (strtolower($arg)) { + case 'all': + $dump_what = MDB2_SCHEMA_DUMP_ALL; + array_shift($args); + break; + case 'data': + $dump_what = MDB2_SCHEMA_DUMP_CONTENT; + array_shift($args); + break; + case 'schema': + array_shift($args); + } + + list($type, $dsn) = $this->getFileOrDsn($args); + if ($type == 'file') { + throw new MDB2_Schema_Tool_ParameterException( + 'Dumping a schema file as a schema file does not make much sense' + ); + } + + $schema = MDB2_Schema::factory($dsn, $this->getSchemaOptions()); + $this->throwExceptionOnError($schema); + + $definition = $schema->getDefinitionFromDatabase(); + $this->throwExceptionOnError($definition); + + + $dump_options = array( + 'output_mode' => 'file', + 'output' => 'php://stdout', + 'end_of_line' => "\r\n" + ); + $op = $schema->dumpDatabase( + $definition, $dump_options, $dump_what + ); + $this->throwExceptionOnError($op); + + $schema->disconnect(); + }//protected function doDump($args) + + + + /** + * Loads a database schema + * + * @param array $args Command line arguments + * + * @return void + */ + protected function doLoad($args) + { + list($typeSource, $dsnSource) = $this->getFileOrDsn($args); + list($typeDest, $dsnDest) = $this->getFileOrDsn($args); + + if ($typeDest == 'file') { + throw new MDB2_Schema_Tool_ParameterException( + 'A schema can only be loaded into a database, not a file' + ); + } + + + $schemaDest = MDB2_Schema::factory($dsnDest, $this->getSchemaOptions()); + $this->throwExceptionOnError($schemaDest); + + //load definition + if ($typeSource == 'file') { + $definition = $schemaDest->parseDatabaseDefinitionFile($dsnSource); + $where = 'loading schema file'; + } else { + $schemaSource = MDB2_Schema::factory($dsnSource, $this->getSchemaOptions()); + $this->throwExceptionOnError($schemaSource, 'connecting to source database'); + + $definition = $schemaSource->getDefinitionFromDatabase(); + $where = 'loading definition from database'; + } + $this->throwExceptionOnError($definition, $where); + + + //create destination database from definition + $simulate = false; + $op = $schemaDest->createDatabase($definition, array(), $simulate); + $this->throwExceptionOnError($op, 'creating the database'); + }//protected function doLoad($args) + + + + /** + * Initializes a database with data + * + * @param array $args Command line arguments + * + * @return void + */ + protected function doInit($args) + { + list($typeSource, $dsnSource) = $this->getFileOrDsn($args); + list($typeDest, $dsnDest) = $this->getFileOrDsn($args); + + if ($typeSource != 'file') { + throw new MDB2_Schema_Tool_ParameterException( + 'Data must come from a source file' + ); + } + + if ($typeDest != 'dsn') { + throw new MDB2_Schema_Tool_ParameterException( + 'A schema can only be loaded into a database, not a file' + ); + } + + $schemaDest = MDB2_Schema::factory($dsnDest, $this->getSchemaOptions()); + $this->throwExceptionOnError($schemaDest, 'connecting to destination database'); + + $definition = $schemaDest->getDefinitionFromDatabase(); + $this->throwExceptionOnError($definition, 'loading definition from database'); + + $op = $schemaDest->writeInitialization($dsnSource, $definition); + $this->throwExceptionOnError($op, 'initializing database'); + }//protected function doInit($args) + + +}//class MDB2_Schema_Tool + +?> diff --git a/inc/MDB2/Schema/Tool/ParameterException.php b/inc/MDB2/Schema/Tool/ParameterException.php new file mode 100644 index 00000000000..fab1e03e250 --- /dev/null +++ b/inc/MDB2/Schema/Tool/ParameterException.php @@ -0,0 +1,6 @@ +<?php + +class MDB2_Schema_Tool_ParameterException extends Exception +{} + +?>
\ No newline at end of file diff --git a/inc/MDB2/Schema/Validate.php b/inc/MDB2/Schema/Validate.php new file mode 100644 index 00000000000..21be024ce9f --- /dev/null +++ b/inc/MDB2/Schema/Validate.php @@ -0,0 +1,922 @@ +<?php +/** + * PHP versions 4 and 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: Christian Dickmann <dickmann@php.net> + * Author: Igor Feghali <ifeghali@php.net> + * + * @category Database + * @package MDB2_Schema + * @author Christian Dickmann <dickmann@php.net> + * @author Igor Feghali <ifeghali@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @version CVS: $Id: Validate.php,v 1.42 2008/11/30 03:34:00 clockwerx Exp $ + * @link http://pear.php.net/packages/MDB2_Schema + */ + +/** + * Validates an XML schema file + * + * @category Database + * @package MDB2_Schema + * @author Igor Feghali <ifeghali@php.net> + * @license BSD http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/packages/MDB2_Schema + */ +class MDB2_Schema_Validate +{ + // {{{ properties + + var $fail_on_invalid_names = true; + + var $valid_types = array(); + + var $force_defaults = true; + + // }}} + // {{{ constructor + + function __construct($fail_on_invalid_names = true, $valid_types = array(), $force_defaults = true) + { + if (empty($GLOBALS['_MDB2_Schema_Reserved'])) { + $GLOBALS['_MDB2_Schema_Reserved'] = array(); + } + + if (is_array($fail_on_invalid_names)) { + $this->fail_on_invalid_names = array_intersect($fail_on_invalid_names, + array_keys($GLOBALS['_MDB2_Schema_Reserved'])); + } elseif ($fail_on_invalid_names === true) { + $this->fail_on_invalid_names = array_keys($GLOBALS['_MDB2_Schema_Reserved']); + } else { + $this->fail_on_invalid_names = array(); + } + $this->valid_types = $valid_types; + $this->force_defaults = $force_defaults; + } + + function MDB2_Schema_Validate($fail_on_invalid_names = true, $valid_types = array(), $force_defaults = true) + { + $this->__construct($fail_on_invalid_names, $valid_types, $force_defaults); + } + + // }}} + // {{{ raiseError() + + function &raiseError($ecode, $msg = null) + { + $error =& MDB2_Schema::raiseError($ecode, null, null, $msg); + return $error; + } + + // }}} + // {{{ isBoolean() + + /** + * Verifies if a given value can be considered boolean. If yes, set value + * to true or false according to its actual contents and return true. If + * not, keep its contents untouched and return false. + * + * @param mixed &$value value to be checked + * + * @return bool + * + * @access public + * @static + */ + function isBoolean(&$value) + { + if (is_bool($value)) { + return true; + } + + if ($value === 0 || $value === 1 || $value === '') { + $value = (bool)$value; + return true; + } + + if (!is_string($value)) { + return false; + } + + switch ($value) { + case '0': + case 'N': + case 'n': + case 'no': + case 'false': + $value = false; + break; + case '1': + case 'Y': + case 'y': + case 'yes': + case 'true': + $value = true; + break; + default: + return false; + } + return true; + } + + // }}} + // {{{ validateTable() + + /* Definition */ + /** + * Checks whether the definition of a parsed table is valid. Modify table + * definition when necessary. + * + * @param array $tables multi dimensional array that contains the + * tables of current database. + * @param array &$table multi dimensional array that contains the + * structure and optional data of the table. + * @param string $table_name name of the parsed table + * + * @return bool|error object + * + * @access public + */ + function validateTable($tables, &$table, $table_name) + { + /* Have we got a name? */ + if (!$table_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'a table has to have a name'); + } + + /* Table name duplicated? */ + if (is_array($tables) && isset($tables[$table_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'table "'.$table_name.'" already exists'); + } + + /* Table name reserved? */ + if (is_array($this->fail_on_invalid_names)) { + $name = strtoupper($table_name); + foreach ($this->fail_on_invalid_names as $rdbms) { + if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'table name "'.$table_name.'" is a reserved word in: '.$rdbms); + } + } + } + + /* Was */ + if (empty($table['was'])) { + $table['was'] = $table_name; + } + + /* Have we got fields? */ + if (empty($table['fields']) || !is_array($table['fields'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'tables need one or more fields'); + } + + /* Autoincrement */ + $autoinc = $primary = false; + foreach ($table['fields'] as $field_name => $field) { + if (!empty($field['autoincrement'])) { + if ($autoinc) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'there was already an autoincrement field in "'.$table_name.'" before "'.$field_name.'"'); + } + $autoinc = $field_name; + } + } + + /* + * Checking Indexes + * this have to be done here otherwise we can't + * guarantee that all table fields were already + * defined in the moment we are parsing indexes + */ + if (!empty($table['indexes']) && is_array($table['indexes'])) { + foreach ($table['indexes'] as $name => $index) { + $skip_index = false; + if (!empty($index['primary'])) { + /* + * Lets see if we should skip this index since there is + * already an auto increment on this field this implying + * a primary key index. + */ + if (count($index['fields']) == '1' + && $autoinc + && array_key_exists($autoinc, $index['fields'])) { + $skip_index = true; + } elseif ($autoinc || $primary) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'there was already an primary index or autoincrement field in "'.$table_name.'" before "'.$name.'"'); + } else { + $primary = true; + } + } + + if (!$skip_index && is_array($index['fields'])) { + foreach ($index['fields'] as $field_name => $field) { + if (!isset($table['fields'][$field_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'index field "'.$field_name.'" does not exist'); + } + if (!empty($index['primary']) + && !$table['fields'][$field_name]['notnull'] + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'all primary key fields must be defined notnull in "'.$table_name.'"'); + } + } + } else { + unset($table['indexes'][$name]); + } + } + } + return MDB2_OK; + } + + // }}} + // {{{ validateField() + + /** + * Checks whether the definition of a parsed field is valid. Modify field + * definition when necessary. + * + * @param array $fields multi dimensional array that contains the + * fields of current table. + * @param array &$field multi dimensional array that contains the + * structure of the parsed field. + * @param string $field_name name of the parsed field + * + * @return bool|error object + * + * @access public + */ + function validateField($fields, &$field, $field_name) + { + /* Have we got a name? */ + if (!$field_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field name missing'); + } + + /* Field name duplicated? */ + if (is_array($fields) && isset($fields[$field_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "'.$field_name.'" already exists'); + } + + /* Field name reserverd? */ + if (is_array($this->fail_on_invalid_names)) { + $name = strtoupper($field_name); + foreach ($this->fail_on_invalid_names as $rdbms) { + if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field name "'.$field_name.'" is a reserved word in: '.$rdbms); + } + } + } + + /* Type check */ + if (empty($field['type'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'no field type specified'); + } + if (!empty($this->valid_types) && !array_key_exists($field['type'], $this->valid_types)) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'no valid field type ("'.$field['type'].'") specified'); + } + + /* Unsigned */ + if (array_key_exists('unsigned', $field) && !$this->isBoolean($field['unsigned'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'unsigned has to be a boolean value'); + } + + /* Fixed */ + if (array_key_exists('fixed', $field) && !$this->isBoolean($field['fixed'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'fixed has to be a boolean value'); + } + + /* Length */ + if (array_key_exists('length', $field) && $field['length'] <= 0) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'length has to be an integer greater 0'); + } + + // if it's a DECIMAL datatype, check if a 'scale' value is provided: + // <length>8,4</length> should be translated to DECIMAL(8,4) + if (is_float($this->valid_types[$field['type']]) + && !empty($field['length']) + && strpos($field['length'], ',') !== false + ) { + list($field['length'], $field['scale']) = explode(',', $field['length']); + } + + /* Was */ + if (empty($field['was'])) { + $field['was'] = $field_name; + } + + /* Notnull */ + if (empty($field['notnull'])) { + $field['notnull'] = false; + } + if (!$this->isBoolean($field['notnull'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "notnull" has to be a boolean value'); + } + + /* Default */ + if ($this->force_defaults + && !array_key_exists('default', $field) + && $field['type'] != 'clob' && $field['type'] != 'blob' + ) { + $field['default'] = $this->valid_types[$field['type']]; + } + if (array_key_exists('default', $field)) { + if ($field['type'] == 'clob' || $field['type'] == 'blob') { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field['type'].'"-fields are not allowed to have a default value'); + } + if ($field['default'] === '' && !$field['notnull']) { + $field['default'] = null; + } + } + if (isset($field['default']) + && PEAR::isError($result = $this->validateDataFieldValue($field, $field['default'], $field_name)) + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'default value of "'.$field_name.'" is incorrect: '.$result->getUserinfo()); + } + + /* Autoincrement */ + if (!empty($field['autoincrement'])) { + if (!$field['notnull']) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'all autoincrement fields must be defined notnull'); + } + + if (empty($field['default'])) { + $field['default'] = '0'; + } elseif ($field['default'] !== '0' && $field['default'] !== 0) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'all autoincrement fields must be defined default "0"'); + } + } + return MDB2_OK; + } + + // }}} + // {{{ validateIndex() + + /** + * Checks whether a parsed index is valid. Modify index definition when + * necessary. + * + * @param array $table_indexes multi dimensional array that contains the + * indexes of current table. + * @param array &$index multi dimensional array that contains the + * structure of the parsed index. + * @param string $index_name name of the parsed index + * + * @return bool|error object + * + * @access public + */ + function validateIndex($table_indexes, &$index, $index_name) + { + if (!$index_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'an index has to have a name'); + } + if (is_array($table_indexes) && isset($table_indexes[$index_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'index "'.$index_name.'" already exists'); + } + if (array_key_exists('unique', $index) && !$this->isBoolean($index['unique'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "unique" has to be a boolean value'); + } + if (array_key_exists('primary', $index) && !$this->isBoolean($index['primary'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "primary" has to be a boolean value'); + } + + /* Have we got fields? */ + if (empty($index['fields']) || !is_array($index['fields'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'indexes need one or more fields'); + } + + if (empty($index['was'])) { + $index['was'] = $index_name; + } + return MDB2_OK; + } + + // }}} + // {{{ validateIndexField() + + /** + * Checks whether a parsed index-field is valid. Modify its definition when + * necessary. + * + * @param array $index_fields multi dimensional array that contains the + * fields of current index. + * @param array &$field multi dimensional array that contains the + * structure of the parsed index-field. + * @param string $field_name name of the parsed index-field + * + * @return bool|error object + * + * @access public + */ + function validateIndexField($index_fields, &$field, $field_name) + { + if (is_array($index_fields) && isset($index_fields[$field_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'index field "'.$field_name.'" already exists'); + } + if (!$field_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'the index-field-name is required'); + } + if (empty($field['sorting'])) { + $field['sorting'] = 'ascending'; + } elseif ($field['sorting'] !== 'ascending' && $field['sorting'] !== 'descending') { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'sorting type unknown'); + } + return MDB2_OK; + } + + // }}} + // {{{ validateConstraint() + + /** + * Checks whether a parsed foreign key is valid. Modify its definition when + * necessary. + * + * @param array $table_constraints multi dimensional array that contains the + * constraints of current table. + * @param array &$constraint multi dimensional array that contains the + * structure of the parsed foreign key. + * @param string $constraint_name name of the parsed foreign key + * + * @return bool|error object + * + * @access public + */ + function validateConstraint($table_constraints, &$constraint, $constraint_name) + { + if (!$constraint_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'a foreign key has to have a name'); + } + if (is_array($table_constraints) && isset($table_constraints[$constraint_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'foreign key "'.$constraint_name.'" already exists'); + } + + /* Have we got fields? */ + if (empty($constraint['fields']) || !is_array($constraint['fields'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'foreign key "'.$constraint_name.'" need one or more fields'); + } + + /* Have we got referenced fields? */ + if (empty($constraint['references']) || !is_array($constraint['references'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'foreign key "'.$constraint_name.'" need to reference one or more fields'); + } + + /* Have we got referenced table? */ + if (empty($constraint['references']['table'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'foreign key "'.$constraint_name.'" need to reference a table'); + } + + if (empty($constraint['was'])) { + $constraint['was'] = $constraint_name; + } + return MDB2_OK; + } + + // }}} + // {{{ validateConstraintField() + + /** + * Checks whether a foreign-field is valid. + * + * @param array $constraint_fields multi dimensional array that contains the + * fields of current foreign key. + * @param string $field_name name of the parsed foreign-field + * + * @return bool|error object + * + * @access public + */ + function validateConstraintField($constraint_fields, $field_name) + { + if (!$field_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'empty value for foreign-field'); + } + if (is_array($constraint_fields) && isset($constraint_fields[$field_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'foreign field "'.$field_name.'" already exists'); + } + return MDB2_OK; + } + + // }}} + // {{{ validateConstraintReferencedField() + + /** + * Checks whether a foreign-referenced field is valid. + * + * @param array $referenced_fields multi dimensional array that contains the + * fields of current foreign key. + * @param string $field_name name of the parsed foreign-field + * + * @return bool|error object + * + * @access public + */ + function validateConstraintReferencedField($referenced_fields, $field_name) + { + if (!$field_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'empty value for referenced foreign-field'); + } + if (is_array($referenced_fields) && isset($referenced_fields[$field_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'foreign field "'.$field_name.'" already referenced'); + } + return MDB2_OK; + } + + // }}} + // {{{ validateSequence() + + /** + * Checks whether the definition of a parsed sequence is valid. Modify + * sequence definition when necessary. + * + * @param array $sequences multi dimensional array that contains the + * sequences of current database. + * @param array &$sequence multi dimensional array that contains the + * structure of the parsed sequence. + * @param string $sequence_name name of the parsed sequence + * + * @return bool|error object + * + * @access public + */ + function validateSequence($sequences, &$sequence, $sequence_name) + { + if (!$sequence_name) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'a sequence has to have a name'); + } + + if (is_array($sequences) && isset($sequences[$sequence_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'sequence "'.$sequence_name.'" already exists'); + } + + if (is_array($this->fail_on_invalid_names)) { + $name = strtoupper($sequence_name); + foreach ($this->fail_on_invalid_names as $rdbms) { + if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'sequence name "'.$sequence_name.'" is a reserved word in: '.$rdbms); + } + } + } + + if (empty($sequence['was'])) { + $sequence['was'] = $sequence_name; + } + + if (!empty($sequence['on']) + && (empty($sequence['on']['table']) || empty($sequence['on']['field'])) + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'sequence "'.$sequence_name.'" on a table was not properly defined'); + } + return MDB2_OK; + } + + // }}} + // {{{ validateDatabase() + + /** + * Checks whether a parsed database is valid. Modify its structure and + * data when necessary. + * + * @param array &$database multi dimensional array that contains the + * structure and optional data of the database. + * + * @return bool|error object + * + * @access public + */ + function validateDatabase(&$database) + { + /* Have we got a name? */ + if (!is_array($database) || !isset($database['name']) || !$database['name']) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'a database has to have a name'); + } + + /* Database name reserved? */ + if (is_array($this->fail_on_invalid_names)) { + $name = strtoupper($database['name']); + foreach ($this->fail_on_invalid_names as $rdbms) { + if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'database name "'.$database['name'].'" is a reserved word in: '.$rdbms); + } + } + } + + /* Create */ + if (isset($database['create']) + && !$this->isBoolean($database['create']) + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "create" has to be a boolean value'); + } + + /* Overwrite */ + if (isset($database['overwrite']) + && $database['overwrite'] !== '' + && !$this->isBoolean($database['overwrite']) + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "overwrite" has to be a boolean value'); + } + + /* + * This have to be done here otherwise we can't guarantee that all + * tables were already defined in the moment we are parsing constraints + */ + if (isset($database['tables'])) { + foreach ($database['tables'] as $table_name => $table) { + if (!empty($table['constraints'])) { + foreach ($table['constraints'] as $constraint_name => $constraint) { + $referenced_table_name = $constraint['references']['table']; + + if (!isset($database['tables'][$referenced_table_name])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'referenced table "'.$referenced_table_name.'" of foreign key "'.$constraint_name.'" of table "'.$table_name.'" does not exist'); + } + + if (empty($constraint['references']['fields'])) { + $referenced_table = $database['tables'][$referenced_table_name]; + + $primary = false; + + if (!empty($referenced_table['indexes'])) { + foreach ($referenced_table['indexes'] as $index_name => $index) { + if (array_key_exists('primary', $index) + && $index['primary'] + ) { + $primary = array(); + foreach ($index['fields'] as $field_name => $field) { + $primary[$field_name] = ''; + } + break; + } + } + } + + if (!$primary) { + foreach ($referenced_table['fields'] as $field_name => $field) { + if (array_key_exists('autoincrement', $field) + && $field['autoincrement'] + ) { + $primary = array( $field_name => '' ); + break; + } + } + } + + if (!$primary) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'referenced table "'.$referenced_table_name.'" has no primary key and no referenced field was specified for foreign key "'.$constraint_name.'" of table "'.$table_name.'"'); + } + + $constraint['references']['fields'] = $primary; + } + + /* the same number of referencing and referenced fields ? */ + if (count($constraint['fields']) != count($constraint['references']['fields'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'The number of fields in the referenced key must match those of the foreign key "'.$constraint_name.'"'); + } + + $database['tables'][$table_name]['constraints'][$constraint_name]['references']['fields'] = $constraint['references']['fields']; + } + } + } + } + + /* + * This have to be done here otherwise we can't guarantee that all + * tables were already defined in the moment we are parsing sequences + */ + if (isset($database['sequences'])) { + foreach ($database['sequences'] as $seq_name => $seq) { + if (!empty($seq['on']) + && empty($database['tables'][$seq['on']['table']]['fields'][$seq['on']['field']]) + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'sequence "'.$seq_name.'" was assigned on unexisting field/table'); + } + } + } + return MDB2_OK; + } + + // }}} + // {{{ validateDataField() + + /* Data Manipulation */ + /** + * Checks whether a parsed DML-field is valid. Modify its structure when + * necessary. This is called when validating INSERT and + * UPDATE. + * + * @param array $table_fields multi dimensional array that contains the + * definition for current table's fields. + * @param array $instruction_fields multi dimensional array that contains the + * parsed fields of the current DML instruction. + * @param string &$field array that contains the parsed instruction field + * + * @return bool|error object + * + * @access public + */ + function validateDataField($table_fields, $instruction_fields, &$field) + { + if (!$field['name']) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field-name has to be specified'); + } + + if (is_array($instruction_fields) && isset($instruction_fields[$field['name']])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'field "'.$field['name'].'" already initialized'); + } + + if (is_array($table_fields) && !isset($table_fields[$field['name']])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field['name'].'" is not defined'); + } + + if (!isset($field['group']['type'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field['name'].'" has no initial value'); + } + + if (isset($field['group']['data']) + && $field['group']['type'] == 'value' + && $field['group']['data'] !== '' + && PEAR::isError($result = $this->validateDataFieldValue($table_fields[$field['name']], $field['group']['data'], $field['name'])) + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + 'value of "'.$field['name'].'" is incorrect: '.$result->getUserinfo()); + } + + return MDB2_OK; + } + + // }}} + // {{{ validateDataFieldValue() + + /** + * Checks whether a given value is compatible with a table field. This is + * done when parsing a field for a INSERT or UPDATE instruction. + * + * @param array $field_def multi dimensional array that contains the + * definition for current table's fields. + * @param string &$field_value value to fill the parsed field + * @param string $field_name name of the parsed field + * + * @return bool|error object + * + * @access public + * @see MDB2_Schema_Validate::validateInsertField() + */ + function validateDataFieldValue($field_def, &$field_value, $field_name) + { + switch ($field_def['type']) { + case 'text': + case 'clob': + if (!empty($field_def['length']) && strlen($field_value) > $field_def['length']) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is larger than "'.$field_def['length'].'"'); + } + break; + case 'blob': + $field_value = pack('H*', $field_value); + if (!empty($field_def['length']) && strlen($field_value) > $field_def['length']) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is larger than "'.$field_def['type'].'"'); + } + break; + case 'integer': + if ($field_value != ((int)$field_value)) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is not of type "'.$field_def['type'].'"'); + } + //$field_value = (int)$field_value; + if (!empty($field_def['unsigned']) && $field_def['unsigned'] && $field_value < 0) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" signed instead of unsigned'); + } + break; + case 'boolean': + if (!$this->isBoolean($field_value)) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is not of type "'.$field_def['type'].'"'); + } + break; + case 'date': + if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/', $field_value) + && $field_value !== 'CURRENT_DATE' + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is not of type "'.$field_def['type'].'"'); + } + break; + case 'timestamp': + if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $field_value) + && strcasecmp($field_value, 'now()') != 0 + && $field_value !== 'CURRENT_TIMESTAMP' + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is not of type "'.$field_def['type'].'"'); + } + break; + case 'time': + if (!preg_match("/([0-9]{2}):([0-9]{2}):([0-9]{2})/", $field_value) + && $field_value !== 'CURRENT_TIME' + ) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is not of type "'.$field_def['type'].'"'); + } + break; + case 'float': + case 'double': + if ($field_value != (double)$field_value) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, + '"'.$field_value.'" is not of type "'.$field_def['type'].'"'); + } + //$field_value = (double)$field_value; + break; + } + return MDB2_OK; + } +} + +?> diff --git a/inc/MDB2/Schema/Writer.php b/inc/MDB2/Schema/Writer.php new file mode 100644 index 00000000000..5ae4918dc1d --- /dev/null +++ b/inc/MDB2/Schema/Writer.php @@ -0,0 +1,581 @@ +<?php +/** + * PHP versions 4 and 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: Writer.php,v 1.40 2008/11/30 03:34:00 clockwerx Exp $ + * @link http://pear.php.net/packages/MDB2_Schema + */ + +/** + * Writes an XML schema file + * + * @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_Writer +{ + // {{{ properties + + var $valid_types = array(); + + // }}} + // {{{ constructor + + function __construct($valid_types = array()) + { + $this->valid_types = $valid_types; + } + + function MDB2_Schema_Writer($valid_types = array()) + { + $this->__construct($valid_types); + } + + // }}} + // {{{ 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 int|PEAR_Error $code integer error code or and PEAR_Error instance + * @param int $mode error mode, see PEAR_Error docs + * 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 $options Extra debug information. Defaults to the last + * query and native error code. + * + * @return object a PEAR error object + * @access public + * @see PEAR_Error + */ + function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) + { + $error =& MDB2_Schema::raiseError($code, $mode, $options, $userinfo); + return $error; + } + + // }}} + // {{{ _escapeSpecialChars() + + /** + * add escapecharacters to all special characters in a string + * + * @param string $string string that should be escaped + * + * @return string escaped string + * @access protected + */ + function _escapeSpecialChars($string) + { + if (!is_string($string)) { + $string = strval($string); + } + + $escaped = ''; + for ($char = 0, $count = strlen($string); $char < $count; $char++) { + switch ($string[$char]) { + case '&': + $escaped .= '&'; + break; + case '>': + $escaped .= '>'; + break; + case '<': + $escaped .= '<'; + break; + case '"': + $escaped .= '"'; + break; + case '\'': + $escaped .= '''; + break; + default: + $code = ord($string[$char]); + if ($code < 32 || $code > 127) { + $escaped .= "&#$code;"; + } else { + $escaped .= $string[$char]; + } + break; + } + } + return $escaped; + } + + // }}} + // {{{ _dumpBoolean() + + /** + * dump the structure of a sequence + * + * @param string $boolean boolean value or variable definition + * + * @return string with xml boolea definition + * @access private + */ + function _dumpBoolean($boolean) + { + if (is_string($boolean)) { + if ($boolean !== 'true' || $boolean !== 'false' + || preg_match('/<variable>.*</variable>/', $boolean) + ) { + return $boolean; + } + } + return $boolean ? 'true' : 'false'; + } + + // }}} + // {{{ dumpSequence() + + /** + * dump the structure of a sequence + * + * @param string $sequence_definition sequence definition + * @param string $sequence_name sequence name + * @param string $eol end of line characters + * @param integer $dump determines what data to dump + * MDB2_SCHEMA_DUMP_ALL : the entire db + * MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db + * MDB2_SCHEMA_DUMP_CONTENT : only the content of the db + * + * @return mixed string xml sequence definition on success, or a error object + * @access public + */ + function dumpSequence($sequence_definition, $sequence_name, $eol, $dump = MDB2_SCHEMA_DUMP_ALL) + { + $buffer = "$eol <sequence>$eol <name>$sequence_name</name>$eol"; + if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT) { + if (!empty($sequence_definition['start'])) { + $start = $sequence_definition['start']; + $buffer .= " <start>$start</start>$eol"; + } + } + + if (!empty($sequence_definition['on'])) { + $buffer .= " <on>$eol"; + $buffer .= " <table>".$sequence_definition['on']['table']; + $buffer .= "</table>$eol <field>".$sequence_definition['on']['field']; + $buffer .= "</field>$eol </on>$eol"; + } + $buffer .= " </sequence>$eol"; + + return $buffer; + } + + // }}} + // {{{ 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 $database_definition unknown + * @param array $arguments associative array that takes pairs of tag + * names and values that define dump options. + * array ( + * '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 + * 'end_of_line' => String + * end of line delimiter that should be used + * default: "\n" + * ); + * @param integer $dump determines what data to dump + * MDB2_SCHEMA_DUMP_ALL : the entire db + * MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db + * MDB2_SCHEMA_DUMP_CONTENT : only the content of the db + * + * @return mixed MDB2_OK on success, or a error object + * @access public + */ + function dumpDatabase($database_definition, $arguments, $dump = MDB2_SCHEMA_DUMP_ALL) + { + if (!empty($arguments['output'])) { + if (!empty($arguments['output_mode']) && $arguments['output_mode'] == 'file') { + $fp = fopen($arguments['output'], 'w'); + if ($fp === false) { + return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null, + 'it was not possible to open output file'); + } + + $output = false; + } elseif (is_callable($arguments['output'])) { + $output = $arguments['output']; + } else { + return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null, + 'no valid output function specified'); + } + } else { + return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null, + 'no output method specified'); + } + + $eol = isset($arguments['end_of_line']) ? $arguments['end_of_line'] : "\n"; + + $sequences = array(); + if (!empty($database_definition['sequences']) + && is_array($database_definition['sequences']) + ) { + foreach ($database_definition['sequences'] as $sequence_name => $sequence) { + $table = !empty($sequence['on']) ? $sequence['on']['table'] :''; + + $sequences[$table][] = $sequence_name; + } + } + + $buffer = '<?xml version="1.0" encoding="ISO-8859-1" ?>'.$eol; + $buffer .= "<database>$eol$eol <name>".$database_definition['name']."</name>"; + $buffer .= "$eol <create>".$this->_dumpBoolean($database_definition['create'])."</create>"; + $buffer .= "$eol <overwrite>".$this->_dumpBoolean($database_definition['overwrite'])."</overwrite>$eol"; + $buffer .= "$eol <charset>".$database_definition['charset']."</charset>$eol"; + + if ($output) { + call_user_func($output, $buffer); + } else { + fwrite($fp, $buffer); + } + + if (!empty($database_definition['tables']) && is_array($database_definition['tables'])) { + foreach ($database_definition['tables'] as $table_name => $table) { + $buffer = "$eol <table>$eol$eol <name>$table_name</name>$eol"; + if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_STRUCTURE) { + $buffer .= "$eol <declaration>$eol"; + if (!empty($table['fields']) && is_array($table['fields'])) { + foreach ($table['fields'] as $field_name => $field) { + if (empty($field['type'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null, + 'it was not specified the type of the field "'. + $field_name.'" of the table "'.$table_name.'"'); + } + if (!empty($this->valid_types) && !array_key_exists($field['type'], $this->valid_types)) { + return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null, + 'type "'.$field['type'].'" is not yet supported'); + } + $buffer .= "$eol <field>$eol <name>$field_name</name>$eol <type>"; + $buffer .= $field['type']."</type>$eol"; + if (!empty($field['fixed']) && $field['type'] === 'text') { + $buffer .= " <fixed>".$this->_dumpBoolean($field['fixed'])."</fixed>$eol"; + } + if (array_key_exists('default', $field) + && $field['type'] !== 'clob' && $field['type'] !== 'blob' + ) { + $buffer .= ' <default>'.$this->_escapeSpecialChars($field['default'])."</default>$eol"; + } + if (!empty($field['notnull'])) { + $buffer .= " <notnull>".$this->_dumpBoolean($field['notnull'])."</notnull>$eol"; + } else { + $buffer .= " <notnull>false</notnull>$eol"; + } + if (!empty($field['autoincrement'])) { + $buffer .= " <autoincrement>" . $field['autoincrement'] ."</autoincrement>$eol"; + } + if (!empty($field['unsigned'])) { + $buffer .= " <unsigned>".$this->_dumpBoolean($field['unsigned'])."</unsigned>$eol"; + } + if (!empty($field['length'])) { + $buffer .= ' <length>'.$field['length']."</length>$eol"; + } + $buffer .= " </field>$eol"; + } + } + + if (!empty($table['indexes']) && is_array($table['indexes'])) { + foreach ($table['indexes'] as $index_name => $index) { + if (strtolower($index_name) === 'primary') { + $index_name = $table_name . '_pKey'; + } + $buffer .= "$eol <index>$eol <name>$index_name</name>$eol"; + if (!empty($index['unique'])) { + $buffer .= " <unique>".$this->_dumpBoolean($index['unique'])."</unique>$eol"; + } + + if (!empty($index['primary'])) { + $buffer .= " <primary>".$this->_dumpBoolean($index['primary'])."</primary>$eol"; + } + + foreach ($index['fields'] as $field_name => $field) { + $buffer .= " <field>$eol <name>$field_name</name>$eol"; + if (!empty($field) && is_array($field)) { + $buffer .= ' <sorting>'.$field['sorting']."</sorting>$eol"; + } + $buffer .= " </field>$eol"; + } + $buffer .= " </index>$eol"; + } + } + + if (!empty($table['constraints']) && is_array($table['constraints'])) { + foreach ($table['constraints'] as $constraint_name => $constraint) { + $buffer .= "$eol <foreign>$eol <name>$constraint_name</name>$eol"; + if (empty($constraint['fields']) || !is_array($constraint['fields'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null, + 'it was not specified a field for the foreign key "'. + $constraint_name.'" of the table "'.$table_name.'"'); + } + if (!is_array($constraint['references']) || empty($constraint['references']['table'])) { + return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null, + 'it was not specified the referenced table of the foreign key "'. + $constraint_name.'" of the table "'.$table_name.'"'); + } + if (!empty($constraint['match'])) { + $buffer .= " <match>".$constraint['match']."</match>$eol"; + } + if (!empty($constraint['ondelete'])) { + $buffer .= " <ondelete>".$constraint['ondelete']."</ondelete>$eol"; + } + if (!empty($constraint['onupdate'])) { + $buffer .= " <onupdate>".$constraint['onupdate']."</onupdate>$eol"; + } + if (!empty($constraint['deferrable'])) { + $buffer .= " <deferrable>".$constraint['deferrable']."</deferrable>$eol"; + } + if (!empty($constraint['initiallydeferred'])) { + $buffer .= " <initiallydeferred>".$constraint['initiallydeferred']."</initiallydeferred>$eol"; + } + foreach ($constraint['fields'] as $field_name => $field) { + $buffer .= " <field>$field_name</field>$eol"; + } + $buffer .= " <references>$eol <table>".$constraint['references']['table']."</table>$eol"; + foreach ($constraint['references']['fields'] as $field_name => $field) { + $buffer .= " <field>$field_name</field>$eol"; + } + $buffer .= " </references>$eol"; + + $buffer .= " </foreign>$eol"; + } + } + + $buffer .= "$eol </declaration>$eol"; + } + + if ($output) { + call_user_func($output, $buffer); + } else { + fwrite($fp, $buffer); + } + + $buffer = ''; + if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT) { + if (!empty($table['initialization']) && is_array($table['initialization'])) { + $buffer = "$eol <initialization>$eol"; + foreach ($table['initialization'] as $instruction) { + switch ($instruction['type']) { + case 'insert': + $buffer .= "$eol <insert>$eol"; + foreach ($instruction['data']['field'] as $field) { + $field_name = $field['name']; + + $buffer .= "$eol <field>$eol <name>$field_name</name>$eol"; + $buffer .= $this->writeExpression($field['group'], 5, $arguments); + $buffer .= " </field>$eol"; + } + $buffer .= "$eol </insert>$eol"; + break; + case 'update': + $buffer .= "$eol <update>$eol"; + foreach ($instruction['data']['field'] as $field) { + $field_name = $field['name']; + + $buffer .= "$eol <field>$eol <name>$field_name</name>$eol"; + $buffer .= $this->writeExpression($field['group'], 5, $arguments); + $buffer .= " </field>$eol"; + } + + if (!empty($instruction['data']['where']) + && is_array($instruction['data']['where']) + ) { + $buffer .= " <where>$eol"; + $buffer .= $this->writeExpression($instruction['data']['where'], 5, $arguments); + $buffer .= " </where>$eol"; + } + + $buffer .= "$eol </update>$eol"; + break; + case 'delete': + $buffer .= "$eol <delete>$eol$eol"; + if (!empty($instruction['data']['where']) + && is_array($instruction['data']['where']) + ) { + $buffer .= " <where>$eol"; + $buffer .= $this->writeExpression($instruction['data']['where'], 5, $arguments); + $buffer .= " </where>$eol"; + } + $buffer .= "$eol </delete>$eol"; + break; + } + } + $buffer .= "$eol </initialization>$eol"; + } + } + $buffer .= "$eol </table>$eol"; + if ($output) { + call_user_func($output, $buffer); + } else { + fwrite($fp, $buffer); + } + + if (isset($sequences[$table_name])) { + foreach ($sequences[$table_name] as $sequence) { + $result = $this->dumpSequence($database_definition['sequences'][$sequence], + $sequence, $eol, $dump); + if (PEAR::isError($result)) { + return $result; + } + + if ($output) { + call_user_func($output, $result); + } else { + fwrite($fp, $result); + } + } + } + } + } + + if (isset($sequences[''])) { + foreach ($sequences[''] as $sequence) { + $result = $this->dumpSequence($database_definition['sequences'][$sequence], + $sequence, $eol, $dump); + if (PEAR::isError($result)) { + return $result; + } + + if ($output) { + call_user_func($output, $result); + } else { + fwrite($fp, $result); + } + } + } + + $buffer = "$eol</database>$eol"; + if ($output) { + call_user_func($output, $buffer); + } else { + fwrite($fp, $buffer); + fclose($fp); + } + + return MDB2_OK; + } + + // }}} + // {{{ writeExpression() + + /** + * Dumps the structure of an element. Elements can be value, column, + * function or expression. + * + * @param array $element multi dimensional array that represents the parsed element + * of a DML instruction. + * @param integer $offset base indentation width + * @param array $arguments associative array that takes pairs of tag + * names and values that define dump options. + * + * @return string + * + * @access public + * @see MDB2_Schema_Writer::dumpDatabase() + */ + function writeExpression($element, $offset = 0, $arguments = null) + { + $eol = isset($arguments['end_of_line']) ? $arguments['end_of_line'] : "\n"; + $str = ''; + + $indent = str_repeat(' ', $offset); + $noffset = $offset + 1; + + switch ($element['type']) { + case 'value': + $str .= "$indent<value>".$this->_escapeSpecialChars($element['data'])."</value>$eol"; + break; + case 'column': + $str .= "$indent<column>".$this->_escapeSpecialChars($element['data'])."</column>$eol"; + break; + case 'function': + $str .= "$indent<function>$eol$indent <name>".$this->_escapeSpecialChars($element['data']['name'])."</name>$eol"; + + if (!empty($element['data']['arguments']) + && is_array($element['data']['arguments']) + ) { + foreach ($element['data']['arguments'] as $v) { + $str .= $this->writeExpression($v, $noffset, $arguments); + } + } + + $str .= "$indent</function>$eol"; + break; + case 'expression': + $str .= "$indent<expression>$eol"; + $str .= $this->writeExpression($element['data']['operants'][0], $noffset, $arguments); + $str .= "$indent <operator>".$element['data']['operator']."</operator>$eol"; + $str .= $this->writeExpression($element['data']['operants'][1], $noffset, $arguments); + $str .= "$indent</expression>$eol"; + break; + } + return $str; + } + + // }}} +} +?> diff --git a/inc/XML/Parser.php b/inc/XML/Parser.php index c8e45947c1a..6f77b5c66da 100755 --- a/inc/XML/Parser.php +++ b/inc/XML/Parser.php @@ -36,7 +36,7 @@ /** * uses PEAR's error handling */ -require_once 'PEAR.php'; +oc_require_once('PEAR.php'); /** * resource could not be created @@ -376,13 +376,12 @@ class XML_Parser extends PEAR /** * check, if file is a remote file */ - if (eregi('^(http|ftp)://', substr($file, 0, 10))) { + if (preg_match('[^(http|ftp)://]', substr($file, 0, 10))) { if (!ini_get('allow_url_fopen')) { return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE); } } - - $fp = @fopen($file, 'rb'); + $fp = fopen($file, 'rb'); if (is_resource($fp)) { $this->fp = $fp; return $fp; @@ -564,7 +563,7 @@ class XML_Parser extends PEAR function raiseError($msg = null, $ecode = 0) { $msg = !is_null($msg) ? $msg : $this->parser; - $err = &new XML_Parser_Error($msg, $ecode); + $err = new XML_Parser_Error($msg, $ecode); return parent::raiseError($err); } diff --git a/inc/lib_base.php b/inc/lib_base.php index 728b9acd018..5fc95b66aee 100755 --- a/inc/lib_base.php +++ b/inc/lib_base.php @@ -98,6 +98,7 @@ oc_require_once('lib_config.php'); oc_require_once('lib_user.php'); oc_require_once('lib_ocs.php'); @oc_require_once('MDB2.php'); +@oc_require_once('MDB2/Schema.php'); oc_require_once('lib_connect.php'); @@ -318,6 +319,7 @@ class OC_UTIL { */ class OC_DB { static private $DBConnection=false; + static private $schema=false; /** * connect to the datbase if not already connected */ @@ -331,8 +333,11 @@ class OC_DB { global $SERVERROOT; if(!self::$DBConnection){ $options = array( - 'debug' => 0, 'portability' => MDB2_PORTABILITY_ALL, + 'log_line_break' => '<br>', + 'idxname_format' => '%s', + 'debug' => false, + 'quote_identifier' => true, ); if($CONFIG_DBTYPE=='sqlite'){ $dsn = array( @@ -349,13 +354,13 @@ class OC_DB { 'database' => $CONFIG_DBNAME, ); } - self::$DBConnection=MDB2::connect($dsn,$options); + self::$DBConnection=&MDB2::factory($dsn,$options); if (@PEAR::isError(self::$DBConnection)) { echo('<b>can not connect to database, using '.$CONFIG_DBTYPE.'. ('.self::$DBConnection->getUserInfo().')</center>'); die(self::$DBConnection->getMessage()); } self::$DBConnection->setFetchMode(MDB2_FETCHMODE_ASSOC); -// self::$DBConnection->loadModule('Manager'); + self::$schema=&MDB2_Schema::factory($dsn,$options); } } @@ -426,7 +431,8 @@ class OC_DB { * @return primarykey */ static function insertid() { - return self::$DBConnection->lastInsertID(); + $id=self::$DBConnection->lastInsertID(); + return $id; } /** @@ -498,7 +504,31 @@ class OC_DB { OC_DB::connect(); return self::$DBConnection->escape($string); } - + + static function getDBStructure($file){ + OC_DB::connect(); + $definition = self::$schema->getDefinitionFromDatabase(); + $dump_options = array( + 'output_mode' => 'file', + 'output' => $file, + 'end_of_line' => "\n" + ); + self::$schema->dumpDatabase($definition, $dump_options, MDB2_SCHEMA_DUMP_STRUCTURE); + } + + static function createDBFromStructure($file){ + OC_DB::connect(); + $definition=@self::$schema->parseDatabaseDefinitionFile($file); + if($definition instanceof MDB2_Schema_Error){ + die($definition->getMessage() . ': ' . $definition->getUserInfo()); + } + $ret=@self::$schema->createDatabase($definition); + if($ret instanceof MDB2_Error) { + die ($ret->getMessage() . ': ' . $ret->getUserInfo()); + }else{ + return true; + } + } } diff --git a/inc/lib_config.php b/inc/lib_config.php index 986d12f4dce..6ac83db951f 100755 --- a/inc/lib_config.php +++ b/inc/lib_config.php @@ -267,169 +267,58 @@ class OC_CONFIG{ } } } - - /** - * Fills the database with the initial tables - * Note: while the AUTO_INCREMENT function is not supported by SQLite - * the same effect can be achieved by accessing the SQLite pseudo-column - * "rowid" - */ - private static function filldatabase(){ - global $CONFIG_DBTYPE; - if($CONFIG_DBTYPE=='sqlite'){ - $query="CREATE TABLE 'locks' ( - 'token' VARCHAR(255) NOT NULL DEFAULT '', - 'path' varchar(200) NOT NULL DEFAULT '', - 'created' int(11) NOT NULL DEFAULT '0', - 'modified' int(11) NOT NULL DEFAULT '0', - 'expires' int(11) NOT NULL DEFAULT '0', - 'owner' varchar(200) DEFAULT NULL, - 'recursive' int(11) DEFAULT '0', - 'writelock' int(11) DEFAULT '0', - 'exclusivelock' int(11) NOT NULL DEFAULT '0', - PRIMARY KEY ('token'), - UNIQUE ('token') - ); - -CREATE TABLE 'log' ( - `id` INTEGER ASC DEFAULT '' NOT NULL, - 'timestamp' int(11) NOT NULL, - 'user' varchar(250) NOT NULL, - 'type' int(11) NOT NULL, - 'message' varchar(250) NOT NULL, - PRIMARY KEY ('id') -); - - -CREATE TABLE 'properties' ( - 'path' varchar(255) NOT NULL DEFAULT '', - 'name' varchar(120) NOT NULL DEFAULT '', - 'ns' varchar(120) NOT NULL DEFAULT 'DAV:', - 'value' text, - PRIMARY KEY ('path','name','ns') -); - -CREATE TABLE 'users' ( - 'user_id' INTEGER ASC DEFAULT '', - 'user_name' varchar(64) NOT NULL DEFAULT '', - 'user_name_clean' varchar(64) NOT NULL DEFAULT '', - 'user_password' varchar(40) NOT NULL DEFAULT '', - PRIMARY KEY ('user_id'), - UNIQUE ('user_name' ,'user_name_clean') -); - -CREATE TABLE 'groups' ( -'group_id' INTEGER ASC DEFAULT '', -'group_name' VARCHAR( 64 ) NOT NULL DEFAULT '', -PRIMARY KEY ('group_id'), -UNIQUE ('group_name') -); - -CREATE TABLE 'user_group' ( -'user_group_id' INTEGER ASC DEFAULT '', -'user_id' VARCHAR( 64 ) NOT NULL DEFAULT '', -'group_id' VARCHAR( 64 ) NOT NULL DEFAULT '', -PRIMARY KEY ('user_group_id') -) -"; - }elseif($CONFIG_DBTYPE=='mysql'){ - $query="CREATE TABLE IF NOT EXISTS `locks` ( - `token` varchar(255) NOT NULL DEFAULT '', - `path` varchar(200) NOT NULL DEFAULT '', - `created` int(11) NOT NULL DEFAULT '0', - `modified` int(11) NOT NULL DEFAULT '0', - `expires` int(11) NOT NULL DEFAULT '0', - `owner` varchar(200) DEFAULT NULL, - `recursive` int(11) DEFAULT '0', - `writelock` int(11) DEFAULT '0', - `exclusivelock` int(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`token`), - UNIQUE KEY `token` (`token`), - KEY `path` (`path`), - KEY `path_2` (`path`), - KEY `path_3` (`path`,`token`), - KEY `expires` (`expires`) -); - -CREATE TABLE IF NOT EXISTS `log` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `timestamp` int(11) NOT NULL, - `user` varchar(250) NOT NULL, - `type` int(11) NOT NULL, - `message` varchar(250) NOT NULL, - PRIMARY KEY (`id`) -); - - -CREATE TABLE IF NOT EXISTS `properties` ( - `path` varchar(255) NOT NULL DEFAULT '', - `name` varchar(120) NOT NULL DEFAULT '', - `ns` varchar(120) NOT NULL DEFAULT 'DAV:', - `value` text, - PRIMARY KEY (`path`,`name`,`ns`), - KEY `path` (`path`) -); - -CREATE TABLE IF NOT EXISTS `users` ( -`user_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , -`user_name` VARCHAR( 64 ) NOT NULL , -`user_name_clean` VARCHAR( 64 ) NOT NULL , -`user_password` VARCHAR( 340) NOT NULL , -UNIQUE ( -`user_name` , -`user_name_clean` -) -); - -CREATE TABLE IF NOT EXISTS `groups` ( -`group_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , -`group_name` VARCHAR( 64 ) NOT NULL , -UNIQUE ( -`group_name` -) -); - -CREATE TABLE IF NOT EXISTS `user_group` ( -`user_group_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , -`user_id` VARCHAR( 64 ) NOT NULL , -`group_id` VARCHAR( 64 ) NOT NULL -) -"; + + /** + * Fills the database with the initial tables + * Note: while the AUTO_INCREMENT function is not supported by SQLite + * the same effect can be achieved by accessing the SQLite pseudo-column + * "rowid" + */ + private static function filldatabase(){ + global $SERVERROOT; + OC_DB::createDBFromStructure($SERVERROOT.'/db_structure.xml'); + } + + /** + * Create the database and user + * @param string adminUser + * @param string adminPwd + * + */ + private static function createdatabase($adminUser,$adminPwd){ + global $CONFIG_DBHOST; + global $CONFIG_DBNAME; + global $CONFIG_DBUSER; + global $CONFIG_DBPWD; + //we cant user OC_BD functions here because we need to connect as the administrative user. + $connection = @new mysqli($CONFIG_DBHOST, $adminUser, $adminPwd); + if (mysqli_connect_errno()) { + @ob_end_clean(); + echo('<html><head></head><body bgcolor="#F0F0F0"><br /><br /><center><b>can not connect to database as administrative user.</center></body></html>'); + exit(); + } + $query="SELECT user FROM mysql.user WHERE user='{$_POST['dbuser']}';"; + $result = @$connection->query($query); + if (!$result) { + $entry='DB Error: "'.$connection->error.'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + if($result->num_rows==0){ + $query="CREATE USER '{$_POST['dbuser']}' IDENTIFIED BY '{$_POST['dbpassword']}';"; + }else{ + $query=''; + } + $query.="CREATE DATABASE IF NOT EXISTS `{$_POST['dbname']}`; + GRANT ALL PRIVILEGES ON `{$_POST['dbname']}` . * TO '{$_POST['dbuser']}';"; + $result = @$connection->multi_query($query); + if (!$result) { + $entry='DB Error: "'.$connection->error.'"<br />'; + $entry.='Offending command was: '.$query.'<br />'; + echo($entry); + } + $connection->close(); } - OC_DB::multiquery($query); - } - - /** - * Create the database and user - * @param string adminUser - * @param string adminPwd - * - */ - private static function createdatabase($adminUser,$adminPwd){ - global $CONFIG_DBHOST; - global $CONFIG_DBNAME; - global $CONFIG_DBUSER; - global $CONFIG_DBPWD; - //we cant user OC_BD functions here because we need to connect as the administrative user. - $connection = @new mysqli($CONFIG_DBHOST, $adminUser, $adminPwd); - if (mysqli_connect_errno()) { - @ob_end_clean(); - echo('<html><head></head><body bgcolor="#F0F0F0"><br /><br /><center><b>can not connect to database as administrative user.</center></body></html>'); - exit(); - } - $query="CREATE USER '{$_POST['dbuser']}' IDENTIFIED BY '{$_POST['dbpassword']}'; - -CREATE DATABASE IF NOT EXISTS `{$_POST['dbname']}` ; - -GRANT ALL PRIVILEGES ON `{$_POST['dbname']}` . * TO '{$_POST['dbuser']}';"; - $result = @$connection->multi_query($query); - if (!$result) { - $entry='DB Error: "'.$connection->error.'"<br />'; - $entry.='Offending command was: '.$query.'<br />'; - echo($entry); - } - $connection->close(); - } } ?> |