summaryrefslogtreecommitdiffstats
path: root/core/command
diff options
context:
space:
mode:
Diffstat (limited to 'core/command')
-rw-r--r--core/command/db/converttype.php252
1 files changed, 252 insertions, 0 deletions
diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php
new file mode 100644
index 00000000000..fb45ab33113
--- /dev/null
+++ b/core/command/db/converttype.php
@@ -0,0 +1,252 @@
+<?php
+/**
+ * Copyright (c) 2013 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 OC\Core\Command\Db;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ConvertType extends Command {
+ /**
+ * @var \OC\Config $config
+ */
+ protected $config;
+
+ /**
+ * @param \OC\Config $config
+ */
+ public function __construct($config) {
+ $this->config = $config;
+ parent::__construct();
+ }
+
+ protected function interact(InputInterface $input, OutputInterface $output) {
+ parent::interact($input, $output);
+ if (!$input->getOption('password')) {
+ $dialog = $this->getHelperSet()->get('dialog');
+ $password = $dialog->askHiddenResponse(
+ $output,
+ 'What is the database password?',
+ false
+ );
+ $input->setOption('password', $password);
+ }
+ }
+
+ protected function configure() {
+ $this
+ ->setName('db:convert-type')
+ ->setDescription('Convert the owncloud database to the newly configured one')
+ ->addArgument(
+ 'type',
+ InputArgument::REQUIRED,
+ 'the type of the database to convert to'
+ )
+ ->addArgument(
+ 'username',
+ InputArgument::REQUIRED,
+ 'the username of the database to convert to'
+ )
+ ->addArgument(
+ 'hostname',
+ InputArgument::REQUIRED,
+ 'the hostname of the database to convert to'
+ )
+ ->addArgument(
+ 'database',
+ InputArgument::REQUIRED,
+ 'the name of the database to convert to'
+ )
+ ->addOption(
+ 'port',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'the port of the database to convert to'
+ )
+ ->addOption(
+ 'password',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'the password of the database to convert to. Will be asked when not specified'
+ )
+ ->addOption(
+ 'clear-schema',
+ null,
+ InputOption::VALUE_NONE,
+ 'remove all tables from the destination database'
+ )
+ ;
+ }
+
+ private static $type2driver = array(
+ 'mysql' => 'pdo_mysql',
+ 'pgsql' => 'pdo_pgsql',
+ 'oci' => 'oci8',
+ 'mssql' => 'pdo_sqlsrv',
+ );
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ // connect 'from' database
+ $fromDB = \OC_DB::getConnection();
+
+ // connect 'to' database
+ $toDB = $this->getToDBConnection($input, $output);
+
+ // Clearing schema in new database
+ if ($input->getOption('clear-schema')) {
+ $schemaManager = $toDB->getSchemaManager();
+ $toTables = $schemaManager->listTableNames();
+ if (!empty($toTables)) {
+ $output->writeln('Clearing schema in new database');
+ }
+ foreach($toTables as $table) {
+ $schemaManager->dropTable($table);
+ }
+ }
+
+ // create tables in new database
+ $output->writeln('Creating schema in new database');
+ $schemaManager = new \OC\DB\MDB2SchemaManager($toDB);
+ $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml');
+ $apps = \OC_App::getEnabledApps();
+ foreach($apps as $app) {
+ if(file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) {
+ $schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml');
+ }
+ }
+
+ // get tables from 'to' database
+ $toTables = $this->getTables($toDB);
+ // get tables from 'from' database
+ $fromTables = $this->getTables($fromDB);
+ // warn/fail if there are more tables in 'from' database
+ $tables = array_diff($fromTables, $toTables);
+ if (!empty($tables)) {
+ $output->writeln('<error>The following tables do NOT exist any more: '.join(', ', $tables).'</error>');
+ $dialog = $this->getHelperSet()->get('dialog');
+ if (!$dialog->askConfirmation(
+ $output,
+ '<question>Continue with the convertion?</question>',
+ false
+ )) {
+ return;
+ }
+ }
+ // enable maintenance mode to prevent changes
+ $tables = array_intersect($toTables, $fromTables);
+ $this->convertDB($fromDB, $toDB, $tables, $input, $output);
+ }
+
+ private function getToDBConnection($input, $output) {
+ $type = $input->getArgument('type');
+ $username = $input->getArgument('username');
+ $hostname = $input->getArgument('hostname');
+ $dbname = $input->getArgument('database');
+ $password = $input->getOption('password');
+
+ if (!isset(self::$type2driver[$type])) {
+ throw new InvalidArgumentException('Unknown type: '.$type);
+ }
+ $connectionParams = array(
+ 'driver' => self::$type2driver[$type],
+ 'user' => $username,
+ 'password' => $password,
+ 'host' => $hostname,
+ 'dbname' => $dbname,
+ );
+ if ($input->getOption('port')) {
+ $connectionParams['port'] = $input->getOption('port');
+ }
+ switch ($type) {
+ case 'mysql':
+ case 'mssql':
+ $connectionParams['charset'] = 'UTF8';
+ break;
+ case 'oci':
+ $connectionParams['charset'] = 'AL32UTF8';
+ break;
+ }
+
+ return \Doctrine\DBAL\DriverManager::getConnection($connectionParams);
+ }
+
+ private function getTables($db) {
+ $schemaManager = $db->getSchemaManager();
+ return $schemaManager->listTableNames();
+ }
+
+ private function copyTable($fromDB, $toDB, $table, $output) {
+ $progress = $this->getHelperSet()->get('progress');
+ $query = 'SELECT COUNT(*) FROM '.$table;
+ $count = $fromDB->fetchColumn($query);
+ $query = 'SELECT * FROM '.$table;
+ $statement = $fromDB->executeQuery($query);
+ $progress->start($output, $count);
+ $progress->setRedrawFrequency($count > 100 ? 5 : 1);
+ while($row = $statement->fetch()) {
+ $progress->advance();
+ $data = array();
+ foreach ($row as $columnName => $value) {
+ $data[$toDB->quoteIdentifier($columnName)] = $value;
+ }
+ $toDB->insert($table, $data);
+ }
+ $progress->finish();
+ }
+
+ private function convertDB($fromDB, $toDB, $tables, $input, $output) {
+ $this->config->setValue('maintenance', true);
+ $type = $input->getArgument('type');
+ try {
+ // copy table rows
+ foreach($tables as $table) {
+ $output->writeln($table);
+ $this->copyTable($fromDB, $toDB, $table, $output);
+ }
+ if ($type == 'pgsql') {
+ $sequences = $toDB->getSchemaManager()->listSequences();
+ $dbname = $input->getArgument('database');
+ foreach($sequences as $sequence) {
+ $info = $toDB->fetchAssoc('SELECT table_schema, table_name, column_name '
+ .'FROM information_schema.columns '
+ .'WHERE column_default = ? AND table_catalog = ?',
+ array("nextval('".$sequence->getName()."'::regclass)", $dbname));
+ $table_name = $info['table_name'];
+ $column_name = $info['column_name'];
+ $toDB->executeQuery("SELECT setval('" . $sequence->getName() . "', (SELECT MAX(" . $column_name . ") FROM " . $table_name . ")+1)");
+ }
+ }
+ // save new database config
+ $this->saveDBInfo($input);
+ } catch(\Exception $e) {
+ $this->config->setValue('maintenance', false);
+ throw $e;
+ }
+ $this->config->setValue('maintenance', false);
+ }
+
+ private function saveDBInfo($input) {
+ $type = $input->getArgument('type');
+ $username = $input->getArgument('username');
+ $dbhost = $input->getArgument('hostname');
+ $dbname = $input->getArgument('database');
+ $password = $input->getOption('password');
+ if ($input->getOption('port')) {
+ $dbhost .= ':'.$input->getOption('port');
+ }
+
+ $this->config->setValue('dbtype', $type);
+ $this->config->setValue('dbname', $dbname);
+ $this->config->setValue('dbhost', $dbhost);
+ $this->config->setValue('dbuser', $username);
+ $this->config->setValue('dbpassword', $password);
+ }
+}