Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Appelman <icewind1991@gmail.com>2010-06-18 22:08:24 +0400
committerRobin Appelman <icewind1991@gmail.com>2010-06-18 22:08:24 +0400
commit7503ad139b53de41e3567d723419e49166de574f (patch)
tree4adf6db30eebcd1e72aff598deb5c5f935540cc0
parent02ad83b04d79079eac4e0783a83db32ed0dba88b (diff)
use MDB2_Schema for the creation of the initial database
-rwxr-xr-xdb_structure.xml381
-rwxr-xr-xinc/MDB2/Driver/Datatype/Common.php2
-rwxr-xr-xinc/MDB2/Driver/Datatype/mysql.php2
-rwxr-xr-xinc/MDB2/Driver/Datatype/pgsql.php2
-rwxr-xr-xinc/MDB2/Driver/Datatype/sqlite.php2
-rwxr-xr-xinc/MDB2/Driver/mysql.php2
-rwxr-xr-xinc/MDB2/LOB.php2
-rw-r--r--inc/MDB2/Schema.php2763
-rw-r--r--inc/MDB2/Schema/Parser.php819
-rw-r--r--inc/MDB2/Schema/Parser2.php624
-rw-r--r--inc/MDB2/Schema/Reserved/ibase.php436
-rw-r--r--inc/MDB2/Schema/Reserved/mssql.php258
-rw-r--r--inc/MDB2/Schema/Reserved/mysql.php284
-rw-r--r--inc/MDB2/Schema/Reserved/oci8.php171
-rw-r--r--inc/MDB2/Schema/Reserved/pgsql.php147
-rw-r--r--inc/MDB2/Schema/Tool.php560
-rw-r--r--inc/MDB2/Schema/Tool/ParameterException.php6
-rw-r--r--inc/MDB2/Schema/Validate.php922
-rw-r--r--inc/MDB2/Schema/Writer.php581
-rwxr-xr-xinc/XML/Parser.php9
-rwxr-xr-xinc/lib_base.php40
-rwxr-xr-xinc/lib_config.php213
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 .= '&amp;';
+ break;
+ case '>':
+ $escaped .= '&gt;';
+ break;
+ case '<':
+ $escaped .= '&lt;';
+ break;
+ case '"':
+ $escaped .= '&quot;';
+ break;
+ case '\'':
+ $escaped .= '&apos;';
+ 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();
- }
}
?>