AddMissingIndices.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org>
  5. *
  6. * @author Bjoern Schiessle <bjoern@schiessle.org>
  7. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  8. * @author Joas Schilling <coding@schilljs.com>
  9. * @author Julius Härtl <jus@bitgrid.net>
  10. * @author Mario Danic <mario@lovelyhq.com>
  11. * @author Morris Jobke <hey@morrisjobke.de>
  12. * @author Robin Appelman <robin@icewind.nl>
  13. * @author Roeland Jago Douma <roeland@famdouma.nl>
  14. * @author Thomas Citharel <nextcloud@tcit.fr>
  15. *
  16. * @license GNU AGPL version 3 or any later version
  17. *
  18. * This program is free software: you can redistribute it and/or modify
  19. * it under the terms of the GNU Affero General Public License as
  20. * published by the Free Software Foundation, either version 3 of the
  21. * License, or (at your option) any later version.
  22. *
  23. * This program is distributed in the hope that it will be useful,
  24. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. * GNU Affero General Public License for more details.
  27. *
  28. * You should have received a copy of the GNU Affero General Public License
  29. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  30. *
  31. */
  32. namespace OC\Core\Command\Db;
  33. use OC\DB\Connection;
  34. use OC\DB\SchemaWrapper;
  35. use OCP\DB\Events\AddMissingIndicesEvent;
  36. use OCP\EventDispatcher\IEventDispatcher;
  37. use Symfony\Component\Console\Command\Command;
  38. use Symfony\Component\Console\Input\InputInterface;
  39. use Symfony\Component\Console\Input\InputOption;
  40. use Symfony\Component\Console\Output\OutputInterface;
  41. /**
  42. * Class AddMissingIndices
  43. *
  44. * if you added any new indices to the database, this is the right place to add
  45. * your update routine for existing instances
  46. *
  47. * @package OC\Core\Command\Db
  48. */
  49. class AddMissingIndices extends Command {
  50. public function __construct(
  51. private Connection $connection,
  52. private IEventDispatcher $dispatcher,
  53. ) {
  54. parent::__construct();
  55. }
  56. protected function configure() {
  57. $this
  58. ->setName('db:add-missing-indices')
  59. ->setDescription('Add missing indices to the database tables')
  60. ->addOption('dry-run', null, InputOption::VALUE_NONE, "Output the SQL queries instead of running them.");
  61. }
  62. protected function execute(InputInterface $input, OutputInterface $output): int {
  63. $dryRun = $input->getOption('dry-run');
  64. // Dispatch event so apps can also update indexes if needed
  65. $event = new AddMissingIndicesEvent();
  66. $this->dispatcher->dispatchTyped($event);
  67. $missingIndices = $event->getMissingIndices();
  68. $toReplaceIndices = $event->getIndicesToReplace();
  69. if ($missingIndices !== [] || $toReplaceIndices !== []) {
  70. $schema = new SchemaWrapper($this->connection);
  71. foreach ($missingIndices as $missingIndex) {
  72. if ($schema->hasTable($missingIndex['tableName'])) {
  73. $table = $schema->getTable($missingIndex['tableName']);
  74. if (!$table->hasIndex($missingIndex['indexName'])) {
  75. $output->writeln('<info>Adding additional ' . $missingIndex['indexName'] . ' index to the ' . $table->getName() . ' table, this can take some time...</info>');
  76. if ($missingIndex['dropUnnamedIndex']) {
  77. foreach ($table->getIndexes() as $index) {
  78. $columns = $index->getColumns();
  79. if ($columns === $missingIndex['columns']) {
  80. $table->dropIndex($index->getName());
  81. }
  82. }
  83. }
  84. if ($missingIndex['uniqueIndex']) {
  85. $table->addUniqueIndex($missingIndex['columns'], $missingIndex['indexName'], $missingIndex['options']);
  86. } else {
  87. $table->addIndex($missingIndex['columns'], $missingIndex['indexName'], [], $missingIndex['options']);
  88. }
  89. if (!$dryRun) {
  90. $this->connection->migrateToSchema($schema->getWrappedSchema());
  91. }
  92. $output->writeln('<info>' . $table->getName() . ' table updated successfully.</info>');
  93. }
  94. }
  95. }
  96. foreach ($toReplaceIndices as $toReplaceIndex) {
  97. if ($schema->hasTable($toReplaceIndex['tableName'])) {
  98. $table = $schema->getTable($toReplaceIndex['tableName']);
  99. $allOldIndicesExists = true;
  100. foreach ($toReplaceIndex['oldIndexNames'] as $oldIndexName) {
  101. if (!$table->hasIndex($oldIndexName)) {
  102. $allOldIndicesExists = false;
  103. }
  104. }
  105. if (!$allOldIndicesExists) {
  106. continue;
  107. }
  108. $output->writeln('<info>Adding additional ' . $toReplaceIndex['newIndexName'] . ' index to the ' . $table->getName() . ' table, this can take some time...</info>');
  109. if ($toReplaceIndex['uniqueIndex']) {
  110. $table->addUniqueIndex($toReplaceIndex['columns'], $toReplaceIndex['newIndexName'], $toReplaceIndex['options']);
  111. } else {
  112. $table->addIndex($toReplaceIndex['columns'], $toReplaceIndex['newIndexName'], [], $toReplaceIndex['options']);
  113. }
  114. if (!$dryRun) {
  115. $this->connection->migrateToSchema($schema->getWrappedSchema());
  116. }
  117. foreach ($toReplaceIndex['oldIndexNames'] as $oldIndexName) {
  118. $output->writeln('<info>Removing ' . $oldIndexName . ' index from the ' . $table->getName() . ' table</info>');
  119. $table->dropIndex($oldIndexName);
  120. }
  121. if (!$dryRun) {
  122. $this->connection->migrateToSchema($schema->getWrappedSchema());
  123. }
  124. $output->writeln('<info>' . $table->getName() . ' table updated successfully.</info>');
  125. }
  126. }
  127. if ($dryRun) {
  128. $sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
  129. if ($sqlQueries !== null) {
  130. $output->writeln($sqlQueries);
  131. }
  132. }
  133. }
  134. return 0;
  135. }
  136. }