Add optional column oc_comments.reference_id

Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Joas Schilling 2020-03-11 12:29:52 +01:00
parent 0faed106d7
commit 720dc4e93d
No known key found for this signature in database
GPG Key ID: 7076EA9751AACDDA
11 changed files with 216 additions and 1 deletions

View File

@ -46,6 +46,7 @@ use GuzzleHttp\Exception\ClientException;
use OC; use OC;
use OC\AppFramework\Http; use OC\AppFramework\Http;
use OC\DB\Connection; use OC\DB\Connection;
use OC\DB\MissingColumnInformation;
use OC\DB\MissingIndexInformation; use OC\DB\MissingIndexInformation;
use OC\DB\SchemaWrapper; use OC\DB\SchemaWrapper;
use OC\IntegrityCheck\Checker; use OC\IntegrityCheck\Checker;
@ -445,6 +446,15 @@ Raw output
return $indexInfo->getListOfMissingIndexes(); return $indexInfo->getListOfMissingIndexes();
} }
protected function hasMissingColumns(): array {
$indexInfo = new MissingColumnInformation();
// Dispatch event so apps can also hint for pending index updates if needed
$event = new GenericEvent($indexInfo);
$this->dispatcher->dispatch(IDBConnection::CHECK_MISSING_COLUMNS_EVENT, $event);
return $indexInfo->getListOfMissingColumns();
}
protected function isSqliteUsed() { protected function isSqliteUsed() {
return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false; return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
} }
@ -693,6 +703,7 @@ Raw output
'isSettimelimitAvailable' => $this->isSettimelimitAvailable(), 'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
'hasFreeTypeSupport' => $this->hasFreeTypeSupport(), 'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
'missingIndexes' => $this->hasMissingIndexes(), 'missingIndexes' => $this->hasMissingIndexes(),
'missingColumns' => $this->hasMissingColumns(),
'isSqliteUsed' => $this->isSqliteUsed(), 'isSqliteUsed' => $this->isSqliteUsed(),
'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'), 'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
'isPHPMailerUsed' => $this->isPHPMailerUsed(), 'isPHPMailerUsed' => $this->isPHPMailerUsed(),

View File

@ -583,6 +583,7 @@ class CheckSetupControllerTest extends TestCase {
'isSqliteUsed' => false, 'isSqliteUsed' => false,
'databaseConversionDocumentation' => 'http://docs.example.org/server/go.php?to=admin-db-conversion', 'databaseConversionDocumentation' => 'http://docs.example.org/server/go.php?to=admin-db-conversion',
'missingIndexes' => [], 'missingIndexes' => [],
'missingColumns' => [],
'isPHPMailerUsed' => false, 'isPHPMailerUsed' => false,
'mailSettingsDocumentation' => 'https://server/index.php/settings/admin', 'mailSettingsDocumentation' => 'https://server/index.php/settings/admin',
'isMemoryLimitSufficient' => true, 'isMemoryLimitSufficient' => true,

View File

@ -39,6 +39,7 @@ use OC\Authentication\Listeners\RemoteWipeNotificationsListener;
use OC\Authentication\Listeners\UserDeletedStoreCleanupListener; use OC\Authentication\Listeners\UserDeletedStoreCleanupListener;
use OC\Authentication\Notifications\Notifier as AuthenticationNotifier; use OC\Authentication\Notifications\Notifier as AuthenticationNotifier;
use OC\Core\Notification\RemoveLinkSharesNotifier; use OC\Core\Notification\RemoveLinkSharesNotifier;
use OC\DB\MissingColumnInformation;
use OC\DB\MissingIndexInformation; use OC\DB\MissingIndexInformation;
use OC\DB\SchemaWrapper; use OC\DB\SchemaWrapper;
use OCP\AppFramework\App; use OCP\AppFramework\App;
@ -167,6 +168,23 @@ class Application extends App {
} }
); );
$eventDispatcher->addListener(IDBConnection::CHECK_MISSING_COLUMNS_EVENT,
function (GenericEvent $event) use ($container) {
/** @var MissingColumnInformation $subject */
$subject = $event->getSubject();
$schema = new SchemaWrapper($container->query(IDBConnection::class));
if ($schema->hasTable('comments')) {
$table = $schema->getTable('comments');
if (!$table->hasColumn('reference_id')) {
$subject->addHintForMissingColumn($table->getName(), 'reference_id');
}
}
}
);
$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class); $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class); $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class);
$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class); $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);

