Преглед изворни кода

Various database migration fixes (#25209)

* String columns with a length higher then 4000 are converted into a CLOB columns automagically - we have to respect this when migrating

* Adding schema migration tests to prevent unnecessary and non-sense migration steps
Fix Oracle autoincrement and unsigned handling

* Fix sqlite integer type for autoincrement

* Use lower case table names - fixes pg

* Fix postgres with default -1 - this only affect pg 9.4 servers - 9.5 seems to work fine
Thomas Müller пре 8 година
родитељ
комит
b55ab6d22a

+ 1 - 1
lib/private/DB/MDB2SchemaManager.php

@@ -84,7 +84,7 @@ class MDB2SchemaManager {
 		} else if ($platform instanceof MySqlPlatform) {
 			return new MySQLMigrator($this->conn, $random, $config, $dispatcher);
 		} else if ($platform instanceof PostgreSqlPlatform) {
-			return new Migrator($this->conn, $random, $config, $dispatcher);
+			return new PostgreSqlMigrator($this->conn, $random, $config, $dispatcher);
 		} else {
 			return new NoCheckMigrator($this->conn, $random, $config, $dispatcher);
 		}

+ 20 - 0
lib/private/DB/Migrator.php

@@ -33,6 +33,8 @@ use \Doctrine\DBAL\Schema\Table;
 use \Doctrine\DBAL\Schema\Schema;
 use \Doctrine\DBAL\Schema\SchemaConfig;
 use \Doctrine\DBAL\Schema\Comparator;
+use Doctrine\DBAL\Types\StringType;
+use Doctrine\DBAL\Types\Type;
 use OCP\IConfig;
 use OCP\Security\ISecureRandom;
 use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -194,7 +196,25 @@ class Migrator {
 		return new Table($newName, $table->getColumns(), $newIndexes, array(), 0, $table->getOptions());
 	}
 
+	/**
+	 * @param Schema $targetSchema
+	 * @param \Doctrine\DBAL\Connection $connection
+	 * @return \Doctrine\DBAL\Schema\SchemaDiff
+	 * @throws DBALException
+	 */
 	protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
+		// adjust varchar columns with a length higher then getVarcharMaxLength to clob
+		foreach ($targetSchema->getTables() as $table) {
+			foreach ($table->getColumns() as $column) {
+				if ($column->getType() instanceof StringType) {
+					if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) {
+						$column->setType(Type::getType('text'));
+						$column->setLength(null);
+					}
+				}
+			}
+		}
+
 		$filterExpression = $this->getFilterExpression();
 		$this->connection->getConfiguration()->
 		setFilterSchemaAssetsExpression($filterExpression);

+ 6 - 0
lib/private/DB/OracleMigrator.php

@@ -23,6 +23,7 @@
 
 namespace OC\DB;
 
+use Doctrine\DBAL\Schema\ColumnDiff;
 use Doctrine\DBAL\Schema\Schema;
 
 class OracleMigrator extends NoCheckMigrator {
@@ -39,7 +40,12 @@ class OracleMigrator extends NoCheckMigrator {
 			$tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name);
 			foreach ($tableDiff->changedColumns as $column) {
 				$column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName);
+				// auto increment is not relevant for oracle and can anyhow not be applied on change
+				$column->changedProperties = array_diff($column->changedProperties, ['autoincrement', 'unsigned']);
 			}
+			$tableDiff->changedColumns = array_filter($tableDiff->changedColumns, function (ColumnDiff $column) {
+				return count($column->changedProperties) > 0;
+			});
 		}
 
 		return $schemaDiff;

+ 55 - 0
lib/private/DB/PostgreSqlMigrator.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\DB;
+
+use Doctrine\DBAL\Schema\Schema;
+
+class PostgreSqlMigrator extends Migrator {
+	/**
+	 * @param Schema $targetSchema
+	 * @param \Doctrine\DBAL\Connection $connection
+	 * @return \Doctrine\DBAL\Schema\SchemaDiff
+	 */
+	protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) {
+		$schemaDiff = parent::getDiff($targetSchema, $connection);
+
+		foreach ($schemaDiff->changedTables as $tableDiff) {
+			// fix default value in brackets - pg 9.4 is returning a negative default value in ()
+			// see https://github.com/doctrine/dbal/issues/2427
+			foreach ($tableDiff->changedColumns as $column) {
+				$column->changedProperties = array_filter($column->changedProperties, function ($changedProperties) use ($column) {
+					if ($changedProperties !== 'default') {
+						return true;
+					}
+					$fromDefault = $column->fromColumn->getDefault();
+					$toDefault = $column->column->getDefault();
+					$fromDefault = trim($fromDefault, "()");
+
+					// by intention usage of !=
+					return $fromDefault != $toDefault;
+				});
+			}
+		}
+
+		return $schemaDiff;
+	}
+}

+ 11 - 0
lib/private/DB/SQLiteMigrator.php

@@ -26,6 +26,8 @@ namespace OC\DB;
 
 use Doctrine\DBAL\DBALException;
 use Doctrine\DBAL\Schema\Schema;
+use Doctrine\DBAL\Types\BigIntType;
+use Doctrine\DBAL\Types\Type;
 
 class SQLiteMigrator extends Migrator {
 
@@ -76,6 +78,15 @@ class SQLiteMigrator extends Migrator {
 		$platform->registerDoctrineTypeMapping('smallint unsigned', 'integer');
 		$platform->registerDoctrineTypeMapping('varchar ', 'string');
 
+		// with sqlite autoincrement columns is of type integer
+		foreach ($targetSchema->getTables() as $table) {
+			foreach ($table->getColumns() as $column) {
+				if ($column->getType() instanceof BigIntType && $column->getAutoincrement()) {
+					$column->setType(Type::getType('integer'));
+				}
+			}
+		}
+
 		return parent::getDiff($targetSchema, $connection);
 	}
 }

+ 4 - 3
tests/lib/DB/DBSchemaTest.php

@@ -8,15 +8,17 @@
 
 namespace Test\DB;
 
+use Doctrine\DBAL\Platforms\SqlitePlatform;
 use OC_DB;
 use OCP\Security\ISecureRandom;
+use Test\TestCase;
 
 /**
  * Class DBSchemaTest
  *
  * @group DB
  */
