diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
diff --git a/config/.gitignore b/config/.gitignore
old mode 100644
new mode 100755
diff --git a/config/config.sample.php b/config/config.sample.php
index dfaaa4284d66a203cb766f4434bd03ab221e8005..dc1a62f46e3df25b55092cab65d33e4a5f82ae9d 100755
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -8,4 +8,5 @@ $CONFIG_DBHOST='localhost';
 $CONFIG_DBNAME='owncloud-db-name';
 $CONFIG_DBUSER='user-name';
 $CONFIG_DBPASSWORD='password';
+$CONFIG_DBTABLEPREFIX = 'oc_';
 ?>
diff --git a/css/small.php b/css/small.php
old mode 100644
new mode 100755
diff --git a/db_structure.xml b/db_structure.xml
new file mode 100755
index 0000000000000000000000000000000000000000..7efb722bd721a0f6ee563301039ab7e25a35cad8
--- /dev/null
+++ b/db_structure.xml
@@ -0,0 +1,381 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>latin1</charset>
+
+ <table>
+
+  <name>*dbprefix*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>*dbprefix*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>*dbprefix*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>*dbprefix*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>*dbprefix*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>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+	<length>4</length>
+   </field>
+
+   <field>
+    <name>group_id</name>
+	<type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+	<length>4</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*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/files/api.php b/files/api.php
index 014bbb56bbc8983bf872e27a068829bfb958f5f4..d6e04d4550faff23bd40f7929a19aa43c7689c35 100755
--- a/files/api.php
+++ b/files/api.php
@@ -55,6 +55,9 @@ if($arguments['action']){
 		case 'getfiles':
 			echo json_encode(OC_FILES::getdirectorycontent($arguments['dir']));
 			break;
+		case 'gettree':
+			echo json_encode(OC_FILES::getTree($arguments['dir']));
+			break;
 		case 'find':
 			echo json_encode(OC_FILESYSTEM::find($arguments['path']));
 			break;
@@ -72,6 +75,8 @@ if($arguments['action']){
 				echo 'false';
 			}
 			break;
+		case 'pull':
+			return OC_FILES::pull($arguments['source'],$arguments['token'],$arguments['dir'],$arguments['file']);
 	}
 }
 
diff --git a/files/pull.php b/files/pull.php
new file mode 100644
index 0000000000000000000000000000000000000000..1cc82425845fa52413ab15a8ed3ea31a433a15e1
--- /dev/null
+++ b/files/pull.php
@@ -0,0 +1,11 @@
+<?php
+$token=$_GET['token'];
+
+$file=sys_get_temp_dir().'/'.'remoteCloudFile'.$token;
+if(file_exists($file) and is_readable($file) and is_writable($file)){
+	readfile($file);
+	unlink($file);
+}else{
+	header("HTTP/1.0 404 Not Found");
+}
+?>
\ No newline at end of file
diff --git a/inc/HTTP/WebDAV/Server/Filesystem.php b/inc/HTTP/WebDAV/Server/Filesystem.php
index b96fb414c27fbfe8376ec0b343b2ce00cba506c0..ea0625a5a1e9aadf0ec8c6ba3526f1dad5bed675 100755
--- a/inc/HTTP/WebDAV/Server/Filesystem.php
+++ b/inc/HTTP/WebDAV/Server/Filesystem.php
@@ -150,6 +150,8 @@
      */
     function fileinfo($path) 
     {
+		global $CONFIG_DBTABLEPREFIX;
+
         // map URI path to filesystem path
         $fspath =$path;
 
@@ -183,7 +185,7 @@
             $info["props"][] = $this->mkprop("getcontentlength",  OC_FILESYSTEM::filesize($fspath));
         }
         // get additional properties from database