View File

@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.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;
use OC\DB\SchemaWrapper;
use OCP\IDBConnection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
/**
* Class AddMissingColumns
*
* if you added a new lazy column to the database, this is the right place to add
* your update routine for existing instances
*
* @package OC\Core\Command\Db
*/
class AddMissingColumns extends Command {
/** @var IDBConnection */
private $connection;
/** @var EventDispatcherInterface */
private $dispatcher;
public function __construct(IDBConnection $connection, EventDispatcherInterface $dispatcher) {
parent::__construct();
$this->connection = $connection;
$this->dispatcher = $dispatcher;
}
protected function configure() {
$this
->setName('db:add-missing-columns')
->setDescription('Add missing optional columns to the database tables');
}
protected function execute(InputInterface $input, OutputInterface $output) {
$this->addCoreColumns($output);
// Dispatch event so apps can also update columns if needed
$event = new GenericEvent($output);
$this->dispatcher->dispatch(IDBConnection::ADD_MISSING_COLUMNS_EVENT, $event);
}
/**
* add missing indices to the share table
*
* @param OutputInterface $output
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
private function addCoreColumns(OutputInterface $output) {
$output->writeln('<info>Check columns of the comments table.</info>');
$schema = new SchemaWrapper($this->connection);
$updated = false;
if ($schema->hasTable('comments')) {
$table = $schema->getTable('comments');
if (!$table->hasColumn('reference_id')) {
$output->writeln('<info>Adding additional reference_id column to the comments table, this can take some time...</info>');
$table->addColumn('reference_id', 'string', [
'notnull' => false,
'length' => 64,
]);
$this->connection->migrateToSchema($schema->getWrappedSchema());
$updated = true;
$output->writeln('<info>Comments table updated successfully.</info>');
}
}
if (!$updated) {
$output->writeln('<info>Done.</info>');
}
}
}

View File

@ -43,7 +43,7 @@ use Symfony\Component\EventDispatcher\GenericEvent;
* Class AddMissingIndices * Class AddMissingIndices
* *
* if you added any new indices to the database, this is the right place to add * if you added any new indices to the database, this is the right place to add
* it your update routine for existing instances * your update routine for existing instances
* *
* @package OC\Core\Command\Db * @package OC\Core\Command\Db
*/ */

View File

@ -769,6 +769,10 @@ class Version13000Date20170718121200 extends SimpleMigrationStep {
'length' => 64, 'length' => 64,
'default' => '', 'default' => '',
]); ]);
$table->addColumn('reference_id', 'string', [
'notnull' => false,
'length' => 64,
]);
$table->setPrimaryKey(['id']); $table->setPrimaryKey(['id']);
$table->addIndex(['parent_id'], 'comments_parent_id_index'); $table->addIndex(['parent_id'], 'comments_parent_id_index');
$table->addIndex(['topmost_parent_id'], 'comments_topmost_parent_id_idx'); $table->addIndex(['topmost_parent_id'], 'comments_topmost_parent_id_idx');

View File

@ -357,6 +357,21 @@
type: OC.SetupChecks.MESSAGE_TYPE_INFO type: OC.SetupChecks.MESSAGE_TYPE_INFO
}) })
} }
if (data.missingColumns.length > 0) {
var listOfMissingColumns = "";
data.missingColumns.forEach(function(element){
listOfMissingColumns += "<li>";
listOfMissingColumns += t('core', 'Missing optional column "{columnName}" in table "{tableName}".', element);
listOfMissingColumns += "</li>";
});
messages.push({
msg: t(
'core',
'The database is missing some optional columns. Due to the fact that adding columns on big tables could take some time they were not added automatically when they can be optional. By running "occ db:add-missing-columns" those missing columns could be added manually while the instance keeps running. Once the columns are added some features might improve responsiveness or usability.'
) + "<ul>" + listOfMissingColumns + "</ul>",
type: OC.SetupChecks.MESSAGE_TYPE_INFO
})
}
if (data.recommendedPHPModules.length > 0) { if (data.recommendedPHPModules.length > 0) {
var listOfRecommendedPHPModules = ""; var listOfRecommendedPHPModules = "";
data.recommendedPHPModules.forEach(function(element){ data.recommendedPHPModules.forEach(function(element){

View File

@ -240,6 +240,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -293,6 +294,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -347,6 +349,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -399,6 +402,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -449,6 +453,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -499,6 +504,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -551,6 +557,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -601,6 +608,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: false, isSettimelimitAvailable: false,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -651,6 +659,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -722,6 +731,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -773,6 +783,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -824,6 +835,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -875,6 +887,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: false, hasFreeTypeSupport: false,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -925,6 +938,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0
@ -1026,6 +1040,7 @@ describe('OC.SetupChecks tests', function() {
isSettimelimitAvailable: true, isSettimelimitAvailable: true,
hasFreeTypeSupport: true, hasFreeTypeSupport: true,
missingIndexes: [], missingIndexes: [],
missingColumns: [],
cronErrors: [], cronErrors: [],
cronInfo: { cronInfo: {
diffInSeconds: 0 diffInSeconds: 0

View File

@ -99,6 +99,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
$application->add(new OC\Core\Command\Db\ConvertMysqlToMB4(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection(), \OC::$server->getURLGenerator(), \OC::$server->getLogger())); $application->add(new OC\Core\Command\Db\ConvertMysqlToMB4(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection(), \OC::$server->getURLGenerator(), \OC::$server->getLogger()));
$application->add(new OC\Core\Command\Db\ConvertFilecacheBigInt(\OC::$server->getDatabaseConnection())); $application->add(new OC\Core\Command\Db\ConvertFilecacheBigInt(\OC::$server->getDatabaseConnection()));
$application->add(new OC\Core\Command\Db\AddMissingIndices(\OC::$server->getDatabaseConnection(), \OC::$server->getEventDispatcher())); $application->add(new OC\Core\Command\Db\AddMissingIndices(\OC::$server->getDatabaseConnection(), \OC::$server->getEventDispatcher()));
$application->add(new OC\Core\Command\Db\AddMissingColumns(\OC::$server->getDatabaseConnection(), \OC::$server->getEventDispatcher()));
$application->add(new OC\Core\Command\Db\Migrations\StatusCommand(\OC::$server->getDatabaseConnection())); $application->add(new OC\Core\Command\Db\Migrations\StatusCommand(\OC::$server->getDatabaseConnection()));
$application->add(new OC\Core\Command\Db\Migrations\MigrateCommand(\OC::$server->getDatabaseConnection())); $application->add(new OC\Core\Command\Db\Migrations\MigrateCommand(\OC::$server->getDatabaseConnection()));
$application->add(new OC\Core\Command\Db\Migrations\GenerateCommand(\OC::$server->getDatabaseConnection(), \OC::$server->getAppManager())); $application->add(new OC\Core\Command\Db\Migrations\GenerateCommand(\OC::$server->getDatabaseConnection(), \OC::$server->getAppManager()));

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
*
* @author Joas Schilling <coding@schilljs.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\DB;
class MissingColumnInformation {
private $listOfMissingColumns = [];
public function addHintForMissingColumn(string $tableName, string $columnName): void {
$this->listOfMissingColumns[] = [
'tableName' => $tableName,
'columnName' => $columnName,
];
}
public function getListOfMissingColumns(): array {
return $this->listOfMissingColumns;
}
}

View File

@ -50,6 +50,8 @@ interface IDBConnection {
const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES'; const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_MISSING_INDEXES';
const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES'; const CHECK_MISSING_INDEXES_EVENT = self::class . '::CHECK_MISSING_INDEXES';
const ADD_MISSING_COLUMNS_EVENT = self::class . '::ADD_MISSING_COLUMNS';
const CHECK_MISSING_COLUMNS_EVENT = self::class . '::CHECK_MISSING_COLUMNS';
/** /**
* Gets the QueryBuilder for the connection. * Gets the QueryBuilder for the connection.