-class DBSchemaTest extends \Test\TestCase {
+class DBSchemaTest extends TestCase {
 	protected $schema_file = 'static://test_db_scheme';
 	protected $schema_file2 = 'static://test_db_scheme2';
 	protected $table1;
@@ -53,7 +55,6 @@ class DBSchemaTest extends \Test\TestCase {
 	 * @medium
 	 */
 	public function testSchema() {
-		$platform = \OC::$server->getDatabaseConnection()->getDatabasePlatform();
 		$this->doTestSchemaCreating();
 		$this->doTestSchemaChanging();
 		$this->doTestSchemaDumping();
@@ -97,7 +98,7 @@ class DBSchemaTest extends \Test\TestCase {
 	 */
 	public function assertTableNotExist($table) {
 		$platform = \OC::$server->getDatabaseConnection()->getDatabasePlatform();
-		if ($platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) {
+		if ($platform instanceof SqlitePlatform) {
 			// sqlite removes the tables after closing the DB
 			$this->assertTrue(true);
 		} else {

+ 99 - 0
tests/lib/DB/SchemaDiffTest.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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 Affero General Public License, version 3,
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\DB;
+
+use Doctrine\DBAL\Schema\SchemaDiff;
+use OC\DB\MDB2SchemaManager;
+use OC\DB\MDB2SchemaReader;
+use OCP\IConfig;
+use Test\TestCase;
+
+/**
+ * Class MigratorTest
+ *
+ * @group DB
+ *
+ * @package Test\DB
+ */
+class SchemaDiffTest extends TestCase {
+	/** @var \Doctrine\DBAL\Connection $connection */
+	private $connection;
+
+	/** @var MDB2SchemaManager */
+	private $manager;
+
+	/** @var IConfig */
+	private $config;
+
+	/** @var string */
+	private $testPrefix;
+
+	protected function setUp() {
+		parent::setUp();
+
+		$this->config = \OC::$server->getConfig();
+		$this->connection = \OC::$server->getDatabaseConnection();
+		$this->manager = new MDB2SchemaManager($this->connection);
+		$this->testPrefix= strtolower($this->getUniqueID($this->config->getSystemValue('dbtableprefix', 'oc_'), 3));
+	}
+
+	protected function tearDown() {
+		$this->manager->removeDBStructure('static://test_db_scheme');
+		parent::tearDown();
+	}
+
+	/**
+	 * @dataProvider providesSchemaFiles
+	 * @param string $xml
+	 */
+	public function testZeroChangeOnSchemaMigrations($xml) {
+
+		$xml = str_replace( '*dbprefix*', $this->testPrefix, $xml );
+		$schemaFile = 'static://test_db_scheme';
+		file_put_contents($schemaFile, $xml);
+
+		// apply schema
+		$this->manager->createDbFromStructure($schemaFile);
+
+		$schemaReader = new MDB2SchemaReader($this->config, $this->connection->getDatabasePlatform());
+		$endSchema = $schemaReader->loadSchemaFromFile($schemaFile);
+
+		// get the diff
+		/** @var SchemaDiff $diff */
+		$migrator = $this->manager->getMigrator();
+		$diff = $this->invokePrivate($migrator, 'getDiff', [$endSchema, $this->connection]);
+
+		// no sql statement is expected
+		$sqls = $diff->toSql($this->connection->getDatabasePlatform());
+		$this->assertEquals([], $sqls);
+	}
+
+	public function providesSchemaFiles() {
+		return [
+			'explicit test on autoincrement' => [file_get_contents(__DIR__ . '/schemDiffData/autoincrement.xml')],
+			'explicit test on clob' => [file_get_contents(__DIR__ . '/schemDiffData/clob.xml')],
+			'explicit test on unsigned' => [file_get_contents(__DIR__ . '/schemDiffData/unsigned.xml')],
+			'explicit test on default -1' => [file_get_contents(__DIR__ . '/schemDiffData/default-1.xml')],
+			'testing core schema' => [file_get_contents(__DIR__ . '/schemDiffData/core.xml')],
+		];
+	}
+}

+ 16 - 0
tests/lib/DB/schemDiffData/autoincrement.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<database>
+	<table>
+		<name>*dbprefix*external_config</name>
+		<declaration>
+			<field>
+				<name>config_id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>6</length>
+			</field>
+		</declaration>
+	</table>
+</database>

+ 14 - 0
tests/lib/DB/schemDiffData/clob.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<database>
+	<table>
+		<name>*dbprefix*external_config</name>
+		<declaration>
+			<field>
+				<name>value</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>100000</length>
+			</field>
+		</declaration>
+	</table>
+</database>

+ 1347 - 0
tests/lib/DB/schemDiffData/core.xml

@@ -0,0 +1,1347 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<database>
+
+	<table>
+
+		<!--
+		Namespaced Key-Value Store for Application Configuration.
+		 - Keys are namespaced per appid.
+		 - E.g. (core, global_cache_gc_lastrun) -> 1385463286
+		-->
+		<name>*dbprefix*appconfig</name>
+
+		<declaration>
+
+			<field>
+				<name>appid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>32</length>
+			</field>
+
+			<field>
+				<name>configkey</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>configvalue</name>
+				<type>clob</type>
+				<notnull>false</notnull>
+			</field>
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Bidirectional Map for Storage Names and Storage Ids.
+		 - Assigns each storage name a unique storage id integer.
+		 - Long storage names are hashed.
+		 - E.g.                     local::/tmp/ <-> 2
+		 - E.g. b5db994aa8c6625100e418406c798269 <-> 27
+		-->
+		<name>*dbprefix*storages</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>numeric_id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>available</name>
+				<type>integer</type>
+				<default>1</default>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>last_checked</name>
+				<type>integer</type>
+			</field>
+		</declaration>
+
+	</table>
+
+	<!-- a list of all mounted storage per user, populated on filesystem setup -->
+	<table>
+
+		<name>*dbprefix*mounts</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>storage_id</name>
+				<type>integer</type>
+				<notnull>true</notnull>
+			</field>
+
+			<!-- fileid of the root of the mount, foreign key: oc_filecache.fileid -->
+			<field>
+				<name>root_id</name>
+				<type>integer</type>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>user_id</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>mount_point</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>4000</length>
+			</field>
+
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Bidirectional Map for Mimetypes and Mimetype Id
+		 - Assigns each mimetype (and supertype) a unique mimetype id integer.
+		 - E.g.     application <-> 5
+		 - E.g. application/pdf <-> 6
+		-->
+		<name>*dbprefix*mimetypes</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>mimetype</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Main file table containing one row for each directory and file.
+		 - Assigns a unique integer fileid to each file (and directory)
+		 - Assigns an etag to each file (and directory)
+		 - Caches various file/dir properties such as:
+		  - path (filename, e.g. files/combinatoricslib-2.0_doc.zip)
+		  - path_hash = md5(path)
+		  - name (basename, e.g. combinatoricslib-2.0_doc.zip)
+		  - size (for directories this is the sum of all contained file sizes)
+		-->
+		<name>*dbprefix*filecache</name>
+
+		<declaration>
+
+			<field>
+				<name>fileid</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<length>4</length>
+			</field>
+
+			<!-- Foreign Key storages::numeric_id -->
+			<field>
+				<name>storage</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>path</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>4000</length>
+			</field>
+
+			<field>
+				<name>path_hash</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>32</length>
+			</field>
+
+			<!-- Foreign Key filecache::fileid -->
+			<field>
+				<name>parent</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>name</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>250</length>
+			</field>
+
+			<!-- Foreign Key mimetypes::id -->
+			<field>
+				<name>mimetype</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<!-- Foreign Key mimetypes::id -->
+			<field>
+				<name>mimepart</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>size</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>8</length>
+			</field>
+
+			<field>
+				<name>mtime</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>storage_mtime</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>encrypted</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+            <field>
+                <name>unencrypted_size</name>
+                <type>integer</type>
+                <default>0</default>
+                <notnull>true</notnull>
+                <length>8</length>
+            </field>
+
+			<field>
+				<name>etag</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>40</length>
+			</field>
+
+            <field>
+                <name>permissions</name>
+                <type>integer</type>
+                <default>0</default>
+                <notnull>false</notnull>
+                <length>4</length>
+            </field>
+
+			<field>
+				<name>checksum</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>255</length>
+			</field>
+
+
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Stores which groups have which users as members in an n:m relationship.
+		 - Maps group id (gid) to a set of users (uid)
+		 - Maps user id (uid) to a set of groups (gid) (but without index)
+		-->
+		<name>*dbprefix*group_user</name>
+
+		<declaration>
+
+			<!-- Foreign Key groups::gid -->
+			<field>
+				<name>gid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+			<field>
+				<name>uid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Stores which groups have which users as admins in an n:m relationship.
+		 - Maps group id (gid) to a set of users (uid)
+		 - Maps user id (uid) to a set of groups (gid)
+
+		NOTE: This could (very likely) be reduced to a single bit in group_user
+		      instead of repeating varchars gid and uid here
+		-->
+		<name>*dbprefix*group_admin</name>
+
+		<declaration>
+
+			<!-- Foreign Key groups::gid -->
+			<field>
+				<name>gid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+			<field>
+				<name>uid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		A simple list of groups.
+		-->
+		<name>*dbprefix*groups</name>
+
+		<declaration>
+
+			<field>
+				<name>gid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Namespaced Key-Value Store for User Preferences
+		 - Keys are namespaced per userid and appid.
+		 - E.g. (admin, files, cache_version) -> 5
+		-->
+		<name>*dbprefix*preferences</name>
+
+		<declaration>
+
+			<!-- Foreign Key users::uid -->
+			<field>
+				<name>userid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>appid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>32</length>
+			</field>
+
+			<field>
+				<name>configkey</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>configvalue</name>
+				<type>clob</type>
+				<notnull>false</notnull>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		WebDAV properties.
+		-->
+		<name>*dbprefix*properties</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<autoincrement>1</autoincrement>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+			<field>
+				<name>userid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>propertypath</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+			<field>
+				<name>propertyname</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+			<field>
+				<name>propertyvalue</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Shares of all types (user-to-user, external-via-link, etc.)
+		-->
+		<name>*dbprefix*share</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<autoincrement>1</autoincrement>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<!-- Constant OCP\Share::SHARE_TYPE_* -->
+			<field>
+				<name>share_type</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
+			<!-- Foreign Key users::uid or NULL -->
+			<field>
+				<name>share_with</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>255</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+            <!-- This is the owner of the share
+                 which does not have to be the initiator of the share -->
+			<field>
+				<name>uid_owner</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+            <!-- This is the initiator of the share -->
+			<field>
+				<name>uid_initiator</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>64</length>
+			</field>
+
+
+
+			<!-- Foreign Key share::id or NULL -->
+			<field>
+				<name>parent</name>
+				<type>integer</type>
+				<notnull>false</notnull>
+				<length>4</length>
+			</field>
+
+			<!-- E.g. file or folder -->
+			<field>
+				<name>item_type</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<!-- Foreign Key filecache::fileid -->
+			<field>
+				<name>item_source</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>255</length>
+			</field>
+
+			<field>
+				<name>item_target</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>255</length>
+			</field>
+
+			<!-- Foreign Key filecache::fileid -->
+			<field>
+				<name>file_source</name>
+				<type>integer</type>
+				<notnull>false</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>file_target</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>512</length>
+			</field>
+
+			<!-- Permission bitfield -->
+			<field>
+				<name>permissions</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
+			<!-- Time of share creation -->
+			<field>
+				<name>stime</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>8</length>
+			</field>
+
+			<!-- Whether the receiver accepted the share, if share_with is set. -->
+			<field>
+				<name>accepted</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
+			<!-- Time of share expiration -->
+			<field>
+				<name>expiration</name>
+				<type>timestamp</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<name>token</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>32</length>
+			</field>
+
+			<field>
+				<name>mail_send</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Scheduled background jobs.
+		See OC\BackgroundJob\JobList.
+		-->
+		<name>*dbprefix*jobs</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>class</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+			<field>
+				<name>argument</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4000</length>
+			</field>
+
+			<field>
+				<!-- timestamp when the job was executed the last time -->
+				<name>last_run</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<!-- timestamp when the job was checked if it needs execution the last time -->
+				<name>last_checked</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<!-- timestamp when the job was reserved the last time, 1 day timeout -->
+				<name>reserved_at</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		List of usernames, their display name and login password.
+		-->
+		<name>*dbprefix*users</name>
+
+		<declaration>
+
+			<field>
+				<name>uid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>displayname</name>
+				<type>text</type>
+				<default></default>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>password</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+
+		</declaration>
+
+	</table>
+
+	<table>
+		<name>*dbprefix*authtoken</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+			<field>
+				<name>uid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>login_name</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>password</name>
+				<type>clob</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<name>name</name>
+				<type>clob</type>
+				<default></default>
+				<notnull>true</notnull>
+			</field>
+
+			<field>
+				<name>token</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>200</length>
+			</field>
+
+			<field>
+				<name>type</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>2</length>
+			</field>
+
+			<field>
+				<name>last_activity</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+		</declaration>
+	</table>
+
+	<table>
+
+		<!--
+		List of tags (category) + a unique tag id (id) per user (uid) and type.
+		-->
+		<name>*dbprefix*vcategory</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<!-- Foreign Key users::uid -->
+			<field>
+				<name>uid</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>type</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>category</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+		</declaration>
+	</table>
+
+	<table>
+
+		<!--
+		Object-Tag associations per tag type.
+		-->
+		<name>*dbprefix*vcategory_to_object</name>
+
+		<declaration>
+
+		<field>
+			<name>objid</name>
+			<type>integer</type>
+			<default>0</default>
+			<notnull>true</notnull>
+			<unsigned>true</unsigned>
+			<length>4</length>
+		</field>
+
+		<!-- Foreign Key vcategory::id -->
+		<field>
+			<name>categoryid</name>
+			<type>integer</type>
+			<default>0</default>
+			<notnull>true</notnull>
+			<unsigned>true</unsigned>
+			<length>4</length>
+		</field>
+
+		<field>
+			<name>type</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>64</length>
+		</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+		<!--
+		List of system-wide tags
+		-->
+		<name>*dbprefix*systemtag</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<!-- Tag name -->
+			<field>
+				<name>name</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<!-- Visibility: 0 user-not-visible, 1 user-visible -->
+			<field>
+				<name>visibility</name>
+				<type>integer</type>
+				<default>1</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
+			<!-- Editable: 0 user-not-editable, 1 user-editable -->
+			<field>
+				<name>editable</name>
+				<type>integer</type>
+				<default>1</default>
+				<notnull>true</notnull>
+				<length>1</length>
+			</field>
+
+		</declaration>
+	</table>
+
+	<table>
+
+		<!--
+		System tag to object associations per object type.
+		-->
+		<name>*dbprefix*systemtag_object_mapping</name>
+
+		<declaration>
+
+		<!-- object id (ex: file id for files)-->
+		<field>
+			<name>objectid</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>64</length>
+		</field>
+
+		<!-- object type (ex: "files")-->
+		<field>
+			<name>objecttype</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>64</length>
+		</field>
+
+		<!-- Foreign Key systemtag::id -->
+		<field>
+			<name>systemtagid</name>
+			<type>integer</type>
+			<default>0</default>
+			<notnull>true</notnull>
+			<unsigned>true</unsigned>
+			<length>4</length>
+		</field>
+
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		System tag to group mapping
+		-->
+		<name>*dbprefix*systemtag_group</name>
+
+		<declaration>
+
+		<!-- Foreign Key systemtag::id -->
+		<field>
+			<name>systemtagid</name>
+			<type>integer</type>
+			<default>0</default>
+			<notnull>true</notnull>
+			<unsigned>true</unsigned>
+			<length>4</length>
+		</field>
+
+		<field>
+			<name>gid</name>
+			<type>string</type>
+			<notnull>true</notnull>
+		</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Namespaced Key-Value Store for arbitrary data.
+		 - Keys are namespaced per userid and appid.
+		 - E.g. (admin, files, foo) -> bar
+		-->
+		<name>*dbprefix*privatedata</name>
+
+		<declaration>
+
+		<field>
+			<name>keyid</name>
+			<type>integer</type>
+			<default>0</default>
+			<notnull>true</notnull>
+			<unsigned>true</unsigned>
+			<length>4</length>
+			<autoincrement>1</autoincrement>
+		</field>
+
+		<!-- Foreign Key users::uid -->
+		<field>
+			<name>user</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>64</length>
+		</field>
+
+		<field>
+			<name>app</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>255</length>
+		</field>
+
+		<field>
+			<name>key</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>255</length>
+		</field>
+
+		<field>
+			<name>value</name>
+			<type>text</type>
+			<default></default>
+			<notnull>true</notnull>
+			<length>255</length>
+		</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+
+		<!--
+		Table for storing transactional file locking
+		-->
+		<name>*dbprefix*file_locks</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+				<autoincrement>1</autoincrement>
+			</field>
+
+			<field>
+				<name>lock</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>key</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>ttl</name>
+				<type>integer</type>
+				<default>-1</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+		<!--
+		default place to store comment data
+		-->
+		<name>*dbprefix*comments</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+				<autoincrement>1</autoincrement>
+			</field>
+
+			<field>
+				<name>parent_id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>topmost_parent_id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>children_count</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>actor_type</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>actor_id</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>message</name>
+				<type>clob</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<name>verb</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>creation_timestamp</name>
+				<type>timestamp</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<name>latest_child_timestamp</name>
+				<type>timestamp</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<name>object_type</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>object_id</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+		<!--
+		default place to store per user and object read markers
+		-->
+		<name>*dbprefix*comments_read_markers</name>
+
+		<declaration>
+
+			<field>
+				<name>user_id</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>marker_datetime</name>
+				<type>timestamp</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<name>object_type</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>object_id</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+	<table>
+		<!--
+		Encrypted credentials storage
+		-->
+		<name>*dbprefix*credentials</name>
+
+		<declaration>
+
+			<field>
+				<name>user</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>identifier</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>credentials</name>
+				<type>clob</type>
+				<notnull>false</notnull>
+			</field>
+
+		</declaration>
+
+	</table>
+
+</database>

+ 51 - 0
tests/lib/DB/schemDiffData/default-1.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<database>
+
+	<table>
+
+		<!--
+		Table for storing transactional file locking
+		-->
+		<name>*dbprefix*file_locks</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+				<autoincrement>1</autoincrement>
+			</field>
+
+			<field>
+				<name>lock</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>key</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<default>(stupid text)</default>
+				<length>64</length>
+			</field>
+
+			<field>
+				<name>ttl</name>
+				<type>integer</type>
+				<default>-1</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+
+		</declaration>
+
+	</table>
+
+</database>

+ 68 - 0
tests/lib/DB/schemDiffData/unsigned.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<database>
+
+	<table>
+
+		<!--
+		Scheduled background jobs.
+		See OC\BackgroundJob\JobList.
+		-->
+		<name>*dbprefix*jobs</name>
+
+		<declaration>
+
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<autoincrement>1</autoincrement>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+
+			<field>
+				<name>class</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+
+			<field>
+				<name>argument</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4000</length>
+			</field>
+
+			<field>
+				<!-- timestamp when the job was executed the last time -->
+				<name>last_run</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<!-- timestamp when the job was checked if it needs execution the last time -->
+				<name>last_checked</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+			<field>
+				<!-- timestamp when the job was reserved the last time, 1 day timeout -->
+				<name>reserved_at</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+			</field>
+
+		</declaration>
+
+	</table>
+
+</database>