Bläddra i källkod

Merge pull request #39397 from nextcloud/backport/39396/stable26

Julius Härtl 10 månader sedan
förälder
incheckning
0cd3950171

+ 23 - 0
apps/settings/lib/Controller/CheckSetupController.php

@@ -71,7 +71,9 @@ use OCP\AppFramework\Controller;
 use OCP\AppFramework\Http\DataDisplayResponse;
 use OCP\AppFramework\Http\DataResponse;
 use OCP\AppFramework\Http\RedirectResponse;
+use OCP\DB\Events\AddMissingIndicesEvent;
 use OCP\DB\Types;
+use OCP\EventDispatcher\IEventDispatcher;
 use OCP\Http\Client\IClientService;
 use OCP\IConfig;
 use OCP\IDateTimeFormatter;
@@ -101,6 +103,8 @@ class CheckSetupController extends Controller {
 	private $checker;
 	/** @var LoggerInterface */
 	private $logger;
+	/** @var IEventDispatcher */
+	private $eventDispatcher;
 	/** @var EventDispatcherInterface */
 	private $dispatcher;
 	/** @var Connection */
@@ -134,6 +138,7 @@ class CheckSetupController extends Controller {
 								IL10N $l10n,
 								Checker $checker,
 								LoggerInterface $logger,
+								IEventDispatcher $eventDispatcher,
 								EventDispatcherInterface $dispatcher,
 								Connection $db,
 								ILockingProvider $lockingProvider,
@@ -154,6 +159,7 @@ class CheckSetupController extends Controller {
 		$this->l10n = $l10n;
 		$this->checker = $checker;
 		$this->logger = $logger;
+		$this->eventDispatcher = $eventDispatcher;
 		$this->dispatcher = $dispatcher;
 		$this->db = $db;
 		$this->lockingProvider = $lockingProvider;
@@ -550,10 +556,27 @@ Raw output
 
 	protected function hasMissingIndexes(): array {
 		$indexInfo = new MissingIndexInformation();
+
 		// Dispatch event so apps can also hint for pending index updates if needed
 		$event = new GenericEvent($indexInfo);
 		$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_INDEXES_EVENT, $event);
 
+		$event = new AddMissingIndicesEvent();
+		$this->eventDispatcher->dispatchTyped($event);
+		$missingIndices = $event->getMissingIndices();
+
+		if ($missingIndices !== []) {
+			$schema = new SchemaWrapper(\OCP\Server::get(Connection::class));
+			foreach ($missingIndices as $missingIndex) {
+				if ($schema->hasTable($missingIndex['tableName'])) {
+					$table = $schema->getTable($missingIndex['tableName']);
+					if (!$table->hasIndex($missingIndex['indexName'])) {
+						$indexInfo->addHintForMissingSubject($missingIndex['tableName'], $missingIndex['indexName']);
+					}
+				}
+			}
+		}
+
 		return $indexInfo->getListOfMissingIndexes();
 	}
 

+ 8 - 0
apps/settings/tests/Controller/CheckSetupControllerTest.php

@@ -47,6 +47,7 @@ use OCP\AppFramework\Http;
 use OCP\AppFramework\Http\DataDisplayResponse;
 use OCP\AppFramework\Http\DataResponse;
 use OCP\AppFramework\Http\RedirectResponse;
+use OCP\EventDispatcher\IEventDispatcher;
 use OCP\Http\Client\IClientService;
 use OCP\IConfig;
 use OCP\IDateTimeFormatter;
@@ -87,6 +88,8 @@ class CheckSetupControllerTest extends TestCase {
 	private $logger;
 	/** @var Checker|\PHPUnit\Framework\MockObject\MockObject */
 	private $checker;
+	/** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
+	private $eventDispatcher;
 	/** @var EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */
 	private $dispatcher;
 	/** @var Connection|\PHPUnit\Framework\MockObject\MockObject */
@@ -137,6 +140,7 @@ class CheckSetupControllerTest extends TestCase {
 			->willReturnCallback(function ($message, array $replace) {
 				return vsprintf($message, $replace);
 			});
+		$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
 		$this->dispatcher = $this->getMockBuilder(EventDispatcherInterface::class)
 			->disableOriginalConstructor()->getMock();
 		$this->checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
@@ -167,6 +171,7 @@ class CheckSetupControllerTest extends TestCase {
 				$this->l10n,
 				$this->checker,
 				$this->logger,
+				$this->eventDispatcher,
 				$this->dispatcher,
 				$this->db,
 				$this->lockingProvider,
@@ -670,6 +675,7 @@ class CheckSetupControllerTest extends TestCase {
 				$this->l10n,
 				$this->checker,
 				$this->logger,
+				$this->eventDispatcher,
 				$this->dispatcher,
 				$this->db,
 				$this->lockingProvider,
@@ -1434,6 +1440,7 @@ Array
 			$this->l10n,
 			$this->checker,
 			$this->logger,
+			$this->eventDispatcher,
 			$this->dispatcher,
 			$this->db,
 			$this->lockingProvider,
@@ -1488,6 +1495,7 @@ Array
 			$this->l10n,
 			$this->checker,
 			$this->logger,
+			$this->eventDispatcher,
 			$this->dispatcher,
 			$this->db,
 			$this->lockingProvider,

+ 32 - 2
core/Command/Db/AddMissingIndices.php

@@ -36,6 +36,8 @@ namespace OC\Core\Command\Db;
 use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
 use OC\DB\Connection;
 use OC\DB\SchemaWrapper;
+use OCP\DB\Events\AddMissingIndicesEvent;
+use OCP\EventDispatcher\IEventDispatcher;
 use OCP\IDBConnection;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputOption;
@@ -54,12 +56,14 @@ use Symfony\Component\EventDispatcher\GenericEvent;
  */
 class AddMissingIndices extends Command {
 	private Connection $connection;
+	private IEventDispatcher $eventDispatcher;
 	private EventDispatcherInterface $dispatcher;
 
-	public function __construct(Connection $connection, EventDispatcherInterface $dispatcher) {
+	public function __construct(Connection $connection, IEventDispatcher $eventDispatcher, EventDispatcherInterface $dispatcher) {
 		parent::__construct();
 
 		$this->connection = $connection;
+		$this->eventDispatcher = $eventDispatcher;
 		$this->dispatcher = $dispatcher;
 	}
 
@@ -71,11 +75,37 @@ class AddMissingIndices extends Command {
 	}
 
 	protected function execute(InputInterface $input, OutputInterface $output): int {
-		$this->addCoreIndexes($output, $input->getOption('dry-run'));
+		$dryRun = $input->getOption('dry-run');
+
+		$this->addCoreIndexes($output, $dryRun);
 
 		// Dispatch event so apps can also update indexes if needed
 		$event = new GenericEvent($output);
 		$this->dispatcher->dispatch(IDBConnection::ADD_MISSING_INDEXES_EVENT, $event);
+
+		$event = new AddMissingIndicesEvent();
+		$this->eventDispatcher->dispatchTyped($event);
+
+		$missingIndices = $event->getMissingIndices();
+		if ($missingIndices !== []) {
+			$schema = new SchemaWrapper($this->connection);
+
+			foreach ($missingIndices as $missingIndex) {
+				if ($schema->hasTable($missingIndex['tableName'])) {
+					$table = $schema->getTable($missingIndex['tableName']);
+					if (!$table->hasIndex($missingIndex['indexName'])) {
+						$output->writeln('<info>Adding additional ' . $missingIndex['indexName'] . ' index to the ' . $table->getName() . ' table, this can take some time...</info>');
+						$table->addIndex($missingIndex['columns'], $missingIndex['indexName']);
+						$sqlQueries = $this->connection->migrateToSchema($schema->getWrappedSchema(), $dryRun);
+						if ($dryRun && $sqlQueries !== null) {
+							$output->writeln($sqlQueries);
+						}
+						$output->writeln('<info>' . $table->getName() . ' table updated successfully.</info>');
+					}
+				}
+			}
+		}
+
 		return 0;
 	}
 

+ 1 - 1
core/register_command.php

@@ -109,7 +109,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
 	$application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig())));
 	$application->add(new OC\Core\Command\Db\ConvertMysqlToMB4(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection(), \OC::$server->getURLGenerator(), \OC::$server->get(LoggerInterface::class)));
 	$application->add(new OC\Core\Command\Db\ConvertFilecacheBigInt(\OC::$server->get(\OC\DB\Connection::class)));
-	$application->add(new OC\Core\Command\Db\AddMissingIndices(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getEventDispatcher()));
+	$application->add(\OCP\Server::get(\OC\Core\Command\Db\AddMissingIndices::class));
 	$application->add(new OC\Core\Command\Db\AddMissingColumns(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getEventDispatcher()));
 	$application->add(new OC\Core\Command\Db\AddMissingPrimaryKeys(\OC::$server->get(\OC\DB\Connection::class), \OC::$server->getEventDispatcher()));
 

+ 1 - 0
lib/composer/composer/autoload_classmap.php

@@ -186,6 +186,7 @@ return array(
     'OCP\\Contacts\\ContactsMenu\\IProvider' => $baseDir . '/lib/public/Contacts/ContactsMenu/IProvider.php',
     'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => $baseDir . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php',
     'OCP\\Contacts\\IManager' => $baseDir . '/lib/public/Contacts/IManager.php',
+    'OCP\\DB\\Events\\AddMissingIndicesEvent' => $baseDir . '/lib/public/DB/Events/AddMissingIndicesEvent.php',
     'OCP\\DB\\Exception' => $baseDir . '/lib/public/DB/Exception.php',
     'OCP\\DB\\IPreparedStatement' => $baseDir . '/lib/public/DB/IPreparedStatement.php',
     'OCP\\DB\\IResult' => $baseDir . '/lib/public/DB/IResult.php',

+ 1 - 0
lib/composer/composer/autoload_static.php

@@ -219,6 +219,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
         'OCP\\Contacts\\ContactsMenu\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IProvider.php',
         'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => __DIR__ . '/../../..' . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php',
         'OCP\\Contacts\\IManager' => __DIR__ . '/../../..' . '/lib/public/Contacts/IManager.php',
+        'OCP\\DB\\Events\\AddMissingIndicesEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingIndicesEvent.php',
         'OCP\\DB\\Exception' => __DIR__ . '/../../..' . '/lib/public/DB/Exception.php',
         'OCP\\DB\\IPreparedStatement' => __DIR__ . '/../../..' . '/lib/public/DB/IPreparedStatement.php',
         'OCP\\DB\\IResult' => __DIR__ . '/../../..' . '/lib/public/DB/IResult.php',

+ 59 - 0
lib/public/DB/Events/AddMissingIndicesEvent.php

@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Julius Härtl <jus@bitgrid.net
+ *
+ * @author Julius Härtl <jus@bitgrid.net
+ *
+ * @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 OCP\DB\Events;
+
+/**
+ * Event to allow apps to register information about missing database indices
+ *
+ * This event will be dispatched for checking on the admin settings and when running
+ * occ db:add-missing-indices which will then create those indices
+ *
+ * @since 28.0.0
+ */
+class AddMissingIndicesEvent extends \OCP\EventDispatcher\Event {
+	/** @var array<array-key, array{tableName: string, indexName: string, columns: string[]}> */
+	private array $missingIndices = [];
+
+	/**
+	 * @param string[] $columns
+	 * @since 28.0.0
+	 */
+	public function addMissingIndex(string $tableName, string $indexName, array $columns): void {
+		$this->missingIndices[] = [
+			'tableName' => $tableName,
+			'indexName' => $indexName,
+			'columns' => $columns
+		];
+	}
+
+	/**
+	 * @since 28.0.0
+	 * @return array<array-key, array{tableName: string, indexName: string, columns: string[]}>
+	 */
+	public function getMissingIndices(): array {
+		return $this->missingIndices;
+	}
+}

+ 3 - 2
lib/public/IDBConnection.php

@@ -34,6 +34,7 @@
 namespace OCP;
 
 use Doctrine\DBAL\Schema\Schema;
+use OCP\DB\Events\AddMissingIndicesEvent;
 use OCP\DB\Exception;
 use OCP\DB\IPreparedStatement;
 use OCP\DB\IResult;
@@ -46,12 +47,12 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
  */
 interface IDBConnection {
 	/**
-	 * @deprecated 22.0.0 this is an internal event
+	 * @deprecated 22.0.0 this is an internal event, use {@see AddMissingIndicesEvent} instead
 	 */
 	public const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES';
 
 	/**
-	 * @deprecated 22.0.0 this is an internal event
+	 * @deprecated 22.0.0 this is an internal event, use {@see AddMissingIndicesEvent} instead
 	 */
 	public const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES';