Merge pull request #21641 from nextcloud/techdebt/noid/bye-bye-database-xml
Bye bye database xml
This commit is contained in:
commit
c15172bc4e
|
@ -243,21 +243,16 @@ class ConvertType extends Command implements CompletionAwareInterface {
|
|||
$toMS->migrate($currentMigration);
|
||||
}
|
||||
|
||||
$schemaManager = new \OC\DB\MDB2SchemaManager($toDB);
|
||||
$apps = $input->getOption('all-apps') ? \OC_App::getAllApps() : \OC_App::getEnabledApps();
|
||||
foreach ($apps as $app) {
|
||||
$output->writeln('<info> - '.$app.'</info>');
|
||||
if (file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) {
|
||||
$schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml');
|
||||
} else {
|
||||
// Make sure autoloading works...
|
||||
\OC_App::loadApp($app);
|
||||
$fromMS = new MigrationService($app, $fromDB);
|
||||
$currentMigration = $fromMS->getMigration('current');
|
||||
if ($currentMigration !== '0') {
|
||||
$toMS = new MigrationService($app, $toDB);
|
||||
$toMS->migrate($currentMigration, true);
|
||||
}
|
||||
// Make sure autoloading works...
|
||||
\OC_App::loadApp($app);
|
||||
$fromMS = new MigrationService($app, $fromDB);
|
||||
$currentMigration = $fromMS->getMigration('current');
|
||||
if ($currentMigration !== '0') {
|
||||
$toMS = new MigrationService($app, $toDB);
|
||||
$toMS->migrate($currentMigration, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
|
||||
*
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Joas Schilling <coding@schilljs.com>
|
||||
* @author Maxence Lange <maxence@artificial-owl.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* 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
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Core\Command\Db\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use OC\DB\Connection;
|
||||
use OC\DB\MDB2SchemaReader;
|
||||
use OC\DB\MigrationService;
|
||||
use OC\Migration\ConsoleOutput;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IConfig;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class GenerateFromSchemaFileCommand extends GenerateCommand {
|
||||
|
||||
/** @var IConfig */
|
||||
protected $config;
|
||||
|
||||
public function __construct(IConfig $config, IAppManager $appManager, Connection $connection) {
|
||||
parent::__construct($connection, $appManager);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
||||
protected function configure() {
|
||||
parent::configure();
|
||||
|
||||
$this->setName('migrations:generate-from-schema');
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output): int {
|
||||
$appName = $input->getArgument('app');
|
||||
$version = $input->getArgument('version');
|
||||
|
||||
if (!preg_match('/^\d{1,16}$/',$version)) {
|
||||
$output->writeln('<error>The given version is invalid. Only 0-9 are allowed (max. 16 digits)</error>');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$schemaFile = $this->appManager->getAppPath($appName) . '/appinfo/database.xml';
|
||||
if (!file_exists($schemaFile)) {
|
||||
$output->writeln('<error>App ' . $appName . ' does not have a database.xml file</error>');
|
||||
return 2;
|
||||
}
|
||||
|
||||
$reader = new MDB2SchemaReader($this->config, $this->connection->getDatabasePlatform());
|
||||
$schema = new Schema();
|
||||
$reader->loadSchemaFromFile($schemaFile, $schema);
|
||||
|
||||
$schemaBody = $this->schemaToMigration($schema);
|
||||
|
||||
$ms = new MigrationService($appName, $this->connection, new ConsoleOutput($output));
|
||||
|
||||
$date = date('YmdHis');
|
||||
$path = $this->generateMigration($ms, 'Version' . $version . 'Date' . $date, $schemaBody);
|
||||
|
||||
$output->writeln("New migration class has been generated to <info>$path</info>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @return string
|
||||
*/
|
||||
protected function schemaToMigration(Schema $schema) {
|
||||
$content = <<<'EOT'
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
EOT;
|
||||
|
||||
foreach ($schema->getTables() as $table) {
|
||||
$content .= str_replace('{{table-name}}', substr($table->getName(), 3), <<<'EOT'
|
||||
|
||||
if (!$schema->hasTable('{{table-name}}')) {
|
||||
$table = $schema->createTable('{{table-name}}');
|
||||
|
||||
EOT
|
||||
);
|
||||
|
||||
foreach ($table->getColumns() as $column) {
|
||||
$content .= str_replace(['{{name}}', '{{type}}'], [$column->getName(), $column->getType()->getName()], <<<'EOT'
|
||||
$table->addColumn('{{name}}', '{{type}}', [
|
||||
|
||||
EOT
|
||||
);
|
||||
if ($column->getAutoincrement()) {
|
||||
$content .= <<<'EOT'
|
||||
'autoincrement' => true,
|
||||
|
||||
EOT;
|
||||
}
|
||||
$content .= str_replace('{{notnull}}', $column->getNotnull() ? 'true' : 'false', <<<'EOT'
|
||||
'notnull' => {{notnull}},
|
||||
|
||||
EOT
|
||||
);
|
||||
if ($column->getLength() !== null) {
|
||||
$content .= str_replace('{{length}}', $column->getLength(), <<<'EOT'
|
||||
'length' => {{length}},
|
||||
|
||||
EOT
|
||||
);
|
||||
}
|
||||
$default = $column->getDefault();
|
||||
if ($default !== null) {
|
||||
if (is_string($default)) {
|
||||
$default = "'$default'";
|
||||
} elseif (is_bool($default)) {
|
||||
$default = ($default === true) ? 'true' : 'false';
|
||||
}
|
||||
$content .= str_replace('{{default}}', $default, <<<'EOT'
|
||||
'default' => {{default}},
|
||||
|
||||
EOT
|
||||
);
|
||||
}
|
||||
if ($column->getUnsigned()) {
|
||||
$content .= <<<'EOT'
|
||||
'unsigned' => true,
|
||||
|
||||
EOT;
|
||||
}
|
||||
|
||||
$content .= <<<'EOT'
|
||||
]);
|
||||
|
||||
EOT;
|
||||
}
|
||||
|
||||
$content .= <<<'EOT'
|
||||
|
||||
EOT;
|
||||
|
||||
$primaryKey = $table->getPrimaryKey();
|
||||
if ($primaryKey !== null) {
|
||||
$content .= str_replace('{{columns}}', implode('\', \'', $primaryKey->getUnquotedColumns()), <<<'EOT'
|
||||
$table->setPrimaryKey(['{{columns}}']);
|
||||
|
||||
EOT
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($table->getIndexes() as $index) {
|
||||
if ($index->isPrimary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($index->isUnique()) {
|
||||
$content .= str_replace(
|
||||
['{{columns}}', '{{name}}'],
|
||||
[implode('\', \'', $index->getUnquotedColumns()), $index->getName()],
|
||||
<<<'EOT'
|
||||
$table->addUniqueIndex(['{{columns}}'], '{{name}}');
|
||||
|
||||
EOT
|
||||
);
|
||||
} else {
|
||||
$content .= str_replace(
|
||||
['{{columns}}', '{{name}}'],
|
||||
[implode('\', \'', $index->getUnquotedColumns()), $index->getName()],
|
||||
<<<'EOT'
|
||||
$table->addIndex(['{{columns}}'], '{{name}}');
|
||||
|
||||
EOT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$content .= <<<'EOT'
|
||||
}
|
||||
|
||||
EOT;
|
||||
}
|
||||
|
||||
$content .= <<<'EOT'
|
||||
return $schema;
|
||||
EOT;
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
|
@ -109,7 +109,6 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
|
|||
$application->add(new OC\Core\Command\Db\Migrations\StatusCommand(\OC::$server->get(\OC\DB\Connection::class)));
|
||||
$application->add(new OC\Core\Command\Db\Migrations\MigrateCommand(\OC::$server->get(\OC\DB\Connection::class)));
|
||||
$application->add(new OC\Core\Command\Db\Migrations\GenerateCommand(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getAppManager()));
|
||||
$application->add(new OC\Core\Command\Db\Migrations\GenerateFromSchemaFileCommand(\OC::$server->getConfig(), \OC::$server->getAppManager(), \OC::$server->get(\OC\DB\Connection::class)));
|
||||
$application->add(new OC\Core\Command\Db\Migrations\ExecuteCommand(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getConfig()));
|
||||
|
||||
$application->add(new OC\Core\Command\Encryption\Disable(\OC::$server->getConfig()));
|
||||
|
|
|
@ -821,7 +821,6 @@ return array(
|
|||
'OC\\Core\\Command\\Db\\ConvertType' => $baseDir . '/core/Command/Db/ConvertType.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\ExecuteCommand' => $baseDir . '/core/Command/Db/Migrations/ExecuteCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\GenerateCommand' => $baseDir . '/core/Command/Db/Migrations/GenerateCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\GenerateFromSchemaFileCommand' => $baseDir . '/core/Command/Db/Migrations/GenerateFromSchemaFileCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\MigrateCommand' => $baseDir . '/core/Command/Db/Migrations/MigrateCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\StatusCommand' => $baseDir . '/core/Command/Db/Migrations/StatusCommand.php',
|
||||
'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => $baseDir . '/core/Command/Encryption/ChangeKeyStorageRoot.php',
|
||||
|
@ -964,8 +963,6 @@ return array(
|
|||
'OC\\DB\\ConnectionAdapter' => $baseDir . '/lib/private/DB/ConnectionAdapter.php',
|
||||
'OC\\DB\\ConnectionFactory' => $baseDir . '/lib/private/DB/ConnectionFactory.php',
|
||||
'OC\\DB\\Exceptions\\DbalException' => $baseDir . '/lib/private/DB/Exceptions/DbalException.php',
|
||||
'OC\\DB\\MDB2SchemaManager' => $baseDir . '/lib/private/DB/MDB2SchemaManager.php',
|
||||
'OC\\DB\\MDB2SchemaReader' => $baseDir . '/lib/private/DB/MDB2SchemaReader.php',
|
||||
'OC\\DB\\MigrationException' => $baseDir . '/lib/private/DB/MigrationException.php',
|
||||
'OC\\DB\\MigrationService' => $baseDir . '/lib/private/DB/MigrationService.php',
|
||||
'OC\\DB\\Migrator' => $baseDir . '/lib/private/DB/Migrator.php',
|
||||
|
|
|
@ -850,7 +850,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\Core\\Command\\Db\\ConvertType' => __DIR__ . '/../../..' . '/core/Command/Db/ConvertType.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\ExecuteCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/ExecuteCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\GenerateCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/GenerateCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\GenerateFromSchemaFileCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/GenerateFromSchemaFileCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\MigrateCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/MigrateCommand.php',
|
||||
'OC\\Core\\Command\\Db\\Migrations\\StatusCommand' => __DIR__ . '/../../..' . '/core/Command/Db/Migrations/StatusCommand.php',
|
||||
'OC\\Core\\Command\\Encryption\\ChangeKeyStorageRoot' => __DIR__ . '/../../..' . '/core/Command/Encryption/ChangeKeyStorageRoot.php',
|
||||
|
@ -993,8 +992,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\DB\\ConnectionAdapter' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionAdapter.php',
|
||||
'OC\\DB\\ConnectionFactory' => __DIR__ . '/../../..' . '/lib/private/DB/ConnectionFactory.php',
|
||||
'OC\\DB\\Exceptions\\DbalException' => __DIR__ . '/../../..' . '/lib/private/DB/Exceptions/DbalException.php',
|
||||
'OC\\DB\\MDB2SchemaManager' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaManager.php',
|
||||
'OC\\DB\\MDB2SchemaReader' => __DIR__ . '/../../..' . '/lib/private/DB/MDB2SchemaReader.php',
|
||||
'OC\\DB\\MigrationException' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationException.php',
|
||||
'OC\\DB\\MigrationService' => __DIR__ . '/../../..' . '/lib/private/DB/MigrationService.php',
|
||||
'OC\\DB\\Migrator' => __DIR__ . '/../../..' . '/lib/private/DB/Migrator.php',
|
||||
|
|
|
@ -44,6 +44,9 @@ use Doctrine\DBAL\Exception;
|
|||
use Doctrine\DBAL\Exception\ConstraintViolationException;
|
||||
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Result;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Statement;
|
||||
|
@ -509,8 +512,7 @@ class Connection extends \Doctrine\DBAL\Connection {
|
|||
* @throws Exception
|
||||
*/
|
||||
public function createSchema() {
|
||||
$schemaManager = new MDB2SchemaManager($this);
|
||||
$migrator = $schemaManager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
return $migrator->createSchema();
|
||||
}
|
||||
|
||||
|
@ -522,8 +524,26 @@ class Connection extends \Doctrine\DBAL\Connection {
|
|||
* @throws Exception
|
||||
*/
|
||||
public function migrateToSchema(Schema $toSchema) {
|
||||
$schemaManager = new MDB2SchemaManager($this);
|
||||
$migrator = $schemaManager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($toSchema);
|
||||
}
|
||||
|
||||
private function getMigrator() {
|
||||
// TODO properly inject those dependencies
|
||||
$random = \OC::$server->getSecureRandom();
|
||||
$platform = $this->getDatabasePlatform();
|
||||
$config = \OC::$server->getConfig();
|
||||
$dispatcher = \OC::$server->getEventDispatcher();
|
||||
if ($platform instanceof SqlitePlatform) {
|
||||
return new SQLiteMigrator($this, $config, $dispatcher);
|
||||
} elseif ($platform instanceof OraclePlatform) {
|
||||
return new OracleMigrator($this, $config, $dispatcher);
|
||||
} elseif ($platform instanceof MySQLPlatform) {
|
||||
return new MySQLMigrator($this, $config, $dispatcher);
|
||||
} elseif ($platform instanceof PostgreSQL94Platform) {
|
||||
return new PostgreSqlMigrator($this, $config, $dispatcher);
|
||||
} else {
|
||||
return new Migrator($this, $config, $dispatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,161 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Bart Visscher <bartv@thisnet.nl>
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
|
||||
* @author Lukas Reschke <lukas@statuscode.ch>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Robin Appelman <robin@icewind.nl>
|
||||
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Victor Dubiniuk <dubiniuk@owncloud.com>
|
||||
* @author Vincent Petry <vincent@nextcloud.com>
|
||||
*
|
||||
* @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\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
class MDB2SchemaManager {
|
||||
/** @var Connection $conn */
|
||||
protected $conn;
|
||||
|
||||
/**
|
||||
* @param Connection $conn
|
||||
*/
|
||||
public function __construct($conn) {
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates tables from XML file
|
||||
* @param string $file file to read structure from
|
||||
* @return bool
|
||||
*
|
||||
* TODO: write more documentation
|
||||
*/
|
||||
public function createDbFromStructure($file) {
|
||||
$schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform());
|
||||
$toSchema = new Schema([], [], $this->conn->getSchemaManager()->createSchemaConfig());
|
||||
$toSchema = $schemaReader->loadSchemaFromFile($file, $toSchema);
|
||||
return $this->executeSchemaChange($toSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OC\DB\Migrator
|
||||
*/
|
||||
public function getMigrator() {
|
||||
$random = \OC::$server->getSecureRandom();
|
||||
$platform = $this->conn->getDatabasePlatform();
|
||||
$config = \OC::$server->getConfig();
|
||||
$dispatcher = \OC::$server->getEventDispatcher();
|
||||
if ($platform instanceof SqlitePlatform) {
|
||||
return new SQLiteMigrator($this->conn, $config, $dispatcher);
|
||||
} elseif ($platform instanceof OraclePlatform) {
|
||||
return new OracleMigrator($this->conn, $config, $dispatcher);
|
||||
} elseif ($platform instanceof MySQLPlatform) {
|
||||
return new MySQLMigrator($this->conn, $config, $dispatcher);
|
||||
} elseif ($platform instanceof PostgreSQL94Platform) {
|
||||
return new PostgreSqlMigrator($this->conn, $config, $dispatcher);
|
||||
} else {
|
||||
return new Migrator($this->conn, $config, $dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads database schema from file
|
||||
*
|
||||
* @param string $file file to read from
|
||||
* @return \Doctrine\DBAL\Schema\Schema
|
||||
*/
|
||||
private function readSchemaFromFile($file) {
|
||||
$platform = $this->conn->getDatabasePlatform();
|
||||
$schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $platform);
|
||||
$toSchema = new Schema([], [], $this->conn->getSchemaManager()->createSchemaConfig());
|
||||
return $schemaReader->loadSchemaFromFile($file, $toSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
* update the database scheme
|
||||
* @param string $file file to read structure from
|
||||
* @param bool $generateSql only return the sql needed for the upgrade
|
||||
* @return string|boolean
|
||||
*/
|
||||
public function updateDbFromStructure($file, $generateSql = false) {
|
||||
$toSchema = $this->readSchemaFromFile($file);
|
||||
$migrator = $this->getMigrator();
|
||||
|
||||
if ($generateSql) {
|
||||
return $migrator->generateChangeScript($toSchema);
|
||||
} else {
|
||||
$migrator->migrate($toSchema);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Schema\Schema $schema
|
||||
* @return string
|
||||
*/
|
||||
public function generateChangeScript($schema) {
|
||||
$migrator = $this->getMigrator();
|
||||
return $migrator->generateChangeScript($schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all tables defined in a database structure xml file
|
||||
*
|
||||
* @param string $file the xml file describing the tables
|
||||
*/
|
||||
public function removeDBStructure($file) {
|
||||
$schemaReader = new MDB2SchemaReader(\OC::$server->getConfig(), $this->conn->getDatabasePlatform());
|
||||
$toSchema = new Schema([], [], $this->conn->getSchemaManager()->createSchemaConfig());
|
||||
$fromSchema = $schemaReader->loadSchemaFromFile($file, $toSchema);
|
||||
$toSchema = clone $fromSchema;
|
||||
foreach ($toSchema->getTables() as $table) {
|
||||
$toSchema->dropTable($table->getName());
|
||||
}
|
||||
$comparator = new \Doctrine\DBAL\Schema\Comparator();
|
||||
$schemaDiff = $comparator->compare($fromSchema, $toSchema);
|
||||
$this->executeSchemaChange($schemaDiff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Schema\Schema|\Doctrine\DBAL\Schema\SchemaDiff $schema
|
||||
* @return bool
|
||||
*/
|
||||
private function executeSchemaChange($schema) {
|
||||
$this->conn->beginTransaction();
|
||||
foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) {
|
||||
$this->conn->query($sql);
|
||||
}
|
||||
$this->conn->commit();
|
||||
|
||||
if ($this->conn->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||
$this->conn->close();
|
||||
$this->conn->connect();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,355 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
*
|
||||
* @author Bart Visscher <bartv@thisnet.nl>
|
||||
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Oliver Gasser <oliver.gasser@gmail.com>
|
||||
* @author Robin Appelman <robin@icewind.nl>
|
||||
* @author Robin McCorkell <robin@mccorkell.me.uk>
|
||||
* @author Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* @author Victor Dubiniuk <dubiniuk@owncloud.com>
|
||||
* @author Vincent Petry <vincent@nextcloud.com>
|
||||
*
|
||||
* @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\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use OCP\IConfig;
|
||||
|
||||
class MDB2SchemaReader {
|
||||
|
||||
/**
|
||||
* @var string $DBTABLEPREFIX
|
||||
*/
|
||||
protected $DBTABLEPREFIX;
|
||||
|
||||
/**
|
||||
* @var \Doctrine\DBAL\Platforms\AbstractPlatform $platform
|
||||
*/
|
||||
protected $platform;
|
||||
|
||||
/** @var IConfig */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param \OCP\IConfig $config
|
||||
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
|
||||
*/
|
||||
public function __construct(IConfig $config, AbstractPlatform $platform) {
|
||||
$this->platform = $platform;
|
||||
$this->config = $config;
|
||||
$this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param Schema $schema
|
||||
* @return Schema
|
||||
* @throws \DomainException
|
||||
*/
|
||||
public function loadSchemaFromFile($file, Schema $schema) {
|
||||
$loadEntities = libxml_disable_entity_loader(false);
|
||||
$xml = simplexml_load_file($file);
|
||||
libxml_disable_entity_loader($loadEntities);
|
||||
foreach ($xml->children() as $child) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $child
|
||||
*/
|
||||
switch ($child->getName()) {
|
||||
case 'name':
|
||||
case 'create':
|
||||
case 'overwrite':
|
||||
case 'charset':
|
||||
break;
|
||||
case 'table':
|
||||
$this->loadTable($schema, $child);
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException('Unknown element: ' . $child->getName());
|
||||
|
||||
}
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Schema\Schema $schema
|
||||
* @param \SimpleXMLElement $xml
|
||||
* @throws \DomainException
|
||||
*/
|
||||
private function loadTable($schema, $xml) {
|
||||
$table = null;
|
||||
foreach ($xml->children() as $child) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $child
|
||||
*/
|
||||
switch ($child->getName()) {
|
||||
case 'name':
|
||||
$name = (string)$child;
|
||||
$name = str_replace('*dbprefix*', $this->DBTABLEPREFIX, $name);
|
||||
$name = $this->platform->quoteIdentifier($name);
|
||||
$table = $schema->createTable($name);
|
||||
break;
|
||||
case 'create':
|
||||
case 'overwrite':
|
||||
case 'charset':
|
||||
break;
|
||||
case 'declaration':
|
||||
if (is_null($table)) {
|
||||
throw new \DomainException('Table declaration before table name');
|
||||
}
|
||||
$this->loadDeclaration($table, $child);
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException('Unknown element: ' . $child->getName());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Schema\Table $table
|
||||
* @param \SimpleXMLElement $xml
|
||||
* @throws \DomainException
|
||||
*/
|
||||
private function loadDeclaration($table, $xml) {
|
||||
foreach ($xml->children() as $child) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $child
|
||||
*/
|
||||
switch ($child->getName()) {
|
||||
case 'field':
|
||||
$this->loadField($table, $child);
|
||||
break;
|
||||
case 'index':
|
||||
$this->loadIndex($table, $child);
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException('Unknown element: ' . $child->getName());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Schema\Table $table
|
||||
* @param \SimpleXMLElement $xml
|
||||
* @throws \DomainException
|
||||
*/
|
||||
private function loadField($table, $xml) {
|
||||
$options = [ 'notnull' => false ];
|
||||
foreach ($xml->children() as $child) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $child
|
||||
*/
|
||||
switch ($child->getName()) {
|
||||
case 'name':
|
||||
$name = (string)$child;
|
||||
$name = $this->platform->quoteIdentifier($name);
|
||||
break;
|
||||
case 'type':
|
||||
$type = (string)$child;
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
$type = 'string';
|
||||
break;
|
||||
case 'clob':
|
||||
$type = 'text';
|
||||
break;
|
||||
case 'timestamp':
|
||||
$type = 'datetime';
|
||||
break;
|
||||
case 'numeric':
|
||||
$type = 'decimal';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'length':
|
||||
$length = (string)$child;
|
||||
$options['length'] = $length;
|
||||
break;
|
||||
case 'unsigned':
|
||||
$unsigned = $this->asBool($child);
|
||||
$options['unsigned'] = $unsigned;
|
||||
break;
|
||||
case 'notnull':
|
||||
$notnull = $this->asBool($child);
|
||||
$options['notnull'] = $notnull;
|
||||
break;
|
||||
case 'autoincrement':
|
||||
$autoincrement = $this->asBool($child);
|
||||
$options['autoincrement'] = $autoincrement;
|
||||
break;
|
||||
case 'default':
|
||||
$default = (string)$child;
|
||||
$options['default'] = $default;
|
||||
break;
|
||||
case 'comments':
|
||||
$comment = (string)$child;
|
||||
$options['comment'] = $comment;
|
||||
break;
|
||||
case 'primary':
|
||||
$primary = $this->asBool($child);
|
||||
$options['primary'] = $primary;
|
||||
break;
|
||||
case 'precision':
|
||||
$precision = (string)$child;
|
||||
$options['precision'] = $precision;
|
||||
break;
|
||||
case 'scale':
|
||||
$scale = (string)$child;
|
||||
$options['scale'] = $scale;
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException('Unknown element: ' . $child->getName());
|
||||
|
||||
}
|
||||
}
|
||||
if (isset($name) && isset($type)) {
|
||||
if (isset($options['default']) && empty($options['default'])) {
|
||||
if (empty($options['notnull']) || !$options['notnull']) {
|
||||
unset($options['default']);
|
||||
$options['notnull'] = false;
|
||||
} else {
|
||||
$options['default'] = '';
|
||||
}
|
||||
if ($type == 'integer' || $type == 'decimal') {
|
||||
$options['default'] = 0;
|
||||
} elseif ($type == 'boolean') {
|
||||
$options['default'] = false;
|
||||
}
|
||||
if (!empty($options['autoincrement']) && $options['autoincrement']) {
|
||||
unset($options['default']);
|
||||
}
|
||||
}
|
||||
if ($type === 'integer' && isset($options['default'])) {
|
||||
$options['default'] = (int)$options['default'];
|
||||
}
|
||||
if ($type === 'integer' && isset($options['length'])) {
|
||||
$length = $options['length'];
|
||||
if ($length < 4) {
|
||||
$type = 'smallint';
|
||||
} elseif ($length > 4) {
|
||||
$type = 'bigint';
|
||||
}
|
||||
}
|
||||
if ($type === 'boolean' && isset($options['default'])) {
|
||||
$options['default'] = $this->asBool($options['default']);
|
||||
}
|
||||
if (!empty($options['autoincrement'])
|
||||
&& !empty($options['notnull'])
|
||||
) {
|
||||
$options['primary'] = true;
|
||||
}
|
||||
|
||||
# not used anymore in the options argument
|
||||
# see https://github.com/doctrine/dbal/commit/138eb85234a1faeaa2e6a32cd7bcc66bb51c64e8#diff-300f55366adb50a32a40882ebdc95c163b141f64cba5f45f20bda04a907b3eb3L82
|
||||
# therefore it's read before and then unset right before the addColumn call
|
||||
$setPrimaryKey = false;
|
||||
if (!empty($options['primary']) && $options['primary']) {
|
||||
$setPrimaryKey = true;
|
||||
}
|
||||
unset($options['primary']);
|
||||
$table->addColumn($name, $type, $options);
|
||||
if ($setPrimaryKey) {
|
||||
$table->setPrimaryKey([$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Schema\Table $table
|
||||
* @param \SimpleXMLElement $xml
|
||||
* @throws \DomainException
|
||||
*/
|
||||
private function loadIndex($table, $xml) {
|
||||
$name = null;
|
||||
$fields = [];
|
||||
foreach ($xml->children() as $child) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $child
|
||||
*/
|
||||
switch ($child->getName()) {
|
||||
case 'name':
|
||||
$name = (string)$child;
|
||||
break;
|
||||
case 'primary':
|
||||
$primary = $this->asBool($child);
|
||||
break;
|
||||
case 'unique':
|
||||
$unique = $this->asBool($child);
|
||||
break;
|
||||
case 'field':
|
||||
foreach ($child->children() as $field) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $field
|
||||
*/
|
||||
switch ($field->getName()) {
|
||||
case 'name':
|
||||
$field_name = (string)$field;
|
||||
$field_name = $this->platform->quoteIdentifier($field_name);
|
||||
$fields[] = $field_name;
|
||||
break;
|
||||
case 'sorting':
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException('Unknown element: ' . $field->getName());
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException('Unknown element: ' . $child->getName());
|
||||
|
||||
}
|
||||
}
|
||||
if (!empty($fields)) {
|
||||
if (isset($primary) && $primary) {
|
||||
if ($table->hasPrimaryKey()) {
|
||||
return;
|
||||
}
|
||||
$table->setPrimaryKey($fields, $name);
|
||||
} else {
|
||||
if (isset($unique) && $unique) {
|
||||
$table->addUniqueIndex($fields, $name);
|
||||
} else {
|
||||
$table->addIndex($fields, $name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new \DomainException('Empty index definition: ' . $name . ' options:' . print_r($fields, true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \SimpleXMLElement|string $xml
|
||||
* @return bool
|
||||
*/
|
||||
private function asBool($xml) {
|
||||
$result = (string)$xml;
|
||||
if ($result == 'true') {
|
||||
$result = true;
|
||||
} elseif ($result == 'false') {
|
||||
$result = false;
|
||||
}
|
||||
return (bool)$result;
|
||||
}
|
||||
}
|
|
@ -46,8 +46,8 @@ use OC\App\AppStore\Fetcher\AppFetcher;
|
|||
use OC\AppFramework\Bootstrap\Coordinator;
|
||||
use OC\Archive\TAR;
|
||||
use OC\DB\Connection;
|
||||
use OC\DB\MigrationService;
|
||||
use OC_App;
|
||||
use OC_DB;
|
||||
use OC_Helper;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IConfig;
|
||||
|
@ -114,6 +114,11 @@ class Installer {
|
|||
}
|
||||
|
||||
$basedir = $app['path'].'/'.$appId;
|
||||
|
||||
if (is_file($basedir . '/appinfo/database.xml')) {
|
||||
throw new \Exception('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
|
||||
}
|
||||
|
||||
$info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
|
||||
|
||||
$l = \OC::$server->getL10N('core');
|
||||
|
@ -152,16 +157,9 @@ class Installer {
|
|||
}
|
||||
|
||||
//install the database
|
||||
if (is_file($basedir.'/appinfo/database.xml')) {
|
||||
if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) {
|
||||
OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
|
||||
} else {
|
||||
OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml');
|
||||
}
|
||||
} else {
|
||||
$ms = new \OC\DB\MigrationService($info['id'], \OC::$server->get(Connection::class));
|
||||
$ms->migrate('latest', true);
|
||||
}
|
||||
$ms = new MigrationService($info['id'], \OC::$server->get(Connection::class));
|
||||
$ms->migrate('latest', true);
|
||||
|
||||
if ($previousVersion) {
|
||||
OC_App::executeRepairSteps($appId, $info['repair-steps']['post-migration']);
|
||||
}
|
||||
|
@ -601,20 +599,8 @@ class Installer {
|
|||
$appPath = OC_App::getAppPath($app);
|
||||
\OC_App::registerAutoloading($app, $appPath);
|
||||
|
||||
if (is_file("$appPath/appinfo/database.xml")) {
|
||||
try {
|
||||
OC_DB::createDbFromStructure("$appPath/appinfo/database.xml");
|
||||
} catch (TableExistsException $e) {
|
||||
throw new HintException(
|
||||
'Failed to enable app ' . $app,
|
||||
'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.',
|
||||
0, $e
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$ms = new \OC\DB\MigrationService($app, \OC::$server->get(Connection::class));
|
||||
$ms->migrate('latest', true);
|
||||
}
|
||||
$ms = new MigrationService($app, \OC::$server->get(Connection::class));
|
||||
$ms->migrate('latest', true);
|
||||
|
||||
//run appinfo/install.php
|
||||
self::includeAppScript("$appPath/appinfo/install.php");
|
||||
|
|
|
@ -972,6 +972,11 @@ class OC_App {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (is_file($appPath . '/appinfo/database.xml')) {
|
||||
\OC::$server->getLogger()->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
|
||||
return false;
|
||||
}
|
||||
|
||||
\OC::$server->getAppManager()->clearAppsCache();
|
||||
$appData = self::getAppInfo($appId);
|
||||
|
||||
|
@ -987,12 +992,8 @@ class OC_App {
|
|||
self::registerAutoloading($appId, $appPath, true);
|
||||
self::executeRepairSteps($appId, $appData['repair-steps']['pre-migration']);
|
||||
|
||||
if (file_exists($appPath . '/appinfo/database.xml')) {
|
||||
OC_DB::updateDbFromStructure($appPath . '/appinfo/database.xml');
|
||||
} else {
|
||||
$ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class));
|
||||
$ms->migrate();
|
||||
}
|
||||
$ms = new MigrationService($appId, \OC::$server->get(\OC\DB\Connection::class));
|
||||
$ms->migrate();
|
||||
|
||||
self::executeRepairSteps($appId, $appData['repair-steps']['post-migration']);
|
||||
self::setupLiveMigrations($appId, $appData['repair-steps']['live-migration']);
|
||||
|
|
|
@ -30,23 +30,12 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use OCP\ILogger;
|
||||
|
||||
/**
|
||||
* This class manages the access to the database. It basically is a wrapper for
|
||||
* Doctrine with some adaptions.
|
||||
*/
|
||||
class OC_DB {
|
||||
|
||||
/**
|
||||
* get MDB2 schema manager
|
||||
*
|
||||
* @return \OC\DB\MDB2SchemaManager
|
||||
*/
|
||||
private static function getMDB2SchemaManager() {
|
||||
return new \OC\DB\MDB2SchemaManager(\OC::$server->get(\OC\DB\Connection::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a SQL query
|
||||
* @param string $query Query string
|
||||
|
@ -159,45 +148,6 @@ class OC_DB {
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates tables from XML file
|
||||
* @param string $file file to read structure from
|
||||
* @return bool
|
||||
*
|
||||
* TODO: write more documentation
|
||||
*/
|
||||
public static function createDbFromStructure($file) {
|
||||
$schemaManager = self::getMDB2SchemaManager();
|
||||
return $schemaManager->createDbFromStructure($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* update the database schema
|
||||
* @param string $file file to read structure from
|
||||
* @throws Exception
|
||||
* @return string|boolean
|
||||
* @suppress PhanDeprecatedFunction
|
||||
*/
|
||||
public static function updateDbFromStructure($file) {
|
||||
$schemaManager = self::getMDB2SchemaManager();
|
||||
try {
|
||||
$result = $schemaManager->updateDbFromStructure($file);
|
||||
} catch (Exception $e) {
|
||||
\OCP\Util::writeLog('core', 'Failed to update database structure ('.$e.')', ILogger::FATAL);
|
||||
throw $e;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all tables defined in a database structure xml file
|
||||
* @param string $file the xml file describing the tables
|
||||
*/
|
||||
public static function removeDBStructure($file) {
|
||||
$schemaManager = self::getMDB2SchemaManager();
|
||||
$schemaManager->removeDBStructure($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a result is an error and throws an exception, works with \Doctrine\DBAL\Exception
|
||||
* @param mixed $result
|
||||
|
|
|
@ -1,311 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<database>
|
||||
|
||||
<name>*dbname*</name>
|
||||
<create>false</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>utf8</charset>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*cntcts_addrsbks</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
<comments>This is the id</comments>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>userid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>displayname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>description</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>ctag</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>active</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*cntcts_cards</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>addressbookid</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>fullname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>carddata</name>
|
||||
<type>clob</type>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>lastmodified</name>
|
||||
<type>integer</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<name>*dbprefix*timestamp</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<autoincrement>1</autoincrement>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>timestamptest</name>
|
||||
<type>timestamp</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*decimal</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<autoincrement>1</autoincrement>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>decimaltest</name>
|
||||
<type>decimal</type>
|
||||
<default/>
|
||||
<notnull>true</notnull>
|
||||
<precision>12</precision>
|
||||
<scale>2</scale>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*migratekeyword</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>select</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*uniconst</name>
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>id</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_hash</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>32</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>etag</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>40</length>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<!--<name>fs_storage_path_hash</name>-->
|
||||
<unique>true</unique>
|
||||
<field>
|
||||
<name>storage</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
<field>
|
||||
<name>path_hash</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*text_table</name>
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>textfield</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
</database>
|
|
@ -1,137 +0,0 @@
|
|||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<database>
|
||||
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>utf8</charset>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*cntcts_addrsbks</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>userid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>displayname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>uri</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>description</name>
|
||||
<type>text</type>
|
||||
<notnull>false</notnull>
|
||||
<length>1024</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>ctag</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>active</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<length>1</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*timestamp</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<autoincrement>1</autoincrement>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>timestamptest</name>
|
||||
<type>timestamp</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*decimal</name>
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<autoincrement>1</autoincrement>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>decimaltest</name>
|
||||
<type>decimal</type>
|
||||
<default/>
|
||||
<notnull>true</notnull>
|
||||
<precision>12</precision>
|
||||
<scale>2</scale>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*migratekeyword</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>select</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
</database>
|
|
@ -1,375 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use OC\DB\MDB2SchemaManager;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
|
||||
/**
|
||||
* Class Connection
|
||||
*
|
||||
* @group DB
|
||||
*
|
||||
* @package Test\DB
|
||||
*/
|
||||
class ConnectionTest extends \Test\TestCase {
|
||||
/**
|
||||
* @var \OCP\IDBConnection
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
self::dropTestTable();
|
||||
parent::setUpBeforeClass();
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void {
|
||||
self::dropTestTable();
|
||||
parent::tearDownAfterClass();
|
||||
}
|
||||
|
||||
protected static function dropTestTable() {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') !== 'oci') {
|
||||
\OC::$server->getDatabaseConnection()->dropTable('table');
|
||||
}
|
||||
}
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->connection = \OC::$server->get(\OC\DB\Connection::class);
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
parent::tearDown();
|
||||
$this->connection->dropTable('table');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
*/
|
||||
public function assertTableExist($table) {
|
||||
if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||
// sqlite removes the tables after closing the DB
|
||||
$this->addToAssertionCount(1);
|
||||
} else {
|
||||
$this->assertTrue($this->connection->tableExists($table), 'Table ' . $table . ' exists.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
*/
|
||||
public function assertTableNotExist($table) {
|
||||
if ($this->connection->getDatabasePlatform() instanceof SqlitePlatform) {
|
||||
// sqlite removes the tables after closing the DB
|
||||
$this->addToAssertionCount(1);
|
||||
} else {
|
||||
$this->assertFalse($this->connection->tableExists($table), 'Table ' . $table . " doesn't exist.");
|
||||
}
|
||||
}
|
||||
|
||||
private function makeTestTable() {
|
||||
$schemaManager = new MDB2SchemaManager($this->connection);
|
||||
$schemaManager->createDbFromStructure(__DIR__ . '/testschema.xml');
|
||||
}
|
||||
|
||||
public function testTableExists() {
|
||||
$this->assertTableNotExist('table');
|
||||
$this->makeTestTable();
|
||||
$this->assertTableExist('table');
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testTableExists
|
||||
*/
|
||||
public function testDropTable() {
|
||||
$this->makeTestTable();
|
||||
$this->assertTableExist('table');
|
||||
$this->connection->dropTable('table');
|
||||
$this->assertTableNotExist('table');
|
||||
}
|
||||
|
||||
private function getTextValueByIntegerField($integerField) {
|
||||
$builder = $this->connection->getQueryBuilder();
|
||||
$query = $builder->select('*')
|
||||
->from('table')
|
||||
->where($builder->expr()->eq('integerfield', $builder->createNamedParameter($integerField, IQueryBuilder::PARAM_INT)));
|
||||
|
||||
$result = $query->execute();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
return $row['textfield'] ?? null;
|
||||
}
|
||||
|
||||
public function testSetValues() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo',
|
||||
'clobfield' => 'not_null'
|
||||
]);
|
||||
|
||||
$this->assertEquals('foo', $this->getTextValueByIntegerField(1));
|
||||
}
|
||||
|
||||
public function testSetValuesOverWrite() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo'
|
||||
]);
|
||||
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'bar'
|
||||
]);
|
||||
|
||||
$this->assertEquals('bar', $this->getTextValueByIntegerField(1));
|
||||
}
|
||||
|
||||
public function testSetValuesOverWritePrecondition() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo',
|
||||
'booleanfield' => true,
|
||||
'clobfield' => 'not_null'
|
||||
]);
|
||||
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'bar'
|
||||
], [
|
||||
'booleanfield' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals('bar', $this->getTextValueByIntegerField(1));
|
||||
}
|
||||
|
||||
|
||||
public function testSetValuesOverWritePreconditionFailed() {
|
||||
$this->expectException(\OCP\PreConditionNotMetException::class);
|
||||
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo',
|
||||
'booleanfield' => true,
|
||||
'clobfield' => 'not_null'
|
||||
]);
|
||||
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'bar'
|
||||
], [
|
||||
'booleanfield' => false
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSetValuesSameNoError() {
|
||||
$this->makeTestTable();
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo',
|
||||
'clobfield' => 'not_null'
|
||||
]);
|
||||
|
||||
// this will result in 'no affected rows' on certain optimizing DBs
|
||||
// ensure the PreConditionNotMetException isn't thrown
|
||||
$this->connection->setValues('table', [
|
||||
'integerfield' => 1
|
||||
], [
|
||||
'textfield' => 'foo'
|
||||
]);
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testInsertIfNotExist() {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') === 'oci') {
|
||||
self::markTestSkipped('Insert if not exist does not work with clob on oracle');
|
||||
}
|
||||
|
||||
$this->makeTestTable();
|
||||
$categoryEntries = [
|
||||
['user' => 'test', 'category' => 'Family', 'expectedResult' => 1],
|
||||
['user' => 'test', 'category' => 'Friends', 'expectedResult' => 1],
|
||||
['user' => 'test', 'category' => 'Coworkers', 'expectedResult' => 1],
|
||||
['user' => 'test', 'category' => 'Coworkers', 'expectedResult' => 0],
|
||||
['user' => 'test', 'category' => 'School', 'expectedResult' => 1],
|
||||
['user' => 'test2', 'category' => 'Coworkers2', 'expectedResult' => 1],
|
||||
['user' => 'test2', 'category' => 'Coworkers2', 'expectedResult' => 0],
|
||||
['user' => 'test2', 'category' => 'School2', 'expectedResult' => 1],
|
||||
['user' => 'test2', 'category' => 'Coworkers', 'expectedResult' => 1],
|
||||
];
|
||||
|
||||
$row = 0;
|
||||
foreach ($categoryEntries as $entry) {
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'textfield' => $entry['user'],
|
||||
'clobfield' => $entry['category'],
|
||||
'integerfield' => $row++,
|
||||
], ['textfield', 'clobfield']);
|
||||
$this->assertEquals($entry['expectedResult'], $result, json_encode($entry));
|
||||
}
|
||||
|
||||
$query = $this->connection->prepare('SELECT * FROM `*PREFIX*table`');
|
||||
$result = $query->execute();
|
||||
$this->assertTrue((bool)$result);
|
||||
$this->assertEquals(7, count($result->fetchAll()));
|
||||
}
|
||||
|
||||
public function testInsertIfNotExistNull() {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') === 'oci') {
|
||||
self::markTestSkipped('Insert if not exist does not work with clob on oracle');
|
||||
}
|
||||
|
||||
$this->makeTestTable();
|
||||
$categoryEntries = [
|
||||
['addressbookid' => 123, 'fullname' => null, 'expectedResult' => 1],
|
||||
['addressbookid' => 123, 'fullname' => null, 'expectedResult' => 0],
|
||||
['addressbookid' => 123, 'fullname' => 'test', 'expectedResult' => 1],
|
||||
];
|
||||
|
||||
$row = 0;
|
||||
foreach ($categoryEntries as $entry) {
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'integerfield_default' => $entry['addressbookid'],
|
||||
'clobfield' => $entry['fullname'],
|
||||
'integerfield' => $row++,
|
||||
], ['integerfield_default', 'clobfield']);
|
||||
$this->assertEquals($entry['expectedResult'], $result, json_encode($entry));
|
||||
}
|
||||
|
||||
$query = $this->connection->prepare('SELECT * FROM `*PREFIX*table`');
|
||||
$result = $query->execute();
|
||||
$this->assertTrue((bool)$result);
|
||||
$this->assertEquals(2, count($result->fetchAll()));
|
||||
}
|
||||
|
||||
public function testInsertIfNotExistDonTOverwrite() {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') === 'oci') {
|
||||
self::markTestSkipped('Insert if not exist does not work with clob on oracle');
|
||||
}
|
||||
|
||||
$this->makeTestTable();
|
||||
$fullName = 'fullname test';
|
||||
$uri = 'uri_1';
|
||||
|
||||
// Normal test to have same known data inserted.
|
||||
$query = $this->connection->prepare('INSERT INTO `*PREFIX*table` (`textfield`, `clobfield`) VALUES (?, ?)');
|
||||
$result = $query->execute([$fullName, $uri]);
|
||||
$this->assertEquals(1, $result->rowCount());
|
||||
$query = $this->connection->prepare('SELECT `textfield`, `clobfield` FROM `*PREFIX*table` WHERE `clobfield` = ?');
|
||||
$result = $query->execute([$uri]);
|
||||
$rowset = $result->fetchAll();
|
||||
$this->assertEquals(1, count($rowset));
|
||||
$this->assertArrayHasKey('textfield', $rowset[0]);
|
||||
$this->assertEquals($fullName, $rowset[0]['textfield']);
|
||||
|
||||
// Try to insert a new row
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'textfield' => $fullName,
|
||||
'clobfield' => $uri,
|
||||
]);
|
||||
$this->assertEquals(0, $result);
|
||||
|
||||
$query = $this->connection->prepare('SELECT `textfield`, `clobfield` FROM `*PREFIX*table` WHERE `clobfield` = ?');
|
||||
$result = $query->execute([$uri]);
|
||||
// Test that previously inserted data isn't overwritten
|
||||
// And that a new row hasn't been inserted.
|
||||
$rowset = $result->fetchAll();
|
||||
$this->assertEquals(1, count($rowset));
|
||||
$this->assertArrayHasKey('textfield', $rowset[0]);
|
||||
$this->assertEquals($fullName, $rowset[0]['textfield']);
|
||||
}
|
||||
|
||||
public function testInsertIfNotExistsViolating() {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') === 'oci') {
|
||||
self::markTestSkipped('Insert if not exist does not work with clob on oracle');
|
||||
}
|
||||
|
||||
$this->makeTestTable();
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'textfield' => md5('welcome.txt'),
|
||||
'clobfield' => $this->getUniqueID()
|
||||
]);
|
||||
$this->assertEquals(1, $result);
|
||||
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'textfield' => md5('welcome.txt'),
|
||||
'clobfield' => $this->getUniqueID()
|
||||
],['textfield']);
|
||||
|
||||
$this->assertEquals(0, $result);
|
||||
}
|
||||
|
||||
public function insertIfNotExistsViolatingThrows() {
|
||||
return [
|
||||
[null],
|
||||
[['clobfield']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider insertIfNotExistsViolatingThrows
|
||||
*
|
||||
* @param array $compareKeys
|
||||
*/
|
||||
public function testInsertIfNotExistsViolatingUnique($compareKeys) {
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') === 'oci') {
|
||||
self::markTestSkipped('Insert if not exist does not work with clob on oracle');
|
||||
}
|
||||
|
||||
$this->makeTestTable();
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'integerfield' => 1,
|
||||
'clobfield' => $this->getUniqueID()
|
||||
]);
|
||||
$this->assertEquals(1, $result);
|
||||
|
||||
$result = $this->connection->insertIfNotExist('*PREFIX*table',
|
||||
[
|
||||
'integerfield' => 1,
|
||||
'clobfield' => $this->getUniqueID()
|
||||
], $compareKeys);
|
||||
|
||||
$this->assertEquals(0, $result);
|
||||
}
|
||||
|
||||
|
||||
public function testUniqueConstraintViolating() {
|
||||
$this->expectException(\Doctrine\DBAL\Exception\UniqueConstraintViolationException::class);
|
||||
|
||||
$this->makeTestTable();
|
||||
|
||||
$testQuery = 'INSERT INTO `*PREFIX*table` (`integerfield`, `textfield`) VALUES(?, ?)';
|
||||
$testParams = [1, 'hello'];
|
||||
|
||||
$this->connection->executeUpdate($testQuery, $testParams);
|
||||
$this->connection->executeUpdate($testQuery, $testParams);
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use OC_DB;
|
||||
use OCP\ITempManager;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class DBSchemaTest
|
||||
*
|
||||
* @group DB
|
||||
*/
|
||||
class DBSchemaTest extends TestCase {
|
||||
protected $schema_file;
|
||||
protected $schema_file2;
|
||||
protected $table1;
|
||||
protected $table2;
|
||||
/** @var ITempManager */
|
||||
protected $tempManager;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->tempManager = \OC::$server->getTempManager();
|
||||
$this->schema_file = $this->tempManager->getTemporaryFile();
|
||||
$this->schema_file2 = $this->tempManager->getTemporaryFile();
|
||||
|
||||
$dbfile = \OC::$SERVERROOT.'/tests/data/db_structure.xml';
|
||||
$dbfile2 = \OC::$SERVERROOT.'/tests/data/db_structure2.xml';
|
||||
|
||||
$r = '_' . \OC::$server->getSecureRandom()->
|
||||
generate(4, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS) . '_';
|
||||
$content = file_get_contents($dbfile);
|
||||
$content = str_replace('*dbprefix*', '*dbprefix*'.$r, $content);
|
||||
file_put_contents($this->schema_file, $content);
|
||||
$content = file_get_contents($dbfile2);
|
||||
$content = str_replace('*dbprefix*', '*dbprefix*'.$r, $content);
|
||||
file_put_contents($this->schema_file2, $content);
|
||||
|
||||
$this->table1 = $r.'cntcts_addrsbks';
|
||||
$this->table2 = $r.'cntcts_cards';
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
unlink($this->schema_file);
|
||||
unlink($this->schema_file2);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
// everything in one test, they depend on each other
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
public function testSchema() {
|
||||
$this->doTestSchemaCreating();
|
||||
$this->doTestSchemaChanging();
|
||||
$this->doTestSchemaRemoving();
|
||||
}
|
||||
|
||||
public function doTestSchemaCreating() {
|
||||
OC_DB::createDbFromStructure($this->schema_file);
|
||||
$this->assertTableExist($this->table1);
|
||||
$this->assertTableExist($this->table2);
|
||||
}
|
||||
|
||||
public function doTestSchemaChanging() {
|
||||
OC_DB::updateDbFromStructure($this->schema_file2);
|
||||
$this->assertTableExist($this->table2);
|
||||
}
|
||||
|
||||
public function doTestSchemaRemoving() {
|
||||
OC_DB::removeDBStructure($this->schema_file);
|
||||
$this->assertTableNotExist($this->table1);
|
||||
$this->assertTableNotExist($this->table2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
*/
|
||||
public function assertTableExist($table) {
|
||||
$this->assertTrue(OC_DB::tableExists($table), 'Table ' . $table . ' does not exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
*/
|
||||
public function assertTableNotExist($table) {
|
||||
$platform = \OC::$server->getDatabaseConnection()->getDatabasePlatform();
|
||||
if ($platform instanceof SqlitePlatform) {
|
||||
// sqlite removes the tables after closing the DB
|
||||
$this->addToAssertionCount(1);
|
||||
} else {
|
||||
$this->assertFalse(OC_DB::tableExists($table), 'Table ' . $table . ' exists.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,323 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
use OC_DB;
|
||||
|
||||
/**
|
||||
* Class LegacyDBTest
|
||||
*
|
||||
* @group DB
|
||||
*/
|
||||
class LegacyDBTest extends \Test\TestCase {
|
||||
protected $backupGlobals = false;
|
||||
|
||||
protected static $schema_file;
|
||||
protected $test_prefix;
|
||||
|
||||
public static function setUpBeforeClass(): void {
|
||||
self::$schema_file = \OC::$server->getTempManager()->getTemporaryFile();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table1;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table2;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table3;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table4;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $table5;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $text_table;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$dbFile = \OC::$SERVERROOT.'/tests/data/db_structure.xml';
|
||||
|
||||
$r = $this->getUniqueID('_', 4).'_';
|
||||
$content = file_get_contents($dbFile);
|
||||
$content = str_replace('*dbprefix*', '*dbprefix*'.$r, $content);
|
||||
file_put_contents(self::$schema_file, $content);
|
||||
OC_DB::createDbFromStructure(self::$schema_file);
|
||||
|
||||
$this->test_prefix = $r;
|
||||
$this->table1 = $this->test_prefix.'cntcts_addrsbks';
|
||||
$this->table2 = $this->test_prefix.'cntcts_cards';
|
||||
$this->table3 = $this->test_prefix.'vcategory';
|
||||
$this->table4 = $this->test_prefix.'decimal';
|
||||
$this->table5 = $this->test_prefix.'uniconst';
|
||||
$this->text_table = $this->test_prefix.'text_table';
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
OC_DB::removeDBStructure(self::$schema_file);
|
||||
unlink(self::$schema_file);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testQuotes() {
|
||||
$query = OC_DB::prepare('SELECT `fullname` FROM `*PREFIX*'.$this->table2.'` WHERE `uri` = ?');
|
||||
$result = $query->execute(['uri_1']);
|
||||
$this->assertTrue((bool)$result);
|
||||
$row = $result->fetchRow();
|
||||
$this->assertFalse($row);
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare('INSERT INTO `*PREFIX*'.$this->table2.'` (`fullname`,`uri`) VALUES (?,?)');
|
||||
$result = $query->execute(['fullname test', 'uri_1']);
|
||||
$this->assertEquals(1, $result);
|
||||
$query = OC_DB::prepare('SELECT `fullname`,`uri` FROM `*PREFIX*'.$this->table2.'` WHERE `uri` = ?');
|
||||
$result = $query->execute(['uri_1']);
|
||||
$this->assertTrue((bool)$result);
|
||||
$row = $result->fetchRow();
|
||||
$this->assertArrayHasKey('fullname', $row);
|
||||
$this->assertEquals($row['fullname'], 'fullname test');
|
||||
$row = $result->fetchRow();
|
||||
$this->assertFalse((bool)$row); //PDO returns false, MDB2 returns null
|
||||
$result->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
public function testNOW() {
|
||||
$query = OC_DB::prepare('INSERT INTO `*PREFIX*'.$this->table2.'` (`fullname`,`uri`) VALUES (NOW(),?)');
|
||||
$result = $query->execute(['uri_2']);
|
||||
$this->assertEquals(1, $result);
|
||||
$query = OC_DB::prepare('SELECT `fullname`,`uri` FROM `*PREFIX*'.$this->table2.'` WHERE `uri` = ?');
|
||||
$result = $query->execute(['uri_2']);
|
||||
$this->assertTrue((bool)$result);
|
||||
$result->closeCursor();
|
||||
}
|
||||
|
||||
public function testUNIX_TIMESTAMP() {
|
||||
$query = OC_DB::prepare('INSERT INTO `*PREFIX*'.$this->table2.'` (`fullname`,`uri`) VALUES (UNIX_TIMESTAMP(),?)');
|
||||
$result = $query->execute(['uri_3']);
|
||||
$this->assertEquals(1, $result);
|
||||
$query = OC_DB::prepare('SELECT `fullname`,`uri` FROM `*PREFIX*'.$this->table2.'` WHERE `uri` = ?');
|
||||
$result = $query->execute(['uri_3']);
|
||||
$this->assertTrue((bool)$result);
|
||||
$result->closeCursor();
|
||||
}
|
||||
|
||||
public function testLastInsertId() {
|
||||
$query = OC_DB::prepare('INSERT INTO `*PREFIX*'.$this->table2.'` (`fullname`,`uri`) VALUES (?,?)');
|
||||
$result1 = OC_DB::executeAudited($query, ['insertid 1','uri_1']);
|
||||
$id1 = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*'.$this->table2);
|
||||
|
||||
// we don't know the id we should expect, so insert another row
|
||||
$result2 = OC_DB::executeAudited($query, ['insertid 2','uri_2']);
|
||||
$id2 = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*'.$this->table2);
|
||||
// now we can check if the two ids are in correct order
|
||||
$this->assertGreaterThan($id1, $id2);
|
||||
}
|
||||
|
||||
public function testUtf8Data() {
|
||||
$table = "*PREFIX*{$this->table2}";
|
||||
$expected = "Ћö雙喜\xE2\x80\xA2";
|
||||
|
||||
$query = OC_DB::prepare("INSERT INTO `$table` (`fullname`, `uri`, `carddata`) VALUES (?, ?, ?)");
|
||||
$result = $query->execute([$expected, 'uri_1', 'This is a vCard']);
|
||||
$this->assertEquals(1, $result);
|
||||
|
||||
$query = OC_DB::prepare("SELECT `fullname` FROM `$table`");
|
||||
|
||||
$result = $query->execute();
|
||||
$actual = $result->fetchOne();
|
||||
$result->closeCursor();
|
||||
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert, select and delete decimal(12,2) values
|
||||
* @dataProvider decimalData
|
||||
*/
|
||||
public function XtestDecimal($insert, $expect) {
|
||||
$table = "*PREFIX*" . $this->table4;
|
||||
$rowname = 'decimaltest';
|
||||
|
||||
$query = OC_DB::prepare('INSERT INTO `' . $table . '` (`' . $rowname . '`) VALUES (?)');
|
||||
$result = $query->execute([$insert]);
|
||||
$this->assertEquals(1, $result);
|
||||
$query = OC_DB::prepare('SELECT `' . $rowname . '` FROM `' . $table . '`');
|
||||
$result = $query->execute();
|
||||
$this->assertTrue((bool)$result);
|
||||
$row = $result->fetchRow();
|
||||
$result->closeCursor();
|
||||
$this->assertArrayHasKey($rowname, $row);
|
||||
$this->assertEquals($expect, $row[$rowname]);
|
||||
$query = OC_DB::prepare('DELETE FROM `' . $table . '`');
|
||||
$result = $query->execute();
|
||||
$this->assertTrue((bool)$result);
|
||||
}
|
||||
|
||||
public function decimalData() {
|
||||
return [
|
||||
['1337133713.37', '1337133713.37'],
|
||||
['1234567890', '1234567890.00'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUpdateAffectedRowsNoMatch() {
|
||||
$this->insertCardData('fullname1', 'uri1');
|
||||
// The WHERE clause does not match any rows
|
||||
$this->assertSame(0, $this->updateCardData('fullname3', 'uri2'));
|
||||
}
|
||||
|
||||
public function testUpdateAffectedRowsDifferent() {
|
||||
$this->insertCardData('fullname1', 'uri1');
|
||||
// The WHERE clause matches a single row and the value we are updating
|
||||
// is different from the one already present.
|
||||
$this->assertSame(1, $this->updateCardData('fullname1', 'uri2'));
|
||||
}
|
||||
|
||||
public function testUpdateAffectedRowsSame() {
|
||||
$this->insertCardData('fullname1', 'uri1');
|
||||
// The WHERE clause matches a single row and the value we are updating
|
||||
// to is the same as the one already present. MySQL reports 0 here when
|
||||
// the PDO::MYSQL_ATTR_FOUND_ROWS flag is not specified.
|
||||
$this->assertSame(1, $this->updateCardData('fullname1', 'uri1'));
|
||||
}
|
||||
|
||||
public function testUpdateAffectedRowsMultiple() {
|
||||
$this->insertCardData('fullname1', 'uri1');
|
||||
$this->insertCardData('fullname2', 'uri2');
|
||||
// The WHERE clause matches two rows. One row contains a value that
|
||||
// needs to be updated, the other one already contains the value we are
|
||||
// updating to. MySQL reports 1 here when the PDO::MYSQL_ATTR_FOUND_ROWS
|
||||
// flag is not specified.
|
||||
$query = OC_DB::prepare("UPDATE `*PREFIX*{$this->table2}` SET `uri` = ?");
|
||||
$this->assertSame(2, $query->execute(['uri1']));
|
||||
}
|
||||
|
||||
protected function insertCardData($fullname, $uri) {
|
||||
$query = OC_DB::prepare("INSERT INTO `*PREFIX*{$this->table2}` (`fullname`, `uri`, `carddata`) VALUES (?, ?, ?)");
|
||||
$this->assertSame(1, $query->execute([$fullname, $uri, $this->getUniqueID()]));
|
||||
}
|
||||
|
||||
protected function updateCardData($fullname, $uri) {
|
||||
$query = OC_DB::prepare("UPDATE `*PREFIX*{$this->table2}` SET `uri` = ? WHERE `fullname` = ?");
|
||||
return $query->execute([$uri, $fullname]);
|
||||
}
|
||||
|
||||
public function testILIKE() {
|
||||
$table = "*PREFIX*{$this->table2}";
|
||||
|
||||
$query = OC_DB::prepare("INSERT INTO `$table` (`fullname`, `uri`, `carddata`) VALUES (?, ?, ?)");
|
||||
$query->execute(['fooBAR', 'foo', 'bar']);
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` LIKE ?");
|
||||
$result = $query->execute(['foobar']);
|
||||
$this->assertCount(0, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` ILIKE ?");
|
||||
$result = $query->execute(['foobar']);
|
||||
$this->assertCount(1, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` ILIKE ?");
|
||||
$result = $query->execute(['foo']);
|
||||
$this->assertCount(0, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
}
|
||||
|
||||
public function testILIKEWildcard() {
|
||||
$table = "*PREFIX*{$this->table2}";
|
||||
|
||||
$query = OC_DB::prepare("INSERT INTO `$table` (`fullname`, `uri`, `carddata`) VALUES (?, ?, ?)");
|
||||
$query->execute(['FooBAR', 'foo', 'bar']);
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` LIKE ?");
|
||||
$result = $query->execute(['%bar']);
|
||||
$this->assertCount(0, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` LIKE ?");
|
||||
$result = $query->execute(['foo%']);
|
||||
$this->assertCount(0, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` LIKE ?");
|
||||
$result = $query->execute(['%ba%']);
|
||||
$this->assertCount(0, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` ILIKE ?");
|
||||
$result = $query->execute(['%bar']);
|
||||
$this->assertCount(1, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` ILIKE ?");
|
||||
$result = $query->execute(['foo%']);
|
||||
$this->assertCount(1, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
|
||||
$query = OC_DB::prepare("SELECT * FROM `$table` WHERE `fullname` ILIKE ?");
|
||||
$result = $query->execute(['%ba%']);
|
||||
$this->assertCount(1, $result->fetchAll());
|
||||
$result->closeCursor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider insertAndSelectDataProvider
|
||||
*/
|
||||
public function testInsertAndSelectData($expected, $throwsOnMysqlWithoutUTF8MB4) {
|
||||
$table = "*PREFIX*{$this->text_table}";
|
||||
$config = \OC::$server->getConfig();
|
||||
|
||||
$query = OC_DB::prepare("INSERT INTO `$table` (`textfield`) VALUES (?)");
|
||||
if ($throwsOnMysqlWithoutUTF8MB4 && $config->getSystemValue('dbtype', 'sqlite') === 'mysql' && $config->getSystemValue('mysql.utf8mb4', false) === false) {
|
||||
$this->markTestSkipped('MySQL requires UTF8mb4 to store value: ' . $expected);
|
||||
}
|
||||
$result = $query->execute([$expected]);
|
||||
$this->assertEquals(1, $result);
|
||||
|
||||
$query = OC_DB::prepare("SELECT `textfield` FROM `$table`");
|
||||
|
||||
$result = $query->execute();
|
||||
$actual = $result->fetchOne();
|
||||
$result->closeCursor();
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
public function insertAndSelectDataProvider() {
|
||||
return [
|
||||
['abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ', false],
|
||||
['0123456789', false],
|
||||
['äöüÄÖÜß!"§$%&/()=?#\'+*~°^`´', false],
|
||||
['²³¼½¬{[]}\\', false],
|
||||
['♡⚗', false],
|
||||
['💩', true], # :hankey: on github
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (c) 2014 Thomas Müller <deepdiver@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
|
||||
/**
|
||||
* Class MDB2SchemaManager
|
||||
*
|
||||
* @group DB
|
||||
*
|
||||
* @package Test\DB
|
||||
*/
|
||||
class MDB2SchemaManagerTest extends \Test\TestCase {
|
||||
protected function tearDown(): void {
|
||||
// do not drop the table for Oracle as it will create a bogus transaction
|
||||
// that will break the following test suites requiring transactions
|
||||
if (\OC::$server->getConfig()->getSystemValue('dbtype', 'sqlite') !== 'oci') {
|
||||
\OC::$server->getDatabaseConnection()->dropTable('table');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testAutoIncrement() {
|
||||
$connection = \OC::$server->get(\OC\DB\Connection::class);
|
||||
if ($connection->getDatabasePlatform() instanceof OraclePlatform) {
|
||||
$this->markTestSkipped('Adding auto increment columns in Oracle is not supported.');
|
||||
}
|
||||
|
||||
$manager = new \OC\DB\MDB2SchemaManager($connection);
|
||||
|
||||
$manager->createDbFromStructure(__DIR__ . '/ts-autoincrement-before.xml');
|
||||
$connection->executeUpdate('insert into `*PREFIX*table` values (?)', ['abc']);
|
||||
$connection->executeUpdate('insert into `*PREFIX*table` values (?)', ['abc']);
|
||||
$connection->executeUpdate('insert into `*PREFIX*table` values (?)', ['123']);
|
||||
$connection->executeUpdate('insert into `*PREFIX*table` values (?)', ['123']);
|
||||
$manager->updateDbFromStructure(__DIR__ . '/ts-autoincrement-after.xml');
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use OC\DB\MDB2SchemaReader;
|
||||
use OCP\IConfig;
|
||||
use Test\TestCase;
|
||||
use Doctrine\DBAL\Types\IntegerType;
|
||||
use Doctrine\DBAL\Types\TextType;
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use Doctrine\DBAL\Types\BooleanType;
|
||||
|
||||
/**
|
||||
* Class MDB2SchemaReaderTest
|
||||
*
|
||||
* @group DB
|
||||
*
|
||||
* @package Test\DB
|
||||
*/
|
||||
class MDB2SchemaReaderTest extends TestCase {
|
||||
/**
|
||||
* @var MDB2SchemaReader $reader
|
||||
*/
|
||||
protected $reader;
|
||||
|
||||
/**
|
||||
* @return IConfig
|
||||
*/
|
||||
protected function getConfig() {
|
||||
/** @var IConfig | \PHPUnit\Framework\MockObject\MockObject $config */
|
||||
$config = $this->getMockBuilder(IConfig::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$config->expects($this->any())
|
||||
->method('getSystemValue')
|
||||
->willReturnMap([
|
||||
['dbname', 'owncloud', 'testDB'],
|
||||
['dbtableprefix', 'oc_', 'test_']
|
||||
]);
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function testRead() {
|
||||
$reader = new MDB2SchemaReader($this->getConfig(), new MySQLPlatform());
|
||||
$schema = $reader->loadSchemaFromFile(__DIR__ . '/testschema.xml', new Schema());
|
||||
$this->assertCount(1, $schema->getTables());
|
||||
|
||||
$table = $schema->getTable('test_table');
|
||||
$this->assertCount(9, $table->getColumns());
|
||||
|
||||
$this->assertEquals(4, $table->getColumn('id')->getLength());
|
||||
$this->assertTrue($table->getColumn('id')->getAutoincrement());
|
||||
$this->assertEquals(0, $table->getColumn('id')->getDefault());
|
||||
$this->assertTrue($table->getColumn('id')->getNotnull());
|
||||
$this->assertInstanceOf(IntegerType::class, $table->getColumn('id')->getType());
|
||||
|
||||
$this->assertSame(10, $table->getColumn('integerfield_default')->getDefault());
|
||||
|
||||
$this->assertEquals(32, $table->getColumn('textfield')->getLength());
|
||||
$this->assertFalse($table->getColumn('textfield')->getAutoincrement());
|
||||
$this->assertSame('foo', $table->getColumn('textfield')->getDefault());
|
||||
$this->assertTrue($table->getColumn('textfield')->getNotnull());
|
||||
$this->assertInstanceOf(StringType::class, $table->getColumn('textfield')->getType());
|
||||
|
||||
$this->assertNull($table->getColumn('clobfield')->getLength());
|
||||
$this->assertFalse($table->getColumn('clobfield')->getAutoincrement());
|
||||
$this->assertNull($table->getColumn('clobfield')->getDefault());
|
||||
$this->assertFalse($table->getColumn('clobfield')->getNotnull());
|
||||
$this->assertInstanceOf(StringType::class, $table->getColumn('clobfield')->getType());
|
||||
// $this->assertInstanceOf(TextType::class, $table->getColumn('clobfield')->getType());
|
||||
|
||||
$this->assertNull($table->getColumn('booleanfield')->getLength());
|
||||
$this->assertFalse($table->getColumn('booleanfield')->getAutoincrement());
|
||||
$this->assertNull($table->getColumn('booleanfield')->getDefault());
|
||||
$this->assertInstanceOf(BooleanType::class, $table->getColumn('booleanfield')->getType());
|
||||
|
||||
$this->assertTrue($table->getColumn('booleanfield_true')->getDefault());
|
||||
$this->assertFalse($table->getColumn('booleanfield_false')->getDefault());
|
||||
|
||||
$this->assertEquals(12, $table->getColumn('decimalfield_precision_scale')->getPrecision());
|
||||
$this->assertEquals(2, $table->getColumn('decimalfield_precision_scale')->getScale());
|
||||
|
||||
$this->assertCount(3, $table->getIndexes());
|
||||
$this->assertEquals(['id'], $table->getIndex('primary')->getUnquotedColumns());
|
||||
$this->assertTrue($table->getIndex('primary')->isPrimary());
|
||||
$this->assertTrue($table->getIndex('primary')->isUnique());
|
||||
$this->assertEquals(['integerfield'], $table->getIndex('index_integerfield')->getUnquotedColumns());
|
||||
$this->assertFalse($table->getIndex('index_integerfield')->isPrimary());
|
||||
$this->assertTrue($table->getIndex('index_integerfield')->isUnique());
|
||||
$this->assertEquals(['booleanfield'], $table->getIndex('index_boolean')->getUnquotedColumns());
|
||||
$this->assertFalse($table->getIndex('index_boolean')->isPrimary());
|
||||
$this->assertFalse($table->getIndex('index_boolean')->isUnique());
|
||||
}
|
||||
}
|
|
@ -12,9 +12,15 @@ namespace Test\DB;
|
|||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaConfig;
|
||||
use OC\DB\Migrator;
|
||||
use OC\DB\MySQLMigrator;
|
||||
use OC\DB\OracleMigrator;
|
||||
use OC\DB\PostgreSqlMigrator;
|
||||
use OC\DB\SQLiteMigrator;
|
||||
use OCP\IConfig;
|
||||
|
||||
/**
|
||||
|
@ -30,11 +36,6 @@ class MigratorTest extends \Test\TestCase {
|
|||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \OC\DB\MDB2SchemaManager
|
||||
*/
|
||||
private $manager;
|
||||
|
||||
/**
|
||||
* @var IConfig
|
||||
**/
|
||||
|
@ -54,11 +55,27 @@ class MigratorTest extends \Test\TestCase {
|
|||
if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
|
||||
$this->markTestSkipped('DB migration tests are not supported on OCI');
|
||||
}
|
||||
$this->manager = new \OC\DB\MDB2SchemaManager($this->connection);
|
||||
|
||||
$this->tableName = $this->getUniqueTableName();
|
||||
$this->tableNameTmp = $this->getUniqueTableName();
|
||||
}
|
||||
|
||||
private function getMigrator(): Migrator {
|
||||
$platform = $this->connection->getDatabasePlatform();
|
||||
$random = \OC::$server->getSecureRandom();
|
||||
$dispatcher = \OC::$server->getEventDispatcher();
|
||||
if ($platform instanceof SqlitePlatform) {
|
||||
return new SQLiteMigrator($this->connection, $this->config, $dispatcher);
|
||||
} elseif ($platform instanceof OraclePlatform) {
|
||||
return new OracleMigrator($this->connection, $this->config, $dispatcher);
|
||||
} elseif ($platform instanceof MySQLPlatform) {
|
||||
return new MySQLMigrator($this->connection, $this->config, $dispatcher);
|
||||
} elseif ($platform instanceof PostgreSQL94Platform) {
|
||||
return new PostgreSqlMigrator($this->connection, $this->config, $dispatcher);
|
||||
}
|
||||
return new Migrator($this->connection, $this->config, $dispatcher);
|
||||
}
|
||||
|
||||
private function getUniqueTableName() {
|
||||
return strtolower($this->getUniqueID($this->config->getSystemValue('dbtableprefix', 'oc_') . 'test_'));
|
||||
}
|
||||
|
@ -132,7 +149,7 @@ class MigratorTest extends \Test\TestCase {
|
|||
|
||||
public function testUpgrade() {
|
||||
[$startSchema, $endSchema] = $this->getDuplicateKeySchemas();
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($startSchema);
|
||||
|
||||
$this->connection->insert($this->tableName, ['id' => 1, 'name' => 'foo']);
|
||||
|
@ -150,7 +167,7 @@ class MigratorTest extends \Test\TestCase {
|
|||
$this->tableName = strtolower($this->getUniqueID($this->config->getSystemValue('dbtableprefix') . 'test_'));
|
||||
|
||||
[$startSchema, $endSchema] = $this->getDuplicateKeySchemas();
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($startSchema);
|
||||
|
||||
$this->connection->insert($this->tableName, ['id' => 1, 'name' => 'foo']);
|
||||
|
@ -165,7 +182,7 @@ class MigratorTest extends \Test\TestCase {
|
|||
|
||||
public function testInsertAfterUpgrade() {
|
||||
[$startSchema, $endSchema] = $this->getDuplicateKeySchemas();
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($startSchema);
|
||||
|
||||
$migrator->migrate($endSchema);
|
||||
|
@ -192,7 +209,7 @@ class MigratorTest extends \Test\TestCase {
|
|||
$table->addColumn('name', 'string');
|
||||
$table->setPrimaryKey(['id']);
|
||||
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($startSchema);
|
||||
|
||||
$migrator->migrate($endSchema);
|
||||
|
@ -213,7 +230,7 @@ class MigratorTest extends \Test\TestCase {
|
|||
$table->addColumn('user', 'string', ['length' => 64]);
|
||||
$table->setPrimaryKey(['id']);
|
||||
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($startSchema);
|
||||
|
||||
$migrator->migrate($endSchema);
|
||||
|
@ -234,7 +251,7 @@ class MigratorTest extends \Test\TestCase {
|
|||
$tableFk->addColumn('name', 'string');
|
||||
$tableFk->addForeignKeyConstraint($this->tableName, ['fk_id'], ['id'], [], $fkName);
|
||||
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$migrator = $this->getMigrator();
|
||||
$migrator->migrate($startSchema);
|
||||
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Thomas Müller <deepdiver@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
/**
|
||||
* Class MySqlMigration
|
||||
*
|
||||
* @group DB
|
||||
*/
|
||||
class MySqlMigrationTest extends \Test\TestCase {
|
||||
|
||||
/** @var \Doctrine\DBAL\Connection */
|
||||
private $connection;
|
||||
|
||||
/** @var string */
|
||||
private $tableName;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->connection = \OC::$server->get(\OC\DB\Connection::class);
|
||||
if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) {
|
||||
$this->markTestSkipped("Test only relevant on MySql");
|
||||
}
|
||||
|
||||
$dbPrefix = \OC::$server->getConfig()->getSystemValue("dbtableprefix");
|
||||
$this->tableName = $this->getUniqueID($dbPrefix . '_enum_bit_test');
|
||||
$this->connection->exec("CREATE TABLE $this->tableName(b BIT, e ENUM('1','2','3','4'))");
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
$this->connection->getSchemaManager()->dropTable($this->tableName);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testNonOCTables() {
|
||||
$manager = new \OC\DB\MDB2SchemaManager($this->connection);
|
||||
$manager->updateDbFromStructure(__DIR__ . '/testschema.xml');
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
<?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\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaDiff;
|
||||
use OC\DB\MDB2SchemaManager;
|
||||
use OC\DB\MDB2SchemaReader;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class MigratorTest
|
||||
*
|
||||
* @group DB
|
||||
*
|
||||
* @package Test\DB
|
||||
*/
|
||||
class SchemaDiffTest extends TestCase {
|
||||
/** @var IDBConnection $connection */
|
||||
private $connection;
|
||||
/** @var \Doctrine\DBAL\Connection $connection */
|
||||
private $internalConnection;
|
||||
|
||||
/** @var MDB2SchemaManager */
|
||||
private $manager;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var string */
|
||||
private $testPrefix;
|
||||
|
||||
private $schemaFile;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->schemaFile = \OC::$server->getTempManager()->getTemporaryFile();
|
||||
|
||||
$this->config = \OC::$server->getConfig();
|
||||
$this->connection = \OC::$server->getDatabaseConnection();
|
||||
$this->internalConnection = \OC::$server->get(\OC\DB\Connection::class);
|
||||
$this->manager = new MDB2SchemaManager($this->internalConnection);
|
||||
$this->testPrefix = strtolower($this->getUniqueID($this->config->getSystemValue('dbtableprefix', 'oc_'), 3));
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
$this->manager->removeDBStructure($this->schemaFile);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providesSchemaFiles
|
||||
* @param string $xml
|
||||
*/
|
||||
public function testZeroChangeOnSchemaMigrations($xml) {
|
||||
$xml = str_replace('*dbprefix*', $this->testPrefix, $xml);
|
||||
$schemaFile = $this->schemaFile;
|
||||
file_put_contents($schemaFile, $xml);
|
||||
|
||||
// apply schema
|
||||
$this->manager->createDbFromStructure($schemaFile);
|
||||
|
||||
$schemaReader = new MDB2SchemaReader($this->config, $this->connection->getDatabasePlatform());
|
||||
$toSchema = new Schema([], [], $this->internalConnection->getSchemaManager()->createSchemaConfig());
|
||||
$endSchema = $schemaReader->loadSchemaFromFile($schemaFile, $toSchema);
|
||||
|
||||
// get the diff
|
||||
/** @var SchemaDiff $diff */
|
||||
$migrator = $this->manager->getMigrator();
|
||||
$diff = $this->invokePrivate($migrator, 'getDiff', [$endSchema, $this->internalConnection]);
|
||||
|
||||
// no sql statement is expected
|
||||
$sqls = $diff->toSql($this->connection->getDatabasePlatform());
|
||||
$this->assertEmpty($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')],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Copyright (c) 2014 Thomas Müller <deepdiver@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace Test\DB;
|
||||
|
||||
/**
|
||||
* Class SqliteMigration
|
||||
*
|
||||
* @group DB
|
||||
*/
|
||||
class SqliteMigrationTest extends \Test\TestCase {
|
||||
|
||||
/** @var \Doctrine\DBAL\Connection */
|
||||
private $connection;
|
||||
|
||||
/** @var string */
|
||||
private $tableName;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->connection = \OC::$server->get(\OC\DB\Connection::class);
|
||||
if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) {
|
||||
$this->markTestSkipped("Test only relevant on Sqlite");
|
||||
}
|
||||
|
||||
$dbPrefix = \OC::$server->getConfig()->getSystemValue("dbtableprefix");
|
||||
$this->tableName = $this->getUniqueID($dbPrefix . '_enum_bit_test');
|
||||
$this->connection->prepare("CREATE TABLE $this->tableName(t0 tinyint unsigned, t1 tinyint)")->execute();
|
||||
}
|
||||
|
||||
protected function tearDown(): void {
|
||||
$this->connection->getSchemaManager()->dropTable($this->tableName);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testNonOCTables() {
|
||||
$manager = new \OC\DB\MDB2SchemaManager($this->connection);
|
||||
$manager->updateDbFromStructure(__DIR__ . '/testschema.xml');
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?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>
|
|
@ -1,14 +0,0 @@
|
|||
<?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>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,51 +0,0 @@
|
|||
<?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>
|
|
@ -1,68 +0,0 @@
|
|||
<?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>
|
|
@ -1,99 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<database>
|
||||
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>utf8</charset>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*table</name>
|
||||
|
||||
<declaration>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<primary>true</primary>
|
||||
<length>4</length>
|
||||
<autoincrement>1</autoincrement>
|
||||
</field>
|
||||
<field>
|
||||
<name>integerfield</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>integerfield_default</name>
|
||||
<type>integer</type>
|
||||
<default>10</default>
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>textfield</name>
|
||||
<type>text</type>
|
||||
<default>foo</default>
|
||||
<notnull>true</notnull>
|
||||
<length>32</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>clobfield</name>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<name>booleanfield</name>
|
||||
<type>boolean</type>
|
||||
</field>
|
||||
<field>
|
||||
<name>booleanfield_true</name>
|
||||
<type>boolean</type>
|
||||
<default>true</default>
|
||||
</field>
|
||||
<field>
|
||||
<name>booleanfield_false</name>
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</field>
|
||||
<field>
|
||||
<name>decimalfield_precision_scale</name>
|
||||
<type>decimal</type>
|
||||
<precision>12</precision>
|
||||
<scale>2</scale>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>index_primary</name>
|
||||
<primary>true</primary>
|
||||
<unique>true</unique>
|
||||
<field>
|
||||
<name>id</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
<index>
|
||||
<name>index_integerfield</name>
|
||||
<unique>true</unique>
|
||||
<field>
|
||||
<name>integerfield</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
<index>
|
||||
<name>index_boolean</name>
|
||||
<unique>false</unique>
|
||||
<field>
|
||||
<name>booleanfield</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
</declaration>
|
||||
</table>
|
||||
</database>
|
|
@ -1,32 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<database>
|
||||
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>utf8</charset>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*table</name>
|
||||
|
||||
<declaration>
|
||||
<field>
|
||||
<name>auto_id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<length>4</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>textfield</name>
|
||||
<type>text</type>
|
||||
<default>foo</default>
|
||||
<notnull>true</notnull>
|
||||
<length>32</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
</database>
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<database>
|
||||
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>utf8</charset>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*table</name>
|
||||
|
||||
<declaration>
|
||||
<field>
|
||||
<name>textfield</name>
|
||||
<type>text</type>
|
||||
<default>foo</default>
|
||||
<notnull>true</notnull>
|
||||
<length>32</length>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
</database>
|
Loading…
Reference in New Issue