-		$query = "SELECT ns, name, value FROM properties WHERE path = '$path'";
+		$query = "SELECT ns, name, value FROM {$CONFIG_DBTABLEPREFIX}properties WHERE path = '$path'";
 		$res = OC_DB::select($query);
 		foreach($res as $row){
             $info["props"][] = $this->mkprop($row["ns"], $row["name"], $row["value"]);
@@ -389,6 +391,7 @@
      */
     function DELETE($options) 
     {
+		global $CONFIG_DBTABLEPREFIX;
         $path =$options["path"];
         if (!OC_FILESYSTEM::file_exists($path)) {
             return "404 Not found";
@@ -402,13 +405,13 @@
 			}
 		}
         if (OC_FILESYSTEM::is_dir($path)) {
-                $query = "DELETE FROM properties WHERE path LIKE '".$this->_slashify($options["path"])."%'";
+                $query = "DELETE FROM {$CONFIG_DBTABLEPREFIX}properties WHERE path LIKE '".$this->_slashify($options["path"])."%'";
                 OC_DB::query($query);
 				OC_FILESYSTEM::delTree($path);
         } else {
             OC_FILESYSTEM::unlink($path);
         }
-            $query = "DELETE FROM properties WHERE path = '$options[path]'";
+            $query = "DELETE FROM {$CONFIG_DBTABLEPREFIX}properties WHERE path = '$options[path]'";
             OC_DB::query($query);
 
         return "204 No Content";
@@ -435,6 +438,7 @@
     function COPY($options, $del=false) 
     {
         // TODO Property updates still broken (Litmus should detect this?)
+		global $CONFIG_DBTABLEPREFIX;
 
         if (!empty($this->_SERVER["CONTENT_LENGTH"])) { // no body parsing yet
             return "415 Unsupported media type";
@@ -508,13 +512,13 @@
             }
             $destpath = $this->_unslashify($options["dest"]);
             if (is_dir($source)) {
-                    $query = "UPDATE properties 
+                    $query = "UPDATE {$CONFIG_DBTABLEPREFIX}properties 
                                  SET path = REPLACE(path, '".$options["path"]."', '".$destpath."') 
                                WHERE path LIKE '".$this->_slashify($options["path"])."%'";
                     OC_DB::query($query);
             }
 
-                $query = "UPDATE properties 
+                $query = "UPDATE {$CONFIG_DBTABLEPREFIX}properties 
                              SET path = '".$destpath."'
                            WHERE path = '".$options["path"]."'";
                 OC_DB::query($query);
@@ -566,6 +570,7 @@
     function PROPPATCH(&$options) 
     {
         global $prefs, $tab;
+		global $CONFIG_DBTABLEPREFIX;
 
         $msg  = "";
         $path = $options["path"];
@@ -577,9 +582,9 @@
                 $options["props"][$key]['status'] = "403 Forbidden";
             } else {
                 if (isset($prop["val"])) {
-                        $query = "REPLACE INTO properties SET path = '$options[path]', name = '$prop[name]', ns= '$prop[ns]', value = '$prop[val]'";
+                        $query = "REPLACE INTO {$CONFIG_DBTABLEPREFIX}properties SET path = '$options[path]', name = '$prop[name]', ns= '$prop[ns]', value = '$prop[val]'";
                 } else {
-                        $query = "DELETE FROM properties WHERE path = '$options[path]' AND name = '$prop[name]' AND ns = '$prop[ns]'";
+                        $query = "DELETE FROM {$CONFIG_DBTABLEPREFIX}properties WHERE path = '$options[path]' AND name = '$prop[name]' AND ns = '$prop[ns]'";
                 }       
                     OC_DB::query($query);
             }
@@ -597,6 +602,8 @@
      */
     function LOCK(&$options) 
     {
+		global $CONFIG_DBTABLEPREFIX;
+
         // get absolute fs path to requested resource
         $fspath = $options["path"];
         // TODO recursive locks on directories not supported yet
@@ -619,12 +626,12 @@
         if (isset($options["update"])) { // Lock Update
             $where = "WHERE path = '$options[path]' AND token = '$options[update]'";
 
-            $query = "SELECT owner, exclusivelock FROM locks $where";
+            $query = "SELECT owner, exclusivelock FROM {$CONFIG_DBTABLEPREFIX}locks $where";
             $res   = OC_DB::select($query);
 
             if (is_array($res) and isset($res[0])) {
 				$row=$res[0];
-                $query = "UPDATE `locks` SET `expires` = '$options[timeout]', `modified` = ".time()." $where";
+                $query = "UPDATE `{$CONFIG_DBTABLEPREFIX}locks` SET `expires` = '$options[timeout]', `modified` = ".time()." $where";
                 OC_DB::query($query);
                 
                 $options['owner'] = $row['owner'];
@@ -634,14 +641,14 @@
                 return true;
             } else {//check for indirect refresh
                $query = "SELECT *
-                  FROM locks
+                  FROM {$CONFIG_DBTABLEPREFIX}locks
                  WHERE recursive = 1
                ";
             $res = OC_DB::select($query);
             foreach($res as $row){
 				if(strpos($options['path'],$row['path'])==0){//are we a child of a folder with an recursive lock
 					$where = "WHERE path = '$row[path]' AND token = '$options[update]'";
-					 $query = "UPDATE `locks` SET `expires` = '$options[timeout]', `modified` = ".time()." $where";
+					 $query = "UPDATE `{$CONFIG_DBTABLEPREFIX}locks` SET `expires` = '$options[timeout]', `modified` = ".time()." $where";
                 OC_DB::query($query);
                 $options['owner'] = $row['owner'];
                 $options['scope'] = $row["exclusivelock"] ? "exclusive" : "shared";
@@ -652,7 +659,7 @@
             }
         }
             
-        $query = "INSERT INTO `locks`
+        $query = "INSERT INTO `{$CONFIG_DBTABLEPREFIX}locks`
                         SET `token`   = '$options[locktoken]'
                           , `path`    = '$options[path]'
                           , `created` = ".time()."
@@ -677,7 +684,8 @@
      */
     function UNLOCK(&$options) 
     {
-            $query = "DELETE FROM locks
+		global $CONFIG_DBTABLEPREFIX;
+            $query = "DELETE FROM {$CONFIG_DBTABLEPREFIX}locks
                       WHERE path = '$options[path]'
                         AND token = '$options[token]'";
             OC_DB::query($query);
@@ -693,9 +701,11 @@
      */
     function checkLock($path) 
     {
+		global $CONFIG_DBTABLEPREFIX;
+
         $result = false;
         $query = "SELECT *
-                  FROM locks
+                  FROM {$CONFIG_DBTABLEPREFIX}locks
                  WHERE path = '$path'
                ";
             $res = OC_DB::select($query);
@@ -717,7 +727,7 @@
         }else{
 			//check for recursive locks;
 			$query = "SELECT *
-                  FROM locks
+                  FROM {$CONFIG_DBTABLEPREFIX}locks
                  WHERE recursive = 1
                ";
             $res = OC_DB::select($query);
@@ -741,4 +751,4 @@
     }
 }
 
-?>
+?>
\ No newline at end of file
diff --git a/inc/MDB2.php b/inc/MDB2.php
index 94991bb0c4f7eb632f4521970c7b285354025e66..aa4259b9db7cb82ca7056e156d9c6b5a37bcef78 100755
--- a/inc/MDB2.php
+++ b/inc/MDB2.php
@@ -329,11 +329,10 @@ class MDB2
     {
         if (!MDB2::classExists($class_name)) {
             $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
-//             echo $file_name;
             if ($debug) {
                 $include = oc_include_once($file_name);
             } else {
-                $include = @oc_include_once($file_name);
+                $include = oc_include_once($file_name);
             }
             if (!$include) {
                 if (!MDB2::fileExists($file_name)) {
@@ -958,8 +957,12 @@ class MDB2
     function fileExists($file)
     {
         // safe_mode does notwork with is_readable()
+        global $SERVERROOT;
         if (!@ini_get('safe_mode')) {
              $dirs = explode(PATH_SEPARATOR, ini_get('include_path'));
+             $dirs[]=$SERVERROOT;
+             $dirs[]=$SERVERROOT. DIRECTORY_SEPARATOR .'inc';
+//              print_r($dirs);die();
              foreach ($dirs as $dir) {
                  if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
                      return true;
@@ -1920,7 +1923,6 @@ class MDB2_Driver_Common extends PEAR
             if (PEAR::isError($err)) {
                 return $err;
             }
-
             // load module in a specific version
             if ($version) {
                 if (method_exists($class_name, 'getClassName')) {
diff --git a/inc/MDB2/Driver/Datatype/Common.php b/inc/MDB2/Driver/Datatype/Common.php
index 329bd60768dbb383169aa281b562e341e045592e..750dbb2477282c333b530b53980c639e7846031f 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 aeec86aefac27a939fec8ebc4d94d12d196aff0e..944248f57c3edd84d855da92298199b79bd7e224 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 29796804902da14c80a4bbc634d77a706e126915..fe18729c84f349592fd14f86477de1de85cdaa65 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 270c13968414195fbf9c12f24d7aab9173215fbc..533d0e9510b8e669fee667a7f6112ccacb897edd 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/Function/mysql.php b/inc/MDB2/Driver/Function/mysql.php
index b405c3ec02eac29128fda34f378c299c7b731e99..aff531c9f3a35d5dc94be44cadd71fb73457e968 100755
--- a/inc/MDB2/Driver/Function/mysql.php
+++ b/inc/MDB2/Driver/Function/mysql.php
@@ -45,7 +45,7 @@
 // $Id: mysql.php,v 1.12 2008/02/17 18:54:08 quipo Exp $
 //
 
-require_once 'MDB2/Driver/Function/Common.php';
+oc_require_once('MDB2/Driver/Function/Common.php');
 
 /**
  * MDB2 MySQL driver for the function modules
diff --git a/inc/MDB2/Driver/Function/pgsql.php b/inc/MDB2/Driver/Function/pgsql.php
index 985dc4ed2fead6a1c1be2ebfd92fe581511f8967..cb47ea57d9fa5ef85e9208310be7b342de316cf0 100755
--- a/inc/MDB2/Driver/Function/pgsql.php
+++ b/inc/MDB2/Driver/Function/pgsql.php
@@ -44,7 +44,7 @@
 //
 // $Id: pgsql.php,v 1.11 2008/11/09 19:46:50 quipo Exp $
 
-require_once 'MDB2/Driver/Function/Common.php';
+oc_require_once('MDB2/Driver/Function/Common.php');
 
 /**
  * MDB2 MySQL driver for the function modules
diff --git a/inc/MDB2/Driver/Function/sqlite.php b/inc/MDB2/Driver/Function/sqlite.php
index 05fac65baf0869a5dc6a0e37ad93657c302429ea..f5499599dd5d552999edcd1c6d988b793b4c3044 100755
--- a/inc/MDB2/Driver/Function/sqlite.php
+++ b/inc/MDB2/Driver/Function/sqlite.php
@@ -45,7 +45,7 @@
 // $Id: sqlite.php,v 1.10 2008/02/17 18:54:08 quipo Exp $
 //
 
-require_once 'MDB2/Driver/Function/Common.php';
+oc_require_once('MDB2/Driver/Function/Common.php');
 
 /**
  * MDB2 SQLite driver for the function modules
diff --git a/inc/MDB2/Driver/Manager/mysql.php b/inc/MDB2/Driver/Manager/mysql.php
index e037a0d16abc4cb469ff5be2c229a62859576340..7bd6a3623a32e2ea0d9605f0ea1ce0b1de771fe6 100755
--- a/inc/MDB2/Driver/Manager/mysql.php
+++ b/inc/MDB2/Driver/Manager/mysql.php
@@ -45,7 +45,7 @@
 // $Id: mysql.php,v 1.113 2008/11/23 20:30:29 quipo Exp $
 //
 
-require_once 'MDB2/Driver/Manager/Common.php';
+oc_require_once('MDB2/Driver/Manager/Common.php');
 
 /**
  * MDB2 MySQL driver for the management modules
diff --git a/inc/MDB2/Driver/Manager/pgsql.php b/inc/MDB2/Driver/Manager/pgsql.php
index 3f9c121a50d885f7daf3dd231abe7a19d7529310..1a7e851897c0feae25eaafb5e02d995bafbbc388 100755
--- a/inc/MDB2/Driver/Manager/pgsql.php
+++ b/inc/MDB2/Driver/Manager/pgsql.php
@@ -44,7 +44,7 @@
 //
 // $Id: pgsql.php,v 1.87 2008/11/29 14:09:59 afz Exp $
 
-require_once 'MDB2/Driver/Manager/Common.php';
+oc_require_once('MDB2/Driver/Manager/Common.php');
 
 /**
  * MDB2 MySQL driver for the management modules
diff --git a/inc/MDB2/Driver/Manager/sqlite.php b/inc/MDB2/Driver/Manager/sqlite.php
index a0f899e7888d3fe6706f27fb8c216155cc819525..85751d39a3ff7422467ad3c2756119df4b6363da 100755
--- a/inc/MDB2/Driver/Manager/sqlite.php
+++ b/inc/MDB2/Driver/Manager/sqlite.php
@@ -46,7 +46,7 @@
 // $Id: sqlite.php,v 1.76 2008/05/31 11:48:48 quipo Exp $
 //
 
-require_once 'MDB2/Driver/Manager/Common.php';
+oc_require_once('MDB2/Driver/Manager/Common.php');
 
 /**
  * MDB2 SQLite driver for the management modules
diff --git a/inc/MDB2/Driver/Reverse/mysql.php b/inc/MDB2/Driver/Reverse/mysql.php
index ab1f601ef0597a2e4211528732b01784bb67cafd..40c62da3baaf0361987cbe28dacb7fd3eff240a9 100755
--- a/inc/MDB2/Driver/Reverse/mysql.php
+++ b/inc/MDB2/Driver/Reverse/mysql.php
@@ -45,7 +45,7 @@
 // $Id: mysql.php,v 1.80 2008/03/26 21:15:37 quipo Exp $
 //
 
-require_once 'MDB2/Driver/Reverse/Common.php';
+oc_require_once('MDB2/Driver/Reverse/Common.php');
 
 /**
  * MDB2 MySQL driver for the schema reverse engineering module
diff --git a/inc/MDB2/Driver/Reverse/pgsql.php b/inc/MDB2/Driver/Reverse/pgsql.php
index e8e217cecf94c1b728593cd32ceb013578ceb51b..d010292cd8c48e66f3e6a55ea47acb450453d361 100755
--- a/inc/MDB2/Driver/Reverse/pgsql.php
+++ b/inc/MDB2/Driver/Reverse/pgsql.php
@@ -45,7 +45,7 @@
 //
 // $Id: pgsql.php,v 1.75 2008/08/22 16:36:20 quipo Exp $
 
-require_once 'MDB2/Driver/Reverse/Common.php';
+oc_require_once('MDB2/Driver/Reverse/Common.php');
 
 /**
  * MDB2 PostGreSQL driver for the schema reverse engineering module
diff --git a/inc/MDB2/Driver/Reverse/sqlite.php b/inc/MDB2/Driver/Reverse/sqlite.php
index 432e5ecb9182efa78a157835498c6cdc7ac71ec5..1b85aa71f9af1302d05b1c3989e480d7f5996bb5 100755
--- a/inc/MDB2/Driver/Reverse/sqlite.php
+++ b/inc/MDB2/Driver/Reverse/sqlite.php
@@ -46,7 +46,7 @@
 // $Id: sqlite.php,v 1.80 2008/05/03 10:30:14 quipo Exp $
 //
 
-require_once 'MDB2/Driver/Reverse/Common.php';
+oc_require_once('MDB2/Driver/Reverse/Common.php');
 
 /**
  * MDB2 SQlite driver for the schema reverse engineering module
diff --git a/inc/MDB2/Driver/mysql.php b/inc/MDB2/Driver/mysql.php
index fbb00cc6bbd1fd942aa1822bd1aa8278354419c2..091c479d1944f0bd74e45b24e587251bc7f28045 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 69db267d69e63cc503f753a03f1a1ab5d739949c..2cdf67afa923334e979ad8b1ec4391c37a387f40 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 0000000000000000000000000000000000000000..44518b32658bb1e41c0a827c956157919d9e3004
--- /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 0000000000000000000000000000000000000000..ed31ba03bdfdd883ab1673f8681886740978ed5b
--- /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 0000000000000000000000000000000000000000..01318473fddf0163f89626aa14d0002ce5ed53f3
--- /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 0000000000000000000000000000000000000000..b208abc83a383c6a4d792b7c25b9bbcedb10b6a8
--- /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 0000000000000000000000000000000000000000..74ac688578015f7c552854918cbe97ee4977a96e
--- /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 0000000000000000000000000000000000000000..4f0575e0bb1018d21ad7cdc1f50c124f8f5b98f6
--- /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 0000000000000000000000000000000000000000..57fe12ddcab96ac1835ce133c68291add516a2f3
--- /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 0000000000000000000000000000000000000000..d358e9c12f033a42b806fb5645a4caa9d1a74ed5
--- /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 0000000000000000000000000000000000000000..9689a0f6d73ef6ee01d01246a1f4e1b449af6333
--- /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 0000000000000000000000000000000000000000..fab1e03e250de7206d7ccf7f195354fb8edfc4a2
--- /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 0000000000000000000000000000000000000000..21be024ce9fbbc883ea200aecbad1f7c038abcf7
--- /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 0000000000000000000000000000000000000000..5ae4918dc1d086af46f6e297a72c7af9d4105b1f
--- /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 c8e45947c1adddbe9293a594b8aac009569101a6..6f77b5c66da83f05e142eab15b4b6e7e5b869588 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 4fcba692b4f6015c26a2bb913dda9ff895ec52c4..5a2a4c82668edd7f2a0ba97c5da072214bddde38 100755
--- a/inc/lib_base.php
+++ b/inc/lib_base.php
@@ -81,7 +81,9 @@ 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');
+oc_require_once('lib_remotestorage.php');
 
 
 if(!is_dir($CONFIG_DATADIRECTORY_ROOT)){
@@ -312,6 +314,7 @@ class OC_UTIL {
  */
 class OC_DB {
 	static private $DBConnection=false;
+	static private $schema=false;
 	static private $affected=0;
 	static private $result=false;
 	/**
@@ -327,8 +330,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' => true,
+				'quote_identifier' => true,
 			);
 			if($CONFIG_DBTYPE=='sqlite'){
 				$dsn = array(
@@ -344,14 +350,22 @@ class OC_DB {
 					'hostspec' => $CONFIG_DBHOST,
 					'database' => $CONFIG_DBNAME,
 				);
+			}elseif($CONFIG_DBTYPE=='pgsql'){
+				$dsn = array(
+					'phptype'  => 'pgsql',
+					'username' => $CONFIG_DBUSER,
+					'password' => $CONFIG_DBPASSWORD,
+					'hostspec' => $CONFIG_DBHOST,
+					'database' => $CONFIG_DBNAME,
+				);
 			}
-			self::$DBConnection=MDB2::connect($dsn,$options);
-			if (@PEAR::isError(self::$DBConnection)) {
+			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(self::$DBConnection);
 		}
 	}
 	
@@ -369,6 +383,8 @@ class OC_DB {
 		OC_DB::connect();
 		if($CONFIG_DBTYPE=='sqlite'){//fix differences between sql versions
 			$cmd=str_replace('`','',$cmd);
+		}elseif($CONFIG_DBTYPE=='pgsql'){
+			$cmd=str_replace('`','"',$cmd);
 		}
 		$result=self::$DBConnection->exec($cmd);
 		if (PEAR::isError($result)) {
@@ -390,7 +406,19 @@ class OC_DB {
    */
 	static function select($cmd){
 		OC_DB::connect();
-		return self::$DBConnection->queryAll($cmd);
+		global $CONFIG_DBTYPE;
+		if($CONFIG_DBTYPE=='sqlite'){//fix differences between sql versions
+			$cmd=str_replace('`','',$cmd);
+		}elseif($CONFIG_DBTYPE=='pgsql'){
+			$cmd=str_replace('`','"',$cmd);
+		}
+		$result=self::$DBConnection->queryAll($cmd);
+		if (PEAR::isError($result)) {
+			$entry='DB Error: "'.$result->getMessage().'"<br />';
+			$entry.='Offending command was: '.$cmd.'<br />';
+			die($entry);
+		}
+		return $result;
 	} 
 	
 	/**
@@ -425,12 +453,8 @@ class OC_DB {
 	* @return primarykey
 	*/
 	static function insertid() {
-		global $CONFIG_DBTYPE;
-		if($CONFIG_DBTYPE=='sqlite'){
-		return self::$DBConnection->lastInsertRowid();
-		}elseif($CONFIG_DBTYPE=='mysql'){
-		return(mysqli_insert_id(self::$DBConnection));
-		}
+		$id=self::$DBConnection->lastInsertID();
+		return $id;
 	}
 
 	/**
@@ -505,7 +529,41 @@ 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();
+		global $CONFIG_DBNAME;
+		global $CONFIG_DBTABLEPREFIX;
+		$content=file_get_contents($file);
+		$file2=tempnam(sys_get_temp_dir(),'oc_db_scheme_');
+		echo $content;
+		$content=str_replace('*dbname*',$CONFIG_DBNAME,$content);
+		$content=str_replace('*dbprefix*',$CONFIG_DBTABLEPREFIX,$content);
+		echo $content;
+		file_put_contents($file2,$content);
+		$definition=@self::$schema->parseDatabaseDefinitionFile($file2);
+		unlink($file2);
+		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;
+		}
+	}
 }
 
 
@@ -589,12 +647,12 @@ function oc_include_once($file){
 	global $CONFIG_HTTPFORCESSL;
 	global $CONFIG_DATEFORMAT;
 	global $CONFIG_INSTALLED;
-	if(is_file($file)){
-		return include_once($file);
-	}elseif(is_file($SERVERROOT.'/'.$file)){
+	if(is_file($SERVERROOT.'/'.$file)){
 		return include_once($SERVERROOT.'/'.$file);
 	}elseif(is_file($SERVERROOT.'/inc/'.$file)){
 		return include_once($SERVERROOT.'/inc/'.$file);
+	}elseif(is_file($file)){
+		return include_once($file);
 	}
 }
 
diff --git a/inc/lib_config.php b/inc/lib_config.php
index 986d12f4dce026901563c632cba950597eb4b22b..cbf86b17aa1f0975804d16d613bfbfd3aaf1dc92 100755
--- a/inc/lib_config.php
+++ b/inc/lib_config.php
@@ -25,6 +25,7 @@ class OC_CONFIG{
     global $CONFIG_HTTPFORCESSL;
     global $CONFIG_DATEFORMAT;
     global $CONFIG_DBNAME;
+	global $CONFIG_DBTABLEPREFIX;
     global $CONFIG_INSTALLED;
 		$allow=false;
 		if(!$CONFIG_INSTALLED){
@@ -130,6 +131,7 @@ class OC_CONFIG{
 			global $WEBROOT;
 			global $CONFIG_DBHOST;
 			global $CONFIG_DBNAME;
+			global $CONFIG_DBTABLEPREFIX;
 			global $CONFIG_INSTALLED;
 			global $CONFIG_DBUSER;
 			global $CONFIG_DBPASSWORD;
@@ -184,13 +186,17 @@ class OC_CONFIG{
 						//create/fill database
 						$CONFIG_DBTYPE=$dbtype;
 						$CONFIG_DBNAME=$_POST['dbname'];
-						if($dbtype=='mysql'){
+						if($dbtype!='sqlite'){
+							$CONFIG_DBTABLEPREFIX=$_POST['dbtableprefix'];
 							$CONFIG_DBHOST=$_POST['dbhost'];
 							$CONFIG_DBUSER=$_POST['dbuser'];
 							$CONFIG_DBPASSWORD=$_POST['dbpassword'];
+						}else{
+							$_POST['dbtableprefix']='';
+							$CONFIG_DBTABLEPREFIX='';
 						}
 						try{
-							if(isset($_POST['createdatabase']) and $CONFIG_DBTYPE=='mysql'){
+							if(isset($_POST['createdatabase']) and $CONFIG_DBTYPE!='sqlite'){
 								self::createdatabase($_POST['dbadminuser'],$_POST['dbadminpwd']);
 							}
 						}catch(Exception $e){
@@ -209,7 +215,6 @@ class OC_CONFIG{
 								self::filldatabase();
 							}
 						}catch(Exception $e){
-							echo 'testin';
 							$error.='error while trying to fill the database<br/>';
 						}
 						if($CONFIG_DBTYPE=='sqlite'){
@@ -241,7 +246,8 @@ class OC_CONFIG{
 					$config.='$CONFIG_DATEFORMAT=\''.$_POST['dateformat']."';\n";
 					$config.='$CONFIG_DBTYPE=\''.$dbtype."';\n";
 					$config.='$CONFIG_DBNAME=\''.$_POST['dbname']."';\n";
-					if($dbtype=='mysql'){
+					$config.='$CONFIG_DBTABLEPREFIX=\''.$_POST['dbtableprefix']."';\n";
+					if($dbtype!='sqlite'){
 						$config.='$CONFIG_DBHOST=\''.$_POST['dbhost']."';\n";
 						$config.='$CONFIG_DBUSER=\''.$_POST['dbuser']."';\n";
 						$config.='$CONFIG_DBPASSWORD=\''.$_POST['dbpassword']."';\n";
@@ -267,169 +273,73 @@ 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(){
+	
+	/**
+	* 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;
 		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
-)
-";
+		//we cant user OC_BD functions here because we need to connect as the administrative user.
+		if($CONFIG_DBTYPE=='mysql'){
+			$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']}`;";
+			$query.="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();
+		}elseif($CONFIG_DBTYPE=='pgsql'){
+			$connection = pg_connect("user='$adminUser' host='$CONFIG_DBHOST' password='$adminPwd'");
+			$query="CREATE USER {$_POST['dbuser']} WITH PASSWORD '{$_POST['dbpassword']}' CREATEDB;";
+			$result = pg_exec($connection, $query);
+			$query="select count(*) from pg_catalog.pg_database where datname = '{$_POST['dbname']}';";
+			$result = pg_exec($connection, $query);
+			if(pg_result($result,0,0)==0){
+				$query="CREATE DATABASE {$_POST['dbname']};";
+				$result = pg_exec($connection, $query);
+				$query="ALTER DATABASE {$_POST['dbname']} OWNER TO {$_POST['dbuser']};";
+				$result = pg_exec($connection, $query);
+			}
+		}
 	}
-      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();
-   }
 }
 ?>
 
diff --git a/inc/lib_connect.php b/inc/lib_connect.php
index ddf03eb6e7494e58ea0a363ece4744d353c84526..9db867715e42e76d884ad66a3d5f7045a3c3fbb6 100644
--- a/inc/lib_connect.php
+++ b/inc/lib_connect.php
@@ -75,9 +75,11 @@ class OC_REMOTE_CLOUD{
 		curl_setopt($ch, CURLOPT_COOKIEFILE,$this->cookiefile); 
 		curl_setopt($ch, CURLOPT_COOKIEJAR,$this->cookiefile); 
 		curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
-		$result=trim(curl_exec($ch));
+		$result=curl_exec($ch);
+		$result=trim($result);
 		$info=curl_getinfo($ch);
 		$httpCode=$info['http_code'];
+		curl_close($ch);
 		if($httpCode==200 or $httpCode==0){
 			return json_decode($result,$assoc);
 		}else{
@@ -120,6 +122,70 @@ class OC_REMOTE_CLOUD{
 		$this->cookiefile=false;
 	}
 	
+	/**
+	* create a new file or directory
+	* @param string $dir
+	* @param string $name
+	* @param string $type
+	*/
+	public function newFile($dir,$name,$type){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('new',array('dir'=>$dir,'name'=>$name,'type'=>$type),true);
+	}
+	
+	/**
+	* deletes a file or directory
+	* @param string $dir
+	* @param string $file
+	*/
+	public function delete($dir,$name){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('delete',array('dir'=>$dir,'file'=>$name),true);
+	}
+	
+	/**
+	* moves a file or directory
+	* @param string $sorceDir
+	* @param string $sorceFile
+	* @param string $targetDir
+	* @param string $targetFile
+	*/
+	public function move($sourceDir,$sourceFile,$targetDir,$targetFile){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('move',array('sourcedir'=>$sourceDir,'source'=>$sourceFile,'targetdir'=>$targetDir,'target'=>$targetFile),true);
+	}
+	
+	/**
+	* copies a file or directory
+	* @param string $sorceDir
+	* @param string $sorceFile
+	* @param string $targetDir
+	* @param string $targetFile
+	*/
+	public function copy($sourceDir,$sourceFile,$targetDir,$targetFile){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('copy',array('sourcedir'=>$sourceDir,'source'=>$sourceFile,'targetdir'=>$targetDir,'target'=>$targetFile),true);
+	}
+	
+	/**
+	* get a file tree
+	* @param string $dir
+	*/
+	public function getTree($dir){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('gettree',array('dir'=>$dir),true);
+	}
+	
 	/**
 	* get the files inside a directory of the remote cloud
 	* @param string $dir
@@ -130,6 +196,53 @@ class OC_REMOTE_CLOUD{
 		}
 		return $this->apiCall('getfiles',array('dir'=>$dir),true);
 	}
+	
+	/**
+	* get a remove file and save it in a temporary file and return the path of the temporary file
+	* @param string $dir
+	* @param string $file
+	* @return string
+	*/
+	public function getFile($dir, $file){
+		if(!$this->connected){
+			return false;
+		}
+		$ch=curl_init();
+		if(!$this->cookiefile){
+			$this->cookiefile=sys_get_temp_dir().'/remoteCloudCookie'.uniqid();
+		}
+		$tmpfile=tempnam(sys_get_temp_dir(),'remoteCloudFile');
+		$fp=fopen($tmpfile,'w+');
+		$url=$this->path.="/files/api.php?action=get&dir=$dir&file=$file";
+		curl_setopt($ch,CURLOPT_URL,$url);
+		curl_setopt($ch, CURLOPT_COOKIEFILE,$this->cookiefile); 
+		curl_setopt($ch, CURLOPT_COOKIEJAR,$this->cookiefile); 
+		curl_setopt($ch, CURLOPT_FILE, $fp);
+		curl_exec($ch);
+		fclose($fp);
+		curl_close($ch);
+		return $tmpfile;
+	}
+	
+	public function sendFile($sourceDir,$sourceFile,$targetDir,$targetFile){
+		global $WEBROOT;
+		$source=$sourceDir.'/'.$sourceFile;
+		$tmp=OC_FILESYSTEM::toTmpFile($source);
+		return $this->sendTmpFile($tmp,$targetDir,$targetFile);
+	}
+	
+	public function sendTmpFile($tmp,$targetDir,$targetFile){
+		$token=sha1(uniqid().$tmp);
+		global $WEBROOT;
+		$file=sys_get_temp_dir().'/'.'remoteCloudFile'.$token;
+		rename($tmp,$file);
+		if((isset($CONFIG_HTTPFORCESSL) and $CONFIG_HTTPFORCESSL) or isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') { 
+			$url = "https://". $_SERVER['SERVER_NAME'] . $WEBROOT;
+		}else{
+			$url = "http://". $_SERVER['SERVER_NAME'] . $WEBROOT;
+		}
+		return $this->apiCall('pull',array('dir'=>$targetDir,'file'=>$targetFile,'token'=>$token,'source'=>$url),true);
+	}
 }
 
 function OC_CONNECT_TEST($path,$user,$password){
@@ -146,6 +259,30 @@ function OC_CONNECT_TEST($path,$user,$password){
 				foreach($files as $file){
 					echo "{$file['type']} {$file['name']}: {$file['size']} bytes<br/>";
 				}
+				echo 'getting file "'.$file['name'].'"...';
+				$size=$file['size'];
+				$file=$remote->getFile('',$file['name']);
+				if(file_exists($file)){
+					$newSize=filesize($file);
+					if($size!=$newSize){
+						echo "fail<br/>Error: $newSize bytes received, $size expected.";
+						echo '<br/><br/>Recieved file:<br/>';
+						readfile($file);
+						unlink($file);
+						return;
+					}
+					OC_FILESYSTEM::fromTmpFile($file,'/remoteFile');
+					echo 'done<br/>';
+					echo 'sending file "burning_avatar.png"...';
+					$res=$remote->sendFile('','burning_avatar.png','','burning_avatar.png');
+					if($res){
+						echo 'done<br/>';
+					}else{
+						echo 'fail<br/>';
+					}
+				}else{
+					echo 'fail<br/>';
+				}
 			}else{
 				echo 'fail<br/>';
 			}
diff --git a/inc/lib_files.php b/inc/lib_files.php
index 1702ef20de6bef2bda088503cad800ce0174022c..8f694536f7ab96075c7acdf861fbfb74b24a8791 100755
--- a/inc/lib_files.php
+++ b/inc/lib_files.php
@@ -54,7 +54,7 @@ class OC_FILES {
 		$dirs=array();
 		$file=array();
 		$files=array();
-		if (OC_FILESYSTEM::is_dir($directory)) {
+		if(OC_FILESYSTEM::is_dir($directory)) {
 			if ($dh = OC_FILESYSTEM::opendir($directory)) {
 			while (($filename = readdir($dh)) !== false) {
 				if($filename<>'.' and $filename<>'..' and substr($filename,0,1)!='.'){
@@ -136,7 +136,11 @@ class OC_FILES {
 			header('Expires: 0');
 			header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
 			header('Pragma: public');
-			header('Content-Length: ' . filesize($filename));
+			if($zip){
+				header('Content-Length: ' . filesize($filename));
+			}else{
+				header('Content-Length: ' . OC_FILESYSTEM::filesize($filename));
+			}
 		}elseif($zip or !OC_FILESYSTEM::file_exists($filename)){
 			header("HTTP/1.0 404 Not Found");
 			die('404 Not Found');
@@ -242,6 +246,44 @@ class OC_FILES {
 	static function getMimeType($path){
 		return OC_FILESYSTEM::getMimeType($path);
 	}
+	
+	/**
+	* get a file tree
+	*
+	* @param  string  path
+	* @return array
+	*/
+	static function getTree($path){
+		return OC_FILESYSTEM::getTree($path);
+	}
+	
+	/**
+	* pull a file from a remote server
+	* @param  string  source
+	* @param  string  token
+	* @param  string  dir
+	* @param  string  file
+	* @return string  guessed mime type
+	*/
+	static function pull($source,$token,$dir,$file){
+		$tmpfile=tempnam(sys_get_temp_dir(),'remoteCloudFile');
+		$fp=fopen($tmpfile,'w+');
+		$url=$source.="/files/pull.php?token=$token";
+		$ch=curl_init();
+		curl_setopt($ch,CURLOPT_URL,$url);
+		curl_setopt($ch, CURLOPT_FILE, $fp);
+		curl_exec($ch);
+		fclose($fp);
+		$info=curl_getinfo($ch);
+		$httpCode=$info['http_code'];
+		curl_close($ch);
+		if($httpCode==200 or $httpCode==0){
+			OC_FILESYSTEM::fromTmpFile($tmpfile,$dir.'/'.$file);
+			return true;
+		}else{
+			return false;
+		}
+	}
 }
 
 function zipAddDir($dir,$zip,$internalDir=''){
@@ -276,4 +318,46 @@ if(!function_exists('sys_get_temp_dir')) {
     }
 }
 
+global $FAKEDIRS;
+$FAKEDIRS=array();
+
+class fakeDirStream{
+	private $name;
+	private $data;
+	private $index;
+	
+	public function dir_opendir($path,$options){
+		global $FAKEDIRS;
+		$url=parse_url($path);
+		$this->name=substr($path,strlen('fakedir://'));
+		$this->index=0;
+		if(isset($FAKEDIRS[$this->name])){
+			$this->data=$FAKEDIRS[$this->name];
+		}else{
+			$this->data=array();
+		}
+		return true;
+	}
+	
+	public function dir_readdir(){
+		if($this->index>=count($this->data)){
+			return false;
+		}
+		$filename=$this->data[$this->index];
+		$this->index++;
+		return $filename;
+	}
+	
+	public function dir_closedir() {
+		$this->data=false;
+		$this->name='';
+		return true;
+	}
+
+	public function dir_rewinddir() {
+		$this->index=0;
+		return true;
+	}
+}
+stream_wrapper_register("fakedir", "fakeDirStream");
 ?>
\ No newline at end of file
diff --git a/inc/lib_filestorage.php b/inc/lib_filestorage.php
index 85382a444471425a2aa0218169cd136ec4499196..10266d4eaf6d978a49e8e6ffb2c49ac302ed6878 100755
--- a/inc/lib_filestorage.php
+++ b/inc/lib_filestorage.php
@@ -62,7 +62,7 @@ class OC_FILESTORAGE{
 	public function filemtime($path){}
 	public function fileatime($path){}
 	public function file_get_contents($path){}
-	public function file_put_contents($path){}
+	public function file_put_contents($path,$data){}
 	public function unlink($path){}
 	public function rename($path1,$path2){}
 	public function copy($path1,$path2){}
@@ -149,8 +149,8 @@ class OC_FILESTORAGE_LOCAL extends OC_FILESTORAGE{
 		}
 		return $return;
 	}
-	public function file_put_contents($path){
-		if($return=file_put_contents($this->datadir.$path)){
+	public function file_put_contents($path,$data){
+		if($return=file_put_contents($this->datadir.$path,$data)){
 			$this->notifyObservers($path,OC_FILEACTION_WRITE);
 		}
 	}
diff --git a/inc/lib_filesystem.php b/inc/lib_filesystem.php
index 6eb317f442e223a3979148e696e041b6147d66e4..492e0c5d3825c9ee1f647edb9cc38eaad8c93883 100755
--- a/inc/lib_filesystem.php
+++ b/inc/lib_filesystem.php
@@ -199,7 +199,7 @@ class OC_FILESYSTEM{
 			return $storage->file_get_contents(substr($path,strlen(self::getMountPoint($path))));
 		}
 	}
-	static public function file_put_contents($path){
+	static public function file_put_contents($path,$data){
 		if(self::canWrite($path) and $storage=self::getStorage($path)){
 			$this->notifyObservers($path,OC_FILEACTION_WRITE | OC_FILEACTION_CREATE);
 			return $storage->file_put_contents(substr($path,strlen(self::getMountPoint($path))));
diff --git a/inc/lib_log.php b/inc/lib_log.php
index 1244a44a9355ad785fcc67a225c5e0e61bcaa63c..989308c82fcfe02f19368aef11855163c59ad977 100755
--- a/inc/lib_log.php
+++ b/inc/lib_log.php
@@ -48,7 +48,8 @@ class OC_LOG {
    * @param message $message
    */
   public static function event($user,$type,$message){
-    $result = OC_DB::query('insert into log (timestamp,user,type,message) values ("'.time().'","'.addslashes($user).'","'.addslashes($type).'","'.addslashes($message).'")');
+	global $CONFIG_DBTABLEPREFIX;
+    $result = OC_DB::query('INSERT INTO `' . $CONFIG_DBTABLEPREFIX . 'log` (`timestamp`,`user`,`type`,`message`) VALUES ('.time().',\''.addslashes($user).'\','.addslashes($type).',\''.addslashes($message).'\');');
   }
 
 
@@ -57,14 +58,15 @@ class OC_LOG {
    *
    */
   public static function show(){
-    global $CONFIG_DATEFORMAT;
+	global $CONFIG_DATEFORMAT;
+	global $CONFIG_DBTABLEPREFIX;
     echo('<div class="center"><table cellpadding="6" cellspacing="0" border="0" class="log">');
-	
+
 	if(OC_USER::ingroup($_SESSION['username_clean'],'admin')){
-		$result = OC_DB::select('select timestamp,user,type,message from log order by timestamp desc limit 20');
+		$result = OC_DB::select('select `timestamp`,`user`,`type`,`message` from '.$CONFIG_DBTABLEPREFIX.'log order by timestamp desc limit 20');
 	}else{
 		$user=$_SESSION['username_clean'];
-		$result = OC_DB::select('select timestamp,user,type,message from log where user=\''.$user.'\' order by timestamp desc limit 20');
+		$result = OC_DB::select('select `timestamp`,`user`,`type`,`message` from '.$CONFIG_DBTABLEPREFIX.'log where user=\''.$user.'\' order by timestamp desc limit 20');
 	}
     foreach($result as $entry){
       echo('<tr class="browserline">');
diff --git a/inc/lib_ocs.php b/inc/lib_ocs.php
index 52dc95800ab2baef2b617f6841ddbf514a5f32f5..e464d2ed9fbba8145b825cd3ac3137e1142d6bf4 100755
--- a/inc/lib_ocs.php
+++ b/inc/lib_ocs.php
@@ -372,15 +372,16 @@ class OC_OCS {
    * @return string xml/json
    */
   private static function activityget($format,$page,$pagesize) {
+	global $CONFIG_DBTABLEPREFIX;
 
     $user=OC_OCS::checkpassword();
 
-    $result = OC_DB::query('select count(*) as co from log');
+    $result = OC_DB::query("select count(*) as co from {$CONFIG_DBTABLEPREFIX}log");
     $entry=$result->fetchRow();
     $totalcount=$entry['co'];
     OC_DB::free_result($result);
 
-    $result = OC_DB::select('select id,timestamp,user,type,message from log order by timestamp desc limit '.($page*$pagesize).','.$pagesize);
+    $result = OC_DB::select("select id,timestamp,user,type,message from {$CONFIG_DBTABLEPREFIX}log order by timestamp desc limit " . ($page*$pagesize) . ",$pagesize");
     $itemscount=count($result);
 
     $url='http://'.substr($_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'],0,-11).'';
diff --git a/inc/lib_remotestorage.php b/inc/lib_remotestorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8835c1641a4cafe50476bbc0a02414478c968a9
--- /dev/null
+++ b/inc/lib_remotestorage.php
@@ -0,0 +1,353 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Frank Karlitschek 
+* @copyright 2010 Frank Karlitschek karlitschek@kde.org 
+* 
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either 
+* version 3 of the License, or any later version.
+* 
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*  
+* You should have received a copy of the GNU Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+
+class OC_FILESTORAGE_REMOTE extends OC_FILESTORAGE{
+	private $url;
+	private $username;
+	private $password;
+	private $remote=false;
+	private $statCache;
+	private $statCacheDir=false;
+	private $changed=array();
+	
+	private function cacheDir($dir){
+		if($this->statCacheDir!=$dir or $this->statCacheDir===false){
+			$this->statCache=$this->remote->getFiles($dir);
+			$keys=array_keys($this->statCache);
+			$this->statCacheDir=$dir;
+		}
+	}
+	
+	public function __construct($arguments){
+		$this->url=$arguments['url'];
+		$this->username=$arguments['username'];
+		$this->password=$arguments['password'];
+	}
+	private function connect(){
+		if($this->remote===false){
+			$this->remote=OC_CONNECT::connect($this->url,$this->username,$this->password);
+		}
+	}
+	public function mkdir($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$return=$this->remote->newFile($parent,$name,'dir');
+		if($return){
+			$this->notifyObservers($path,OC_FILEACTION_CREATE);
+		}
+		return $return;
+	}
+	public function rmdir($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$return=$this->remote->delete($parent,$name);
+		if($return){
+			$this->notifyObservers($path,OC_FILEACTION_DELETE);
+		}
+		return $return;
+	}
+	public function opendir($path){
+		$this->connect();
+		$this->cacheDir($path);
+		$dirs=array_keys($this->statCache);
+		$id=uniqid();
+		global $FAKEDIRS;
+		$FAKEDIRS[$id]=$dirs;
+		if($return=opendir("fakedir://$id")){
+			$this->notifyObservers($path,OC_FILEACTION_READ);
+		}
+		return $return;
+	}
+	public function is_dir($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($path);
+		if($path=='' or $path=='/'){
+			return true;
+		}
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return ($this->statCache[$name]['type'=='dir']);
+	}
+	public function is_file($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return ($this->statCache[$name]['type'!='dir']);
+	}
+	public function stat($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return $false;
+		}
+		return $this->statCache[$name];
+	}
+	public function filetype($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['type'];
+	}
+	public function filesize($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return $false;
+		}
+		return $this->statCache[$name]['size'];
+	}
+	public function is_readable($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['readable'];
+	}
+	public function is_writeable($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['writeable'];
+	}
+	public function file_exists($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		return isset($this->statCache[$name]);
+	}
+	public function readfile($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		readfile($file);
+		unlink($file);
+	}
+	public function filectime($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['ctime'];
+	}
+	public function filemtime($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['mtime'];
+	}
+	public function fileatime($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['atime'];
+	}
+	public function file_get_contents($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		file_get_contents($file);
+		unlink($file);
+	}
+	public function file_put_contents($path,$data){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		$file=tempnam(sys_get_temp_dir(),'oc_');
+		file_put_contents($file,$data);
+		if($return=$this->remote->sendTmpFile($file,$parent,$name)){
+			$this->notifyObservers($path,OC_FILEACTION_WRITE);
+		}
+	}
+	public function unlink($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if($return=$this->remote->delete($paren,$name)){
+			$this->notifyObservers($path,OC_FILEACTION_DELETE);
+		}
+		return $return;
+	}
+	public function rename($path1,$path2){
+		$this->connect();
+		$parent1=dirname($path1);
+		$name1=substr($path1,strlen($parent1)+1);
+		$parent2=dirname($path2);
+		$name2=substr($path2,strlen($parent2)+1);
+		if($return=$this->remote->move($parent1,$name1,$parent2,$name2)){
+			$this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME);
+		}
+		return $return;
+	}
+	public function copy($path1,$path2){
+		$this->connect();
+		$parent1=dirname($path1);
+		$name1=substr($path1,strlen($parent1)+1);
+		$parent2=dirname($path2);
+		$name2=substr($path2,strlen($parent2)+1);
+		if($return=$this->copy->rename($parent1,$name1,$parent2,$name2)){
+			$this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME);
+		}
+		return $return;
+	}
+	public function fopen($path,$mode){
+		$this->connect();
+		$changed=false;
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		if($return=fopen($file,$mode)){
+			switch($mode){
+				case 'r':
+					$this->notifyObservers($path,OC_FILEACTION_READ);
+					break;
+				case 'r+':
+				case 'w+':
+				case 'x+':
+				case 'a+':
+					$this->notifyObservers($path,OC_FILEACTION_READ | OC_FILEACTION_WRITE);
+					$this->changed[]=array('dir'=>$parent,'file'=>$name,'tmp'=>$file);
+					break;
+				case 'w':
+				case 'x':
+				case 'a':
+					$this->notifyObservers($path,OC_FILEACTION_WRITE);
+					$this->changed[]=array('dir'=>$parent,'file'=>$name,'tmp'=>$file);
+					break;
+			}
+		}
+		return $return;
+	}
+	
+	public function getMimeType($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if(substr($name,0,1)=='/'){
+			$name=substr($name,1);
+		}
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['mime'];
+	}
+	
+	public function toTmpFile($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if(substr($name,0,1)=='/'){
+			$name=substr($name,1);
+		}
+		$filename=$this->remote->getFile($parent,$name);
+		if($filename){
+			$this->notifyObservers($path,OC_FILEACTION_READ);
+			return $filename;
+		}else{
+			return false;
+		}
+	}
+	
+	public function fromTmpFile($tmpFile,$path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if($this->remote->sendTmpFile($tmpFile,$parent,$name)){
+			$this->notifyObservers($path,OC_FILEACTION_CREATE);
+			return true;
+		}else{
+			return false;
+		}
+	}
+	
+	public function delTree($dir) {
+		$this->connect();
+		$parent=dirname($dir);
+		$name=substr($dir,strlen($parent)+1);
+		$return=$this->remote->delete($parent,$name);
+		if($return=rmdir($dir)){
+			$this->notifyObservers($dir,OC_FILEACTION_DELETE);
+		}
+		return $return;
+	}
+	
+	public function find($path){
+		return $this->getTree($path);
+	}
+	
+	public function getTree($dir) {
+		$this->connect();
+		if($return=$this->remote->getTree($dir)){
+			$this->notifyObservers($dir,OC_FILEACTION_READ);
+		}
+		return $return;
+	}
+	
+	public function __destruct(){
+		foreach($this->changed as $changed){
+			$this->remote->sendTmpFile($changed['tmp'],$changed['dir'],$changed['file']);
+		}
+	}
+}
+
+?>
diff --git a/inc/lib_user.php b/inc/lib_user.php
index 7a75e02ece2b6466a6b57b7542a8a3818fae3922..896478bf2833aece8ae00d3ec44e182127eb0c07 100755
--- a/inc/lib_user.php
+++ b/inc/lib_user.php
@@ -27,6 +27,14 @@ if(!$CONFIG_INSTALLED){
 	$_SESSION['username_clean']='';
 }
 
+//cache the userid's an groupid's
+if(!isset($_SESSION['user_id_cache'])){
+	$_SESSION['user_id_cache']=array();
+}
+if(!isset($_SESSION['group_id_cache'])){
+	$_SESSION['group_id_cache']=array();
+}
+
 /**
  * Class for usermanagement
  *
@@ -63,14 +71,15 @@ class OC_USER {
 	*
 	*/
 	public static function createuser($username,$password){
-		if(OC_USER::getuserid($username)!=0){
+		global $CONFIG_DBTABLEPREFIX;
+		if(OC_USER::getuserid($username,true)!=0){
 			return false;
 		}else{
-			$password=sha1($password);
 			$usernameclean=strtolower($username);
+			$password=sha1($password);
 			$username=OC_DB::escape($username);
 			$usernameclean=OC_DB::escape($usernameclean);
-			$query="INSERT INTO  `users` (`user_id` ,`user_name` ,`user_name_clean` ,`user_password`) VALUES (NULL ,  '$username',  '$usernameclean',  '$password')";
+			$query="INSERT INTO  `{$CONFIG_DBTABLEPREFIX}users` (`user_name` ,`user_name_clean` ,`user_password`) VALUES ('$username',  '$usernameclean',  '$password')";
 			$result=OC_DB::query($query);
 			return ($result)?true:false;
 		}
@@ -82,11 +91,13 @@ class OC_USER {
 	*
 	*/
 	public static function login($username,$password){
+		global $CONFIG_DBTABLEPREFIX;
+
 		$password=sha1($password);
 		$usernameclean=strtolower($username);
 		$username=OC_DB::escape($username);
 		$usernameclean=OC_DB::escape($usernameclean);
-		$query="SELECT user_id FROM  users WHERE  user_name_clean =  '$usernameclean' AND  user_password =  '$password' LIMIT 1";
+		$query = "SELECT user_id FROM {$CONFIG_DBTABLEPREFIX}users WHERE user_name_clean = '$usernameclean' AND  user_password =  '$password' LIMIT 1";
 		$result=OC_DB::select($query);
 		if(isset($result[0]) && isset($result[0]['user_id'])){
 			$_SESSION['user_id']=$result[0]['user_id'];
@@ -124,9 +135,10 @@ class OC_USER {
 	*
 	*/
 	public static function creategroup($groupname){
-		if(OC_USER::getgroupid($groupname)==0){
+		global $CONFIG_DBTABLEPREFIX;
+		if(OC_USER::getgroupid($groupname,true)==0){
 			$groupname=OC_DB::escape($groupname);
-			$query="INSERT INTO  `groups` (`group_id` ,`group_name`) VALUES (NULL ,  '$groupname')";
+			$query="INSERT INTO  `{$CONFIG_DBTABLEPREFIX}groups` (`group_name`) VALUES ('$groupname')";
 			$result=OC_DB::query($query);
 			return ($result)?true:false;
 		}else{
@@ -138,16 +150,20 @@ class OC_USER {
 	* get the id of a user
 	*
 	*/
-	public static function getuserid($username){
+	public static function getuserid($username,$nocache=false){
+		global $CONFIG_DBTABLEPREFIX;
 		$usernameclean=strtolower($username);
-		$username=OC_DB::escape($username);
+		if(!$nocache and isset($_SESSION['user_id_cache'][$usernameclean])){//try to use cached value to save an sql query
+			return $_SESSION['user_id_cache'][$usernameclean];
+		}
 		$usernameclean=OC_DB::escape($usernameclean);
-		$query="SELECT user_id FROM  users WHERE user_name_clean = '$usernameclean'";
+		$query="SELECT user_id FROM {$CONFIG_DBTABLEPREFIX}users WHERE user_name_clean = '$usernameclean'";
 		$result=OC_DB::select($query);
 		if(!is_array($result)){
 			return 0;
 		}
 		if(isset($result[0]) && isset($result[0]['user_id'])){
+			$_SESSION['user_id_cache'][$usernameclean]=$result[0]['user_id'];
 			return $result[0]['user_id'];
 		}else{
 			return 0;
@@ -158,14 +174,19 @@ class OC_USER {
 	* get the id of a group
 	*
 	*/
-	public static function getgroupid($groupname){
+	public static function getgroupid($groupname,$nocache=false){
+		global $CONFIG_DBTABLEPREFIX;
+		if(!$nocache and isset($_SESSION['group_id_cache'][$groupname])){//try to use cached value to save an sql query
+			return $_SESSION['group_id_cache'][$groupname];
+		}
 		$groupname=OC_DB::escape($groupname);
-		$query="SELECT group_id FROM groups WHERE  group_name = '$groupname'";
+		$query="SELECT group_id FROM {$CONFIG_DBTABLEPREFIX}groups WHERE group_name = '$groupname'";
 		$result=OC_DB::select($query);
 		if(!is_array($result)){
 			return 0;
 		}
 		if(isset($result[0]) && isset($result[0]['group_id'])){
+			$_SESSION['group_id_cache'][$groupname]=$result[0]['group_id'];
 			return $result[0]['group_id'];
 		}else{
 			return 0;
@@ -176,9 +197,13 @@ class OC_USER {
 	* get the name of a group
 	*
 	*/
-	public static function getgroupname($groupid){
+	public static function getgroupname($groupid,$nocache=false){
+		global $CONFIG_DBTABLEPREFIX;
+		if($nocache and $name=array_search($groupid,$_SESSION['group_id_cache'])){//try to use cached value to save an sql query
+			return $name;
+		}
 		$groupid=(integer)$groupid;
-		$query="SELECT group_name FROM  groups WHERE  group_id =  '$groupid' LIMIT 1";
+		$query="SELECT group_name FROM {$CONFIG_DBTABLEPREFIX}groups WHERE group_id = '$groupid' LIMIT 1";
 		$result=OC_DB::select($query);
 		if(isset($result[0]) && isset($result[0]['group_name'])){
 			return $result[0]['group_name'];
@@ -192,10 +217,12 @@ class OC_USER {
 	*
 	*/
 	public static function ingroup($username,$groupname){
+		global $CONFIG_DBTABLEPREFIX;
+
 		$userid=OC_USER::getuserid($username);
 		$groupid=OC_USER::getgroupid($groupname);
 		if($groupid>0 and $userid>0){
-			$query="SELECT user_group_id FROM  user_group WHERE  group_id = $groupid  AND user_id = $userid LIMIT 1";
+			$query="SELECT * FROM  {$CONFIG_DBTABLEPREFIX}user_group WHERE group_id = '$groupid'  AND user_id = '$userid';";
 			$result=OC_DB::select($query);
 			if(isset($result[0]) && isset($result[0]['user_group_id'])){
 				return true;
@@ -212,11 +239,13 @@ class OC_USER {
 	*
 	*/
 	public static function addtogroup($username,$groupname){
+		global $CONFIG_DBTABLEPREFIX;
+
 		if(!OC_USER::ingroup($username,$groupname)){
 			$userid=OC_USER::getuserid($username);
 			$groupid=OC_USER::getgroupid($groupname);
 			if($groupid!=0 and $userid!=0){
-				$query="INSERT INTO `user_group` (`user_group_id` ,`user_id` ,`group_id`) VALUES (NULL ,  '$userid',  '$groupid');";
+				$query="INSERT INTO `{$CONFIG_DBTABLEPREFIX}user_group` (`user_id` ,`group_id`) VALUES ('$userid',  '$groupid');";
 				$result=OC_DB::query($query);
 				if($result){
 					return true;
@@ -240,8 +269,10 @@ class OC_USER {
 	*
 	*/
 	public static function getusergroups($username){
+		global $CONFIG_DBTABLEPREFIX;
+
 		$userid=OC_USER::getuserid($username);
-		$query="SELECT group_id FROM  user_group WHERE  user_id =  '$userid'";
+		$query = "SELECT group_id FROM {$CONFIG_DBTABLEPREFIX}user_group WHERE user_id = '$userid'";
 		$result=OC_DB::select($query);
 		$groups=array();
 		if(is_array($result)){
@@ -258,9 +289,11 @@ class OC_USER {
 	*
 	*/
 	public static function setpassword($username,$password){
+		global $CONFIG_DBTABLEPREFIX;
+
 		$password=sha1($password);
 		$userid=OC_USER::getuserid($username);
-		$query="UPDATE  users SET  user_password = '$password' WHERE  user_id ='$userid'";
+		$query = "UPDATE {$CONFIG_DBTABLEPREFIX}users SET user_password = '$password' WHERE user_id ='$userid'";
 		$result=OC_DB::query($query);
 		if($result){
 			return true;
@@ -274,11 +307,13 @@ class OC_USER {
 	*
 	*/
 	public static function checkpassword($username,$password){
+		global $CONFIG_DBTABLEPREFIX;
+
 		$password=sha1($password);
 		$usernameclean=strtolower($username);
 		$username=OC_DB::escape($username);
 		$usernameclean=OC_DB::escape($usernameclean);
-		$query="SELECT user_id FROM  'users' WHERE  user_name_clean =  '$usernameclean' AND  user_password =  '$password' LIMIT 1";
+		$query = "SELECT user_id FROM '{$CONFIG_DBTABLEPREFIX}users' WHERE user_name_clean = '$usernameclean' AND user_password =  '$password' LIMIT 1";
 		$result=OC_DB::select($query);
 		if(isset($result[0]) && isset($result[0]['user_id']) && $result[0]['user_id']>0){
 			return true;
diff --git a/inc/templates/adminform.php b/inc/templates/adminform.php
index 9846cfee6d3bc979ba160bf81e52479dca4bb3b9..882c0dd3778615d88f264a88d3cdb87080717c29 100755
--- a/inc/templates/adminform.php
+++ b/inc/templates/adminform.php
@@ -10,6 +10,7 @@ if(!$f) die('Error: Config file (config/config.php) is not writable for the webs
 if(!isset($fillDB)) $fillDB=true;
 if(!isset($CONFIG_DBHOST)) $CONFIG_DBHOST='localhost';
 if(!isset($CONFIG_DBUSER)) $CONFIG_DBUSER='owncloud';
+if(!isset($CONFIG_DBTABLEPREFIX)) $CONFIG_DBTABLEPREFIX='oc_';
 $newuserpassword=OC_USER::generatepassword();
 ?>
 <script type="text/javascript">
@@ -27,7 +28,7 @@ function showBackupPath(){
 function dbtypechange(){
     var dropdown=action=document.getElementById('dbtype');
     var type=dropdown.options[dropdown.selectedIndex].value;
-    var inputs=Array('dbhost','dbuser','dbpass','dbpass_retype','dbcreaterow','dbAdminPwd','dbAdminUser','dbname','dbfill');
+    var inputs=Array('dbhost','dbuser','dbpass','dbpass_retype','dbcreaterow','dbAdminPwd','dbAdminUser','dbname','dbfill','dbtableprefix');
     var id,element;
     if(type=='sqlite'){
         for(i in inputs){
@@ -37,7 +38,7 @@ function dbtypechange(){
                 element.style.display='none';
             }
         }
-    }else if(type=='mysql'){
+    }else if(type=='mysql' || type=='pgsql'){
         for(i in inputs){
             id=inputs[i];
             element=document.getElementById(id);
@@ -80,30 +81,43 @@ if($FIRSTRUN){?>
 <select id='dbtype' name="dbtype" onchange='dbtypechange()'>
 <?php
 global $CONFIG_DBTYPE;
-$dbtypes=array();
 if($CONFIG_DBTYPE=='sqlite'){
 	if(is_callable('sqlite_open')){
-		$dbtypes[]='SQLite';
+		echo "<option value='sqlite'>SQLite</option>";
 	}
 	if(is_callable('mysql_connect')){
-		$dbtypes[]='MySQL';
+		echo "<option value='mysql'>MySQL</option>";
 	}
-}else{
+	if(is_callable('pg_connect')){
+		echo "<option value='pgsql'>PostgreSQL</option>";
+	}
+}elseif($CONFIG_DBTYPE=='mysql'){
 	if(is_callable('mysql_connect')){
-		$dbtypes[]='MySQL';
+		echo "<option value='mysql'>MySQL</option>";
 	}
 	if(is_callable('sqlite_open')){
-		$dbtypes[]='SQLite';
+		echo "<option value='sqlite'>SQLite</option>";
+	}
+	if(is_callable('pg_connect')){
+		echo "<option value='pgsql'>PostgreSQL</option>";
+	}
+}elseif($CONFIG_DBTYPE=='pgsql'){
+	if(is_callable('pg_connect')){
+		echo "<option value='pgsql'>PostgreSQL</option>";
+	}
+	if(is_callable('mysql_connect')){
+		echo "<option value='mysql'>MySQL</option>";
+	}
+	if(is_callable('sqlite_open')){
+		echo "<option value='sqlite'>SQLite</option>";
 	}
-}
-foreach($dbtypes as $dbtype){
-	echo "<option value='".strtolower($dbtype)."'>$dbtype</option>";
 }
 ?>
 </select>
 </td></tr>
 <tr id='dbhost'><td>database host:</td><td><input type="text" name="dbhost" size="30" class="formstyle" value='<?php echo($CONFIG_DBHOST);?>'></input></td></tr>
 <tr id='dbname'><td>database name:</td><td><input type="text" name="dbname" size="30" class="formstyle" value='<?php echo($CONFIG_DBNAME);?>'></input></td></tr>
+<tr id='dbtableprefix'><td>database table prefix:</td><td><input type="text" name="dbtableprefix" size="30" class="formstyle" value='<?php echo($CONFIG_DBTABLEPREFIX);?>'></input></td></tr>
 <tr id='dbuser'><td>database user:</td><td><input type="text" name="dbuser" size="30" class="formstyle" value='<?php echo($CONFIG_DBUSER);?>'></input></td></tr>
 <tr id='dbpass'><td>database password:</td><td><input type="password" name="dbpassword" size="30" class="formstyle" value=''></input></td><td>(leave empty to keep current password)</td></tr>
 <tr id='dbpass_retype'><td>retype database password:</td><td><input type="password" name="dbpassword2" size="30" class="formstyle" value=''></input></td></tr>