diff --git a/apps/dav/tests/unit/Comments/CommentsNodeTest.php b/apps/dav/tests/unit/Comments/CommentsNodeTest.php
index 0555d09379c06de843c2d2ee5a4de13d8e386f48..a219b0a5c746be626456a8065488c20ba8702cb7 100644
--- a/apps/dav/tests/unit/Comments/CommentsNodeTest.php
+++ b/apps/dav/tests/unit/Comments/CommentsNodeTest.php
@@ -108,7 +108,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->node->delete();
 	}
 
-	
+
 	public function testDeleteForbidden() {
 		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
 
@@ -150,7 +150,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->assertSame($this->node->getName(), $id);
 	}
 
-	
+
 	public function testSetName() {
 		$this->expectException(\Sabre\DAV\Exception\MethodNotAllowed::class);
 
@@ -195,7 +195,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->assertTrue($this->node->updateComment($msg));
 	}
 
-	
+
 	public function testUpdateCommentLogException() {
 		$this->expectException(\Exception::class);
 		$this->expectExceptionMessage('buh!');
@@ -236,7 +236,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->node->updateComment($msg);
 	}
 
-	
+
 	public function testUpdateCommentMessageTooLongException() {
 		$this->expectException(\Sabre\DAV\Exception\BadRequest::class);
 		$this->expectExceptionMessage('Message exceeds allowed character limit of');
@@ -275,7 +275,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->node->updateComment('foo');
 	}
 
-	
+
 	public function testUpdateForbiddenByUser() {
 		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
 
@@ -310,7 +310,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->node->updateComment($msg);
 	}
 
-	
+
 	public function testUpdateForbiddenByType() {
 		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
 
@@ -340,7 +340,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$this->node->updateComment($msg);
 	}
 
-	
+
 	public function testUpdateForbiddenByNotLoggedIn() {
 		$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
 
@@ -403,6 +403,7 @@ class CommentsNodeTest extends \Test\TestCase {
 			$ns . 'latestChildDateTime' => new \DateTime('2016-01-12 18:48:00'),
 			$ns . 'objectType' => 'files',
 			$ns . 'objectId' => '1848',
+			$ns . 'referenceId' => 'ref',
 			$ns . 'isUnread' => null,
 		];
 
@@ -469,6 +470,10 @@ class CommentsNodeTest extends \Test\TestCase {
 			->method('getObjectId')
 			->willReturn($expected[$ns . 'objectId']);
 
+		$this->comment->expects($this->once())
+			->method('getReferenceId')
+			->willReturn($expected[$ns . 'referenceId']);
+
 		$user = $this->getMockBuilder(IUser::class)
 			->disableOriginalConstructor()
 			->getMock();
@@ -484,7 +489,7 @@ class CommentsNodeTest extends \Test\TestCase {
 		$properties = $this->node->getProperties(null);
 
 		foreach($properties as $name => $value) {
-			$this->assertTrue(array_key_exists($name, $expected));
+			$this->assertArrayHasKey($name, $expected);
 			$this->assertSame($expected[$name], $value);
 			unset($expected[$name]);
 		}
diff --git a/apps/settings/lib/Controller/CheckSetupController.php b/apps/settings/lib/Controller/CheckSetupController.php
index 04711cf5308ad74761bddb516ac7ec7cff29f0ff..6f3bb539eff568e8cd5c0720bebfd9c7b0619fa5 100644
--- a/apps/settings/lib/Controller/CheckSetupController.php
+++ b/apps/settings/lib/Controller/CheckSetupController.php
@@ -46,6 +46,7 @@ use GuzzleHttp\Exception\ClientException;
 use OC;
 use OC\AppFramework\Http;
 use OC\DB\Connection;
+use OC\DB\MissingColumnInformation;
 use OC\DB\MissingIndexInformation;
 use OC\DB\SchemaWrapper;
 use OC\IntegrityCheck\Checker;
@@ -445,6 +446,15 @@ Raw output
 		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() {
 		return strpos($this->config->getSystemValue('dbtype'), 'sqlite') !== false;
 	}
@@ -693,6 +703,7 @@ Raw output
 				'isSettimelimitAvailable' => $this->isSettimelimitAvailable(),
 				'hasFreeTypeSupport' => $this->hasFreeTypeSupport(),
 				'missingIndexes' => $this->hasMissingIndexes(),
+				'missingColumns' => $this->hasMissingColumns(),
 				'isSqliteUsed' => $this->isSqliteUsed(),
 				'databaseConversionDocumentation' => $this->urlGenerator->linkToDocs('admin-db-conversion'),
 				'isPHPMailerUsed' => $this->isPHPMailerUsed(),
diff --git a/apps/settings/tests/Controller/CheckSetupControllerTest.php b/apps/settings/tests/Controller/CheckSetupControllerTest.php
index 5e59bfe353aff382ba2193a6b925a8088d4ea564..c15f3b8f23ac57c0de26e1493ed3939ec361f3db 100644
--- a/apps/settings/tests/Controller/CheckSetupControllerTest.php
+++ b/apps/settings/tests/Controller/CheckSetupControllerTest.php
@@ -583,6 +583,7 @@ class CheckSetupControllerTest extends TestCase {
 				'isSqliteUsed' => false,
 				'databaseConversionDocumentation' => 'http://docs.example.org/server/go.php?to=admin-db-conversion',
 				'missingIndexes' => [],
+				'missingColumns' => [],
 				'isPHPMailerUsed' => false,
 				'mailSettingsDocumentation' => 'https://server/index.php/settings/admin',
 				'isMemoryLimitSufficient' => true,
diff --git a/core/Application.php b/core/Application.php
index c1ac8971532167feab410b9a3eddea894762762b..15246c9c76c608b8148cec058e5f93e5e910bbdf 100644
--- a/core/Application.php
+++ b/core/Application.php
@@ -39,6 +39,7 @@ use OC\Authentication\Listeners\RemoteWipeNotificationsListener;
 use OC\Authentication\Listeners\UserDeletedStoreCleanupListener;
 use OC\Authentication\Notifications\Notifier as AuthenticationNotifier;
 use OC\Core\Notification\RemoveLinkSharesNotifier;
+use OC\DB\MissingColumnInformation;
 use OC\DB\MissingIndexInformation;
 use OC\DB\SchemaWrapper;
 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, RemoteWipeNotificationsListener::class);
 		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);
diff --git a/core/Command/Db/AddMissingColumns.php b/core/Command/Db/AddMissingColumns.php
new file mode 100644
index 0000000000000000000000000000000000000000..4672770c6af33f5b66e14fa017add98badf4ed09
--- /dev/null
+++ b/core/Command/Db/AddMissingColumns.php
@@ -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>');
+		}
+	}
+}
diff --git a/core/Command/Db/AddMissingIndices.php b/core/Command/Db/AddMissingIndices.php
index b1763575ccf564348fa37c8d558b215424871452..924c2af05c03f1a46cd58049e4eff3dc527b106a 100644
--- a/core/Command/Db/AddMissingIndices.php
+++ b/core/Command/Db/AddMissingIndices.php
@@ -43,7 +43,7 @@ use Symfony\Component\EventDispatcher\GenericEvent;
  * Class AddMissingIndices
  *
  * 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
  */
diff --git a/core/Migrations/Version13000Date20170718121200.php b/core/Migrations/Version13000Date20170718121200.php
index e9f376a7e424852ecdfa523eb1c929e310d083aa..757697e7e90a5ce9c198213866afc22cbd8a966b 100644
--- a/core/Migrations/Version13000Date20170718121200.php
+++ b/core/Migrations/Version13000Date20170718121200.php
@@ -769,6 +769,10 @@ class Version13000Date20170718121200 extends SimpleMigrationStep {
 				'length' => 64,
 				'default' => '',
 			]);
+			$table->addColumn('reference_id', 'string', [
+				'notnull' => false,
+				'length' => 64,
+			]);
 			$table->setPrimaryKey(['id']);
 			$table->addIndex(['parent_id'], 'comments_parent_id_index');
 			$table->addIndex(['topmost_parent_id'], 'comments_topmost_parent_id_idx');
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index 12d1104a6327b31c79073d7ef127b7705b5ad340..2e94c82486ceb335b61fa6f3c29b70f1a2234884 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -357,6 +357,21 @@
 							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) {
 						var listOfRecommendedPHPModules = "";
 						data.recommendedPHPModules.forEach(function(element){
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index 5e93cbf7bdf868e9a70cb427e8efd3e3428e27d2..f4c81c6bf78ed6a9945e95e4892d4d48b50a1c80 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -240,6 +240,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -293,6 +294,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -347,6 +349,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -399,6 +402,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -449,6 +453,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -499,6 +504,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -551,6 +557,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -601,6 +608,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: false,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -651,6 +659,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -722,6 +731,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -773,6 +783,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -824,6 +835,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -875,6 +887,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: false,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -925,6 +938,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -975,6 +989,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
@@ -1026,6 +1041,7 @@ describe('OC.SetupChecks tests', function() {
 					isSettimelimitAvailable: true,
 					hasFreeTypeSupport: true,
 					missingIndexes: [],
+					missingColumns: [],
 					cronErrors: [],
 					cronInfo: {
 						diffInSeconds: 0
diff --git a/core/register_command.php b/core/register_command.php
index efa3146c49223cad1f705766ce77dc163a4cf07a..e355d1429c1484221b234618bb959845ad697587 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -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\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\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\MigrateCommand(\OC::$server->getDatabaseConnection()));
 	$application->add(new OC\Core\Command\Db\Migrations\GenerateCommand(\OC::$server->getDatabaseConnection(), \OC::$server->getAppManager()));
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 4cf77a34a5c2c77ba31fdfe007996d005b739220..9a1f5befb4d487d6779206190db829a9cd9eb957 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -738,6 +738,7 @@ return array(
     'OC\\Core\\Command\\Config\\System\\DeleteConfig' => $baseDir . '/core/Command/Config/System/DeleteConfig.php',
     'OC\\Core\\Command\\Config\\System\\GetConfig' => $baseDir . '/core/Command/Config/System/GetConfig.php',
     'OC\\Core\\Command\\Config\\System\\SetConfig' => $baseDir . '/core/Command/Config/System/SetConfig.php',
+    'OC\\Core\\Command\\Db\\AddMissingColumns' => $baseDir . '/core/Command/Db/AddMissingColumns.php',
     'OC\\Core\\Command\\Db\\AddMissingIndices' => $baseDir . '/core/Command/Db/AddMissingIndices.php',
     'OC\\Core\\Command\\Db\\ConvertFilecacheBigInt' => $baseDir . '/core/Command/Db/ConvertFilecacheBigInt.php',
     'OC\\Core\\Command\\Db\\ConvertMysqlToMB4' => $baseDir . '/core/Command/Db/ConvertMysqlToMB4.php',
@@ -873,6 +874,7 @@ return array(
     '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',
+    'OC\\DB\\MissingColumnInformation' => $baseDir . '/lib/private/DB/MissingColumnInformation.php',
     'OC\\DB\\MissingIndexInformation' => $baseDir . '/lib/private/DB/MissingIndexInformation.php',
     'OC\\DB\\MySQLMigrator' => $baseDir . '/lib/private/DB/MySQLMigrator.php',
     'OC\\DB\\MySqlTools' => $baseDir . '/lib/private/DB/MySqlTools.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index c3a1a6a626458961740d7971fd55c38afa5476df..318888bd434497964df925f3e9ed9feab3646376 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -767,6 +767,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         'OC\\Core\\Command\\Config\\System\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/DeleteConfig.php',
         'OC\\Core\\Command\\Config\\System\\GetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/GetConfig.php',
         'OC\\Core\\Command\\Config\\System\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/SetConfig.php',
+        'OC\\Core\\Command\\Db\\AddMissingColumns' => __DIR__ . '/../../..' . '/core/Command/Db/AddMissingColumns.php',
         'OC\\Core\\Command\\Db\\AddMissingIndices' => __DIR__ . '/../../..' . '/core/Command/Db/AddMissingIndices.php',
         'OC\\Core\\Command\\Db\\ConvertFilecacheBigInt' => __DIR__ . '/../../..' . '/core/Command/Db/ConvertFilecacheBigInt.php',
         'OC\\Core\\Command\\Db\\ConvertMysqlToMB4' => __DIR__ . '/../../..' . '/core/Command/Db/ConvertMysqlToMB4.php',
@@ -902,6 +903,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
         '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',
+        'OC\\DB\\MissingColumnInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingColumnInformation.php',
         'OC\\DB\\MissingIndexInformation' => __DIR__ . '/../../..' . '/lib/private/DB/MissingIndexInformation.php',
         'OC\\DB\\MySQLMigrator' => __DIR__ . '/../../..' . '/lib/private/DB/MySQLMigrator.php',
         'OC\\DB\\MySqlTools' => __DIR__ . '/../../..' . '/lib/private/DB/MySqlTools.php',
diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php
index 71687c7a60920fe971fd367a662502c898d32748..3b4a523b8848c3aa86d9d84ec6dd88c8f92ad6c2 100644
--- a/lib/private/Comments/Comment.php
+++ b/lib/private/Comments/Comment.php
@@ -42,6 +42,7 @@ class Comment implements IComment {
 		'actorId'         => '',
 		'objectType'      => '',
 		'objectId'        => '',
+		'referenceId'     => null,
 		'creationDT'      => null,
 		'latestChildDT'   => null,
 	];
@@ -395,6 +396,36 @@ class Comment implements IComment {
 		return $this;
 	}
 
+	/**
+	 * returns the reference id of the comment
+	 *
+	 * @return string|null
+	 * @since 19.0.0
+	 */
+	public function getReferenceId(): ?string {
+		return $this->data['referenceId'];
+	}
+
+	/**
+	 * sets (overwrites) the reference id of the comment
+	 *
+	 * @param string $referenceId e.g. sha256 hash sum
+	 * @return IComment
+	 * @since 19.0.0
+	 */
+	public function setReferenceId(?string $referenceId): IComment {
+		if ($referenceId === null) {
+			$this->data['referenceId'] = $referenceId;
+		} else {
+			$referenceId = trim($referenceId);
+			if ($referenceId === '') {
+				throw new \InvalidArgumentException('Non empty string expected.');
+			}
+			$this->data['referenceId'] = $referenceId;
+		}
+		return $this;
+	}
+
 	/**
 	 * sets the comment data based on an array with keys as taken from the
 	 * database.
diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php
index f1c7224359774ea3ea5bd4145992c1e02afd906b..d04f3f965b34857e135c24ab9e78a820018be5ab 100644
--- a/lib/private/Comments/Manager.php
+++ b/lib/private/Comments/Manager.php
@@ -29,6 +29,7 @@
 namespace OC\Comments;
 
 use Doctrine\DBAL\Exception\DriverException;
+use Doctrine\DBAL\Exception\InvalidFieldNameException;
 use OCP\Comments\CommentsEvent;
 use OCP\Comments\IComment;
 use OCP\Comments\ICommentsEventHandler;
@@ -96,6 +97,7 @@ class Manager implements ICommentsManager {
 			$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
 		}
 		$data['children_count'] = (int)$data['children_count'];
+		$data['reference_id'] = $data['reference_id'] ?? null;
 		return $data;
 	}
 
@@ -744,23 +746,46 @@ class Manager implements ICommentsManager {
 	 * @param IComment $comment
 	 * @return bool
 	 */
-	protected function insert(IComment &$comment) {
+	protected function insert(IComment $comment): bool {
+
+		try {
+			$result = $this->insertQuery($comment, true);
+		} catch (InvalidFieldNameException $e) {
+			// The reference id field was only added in Nextcloud 19.
+			// In order to not cause too long waiting times on the update,
+			// it was decided to only add it lazy, as it is also not a critical
+			// feature, but only helps to have a better experience while commenting.
+			// So in case the reference_id field is missing,
+			// we simply save the comment without that field.
+			$result = $this->insertQuery($comment, false);
+		}
+
+		return $result;
+	}
+
+	protected function insertQuery(IComment $comment, bool $tryWritingReferenceId): bool {
 		$qb = $this->dbConn->getQueryBuilder();
-		$affectedRows = $qb
-			->insert('comments')
-			->values([
-				'parent_id' => $qb->createNamedParameter($comment->getParentId()),
-				'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()),
-				'children_count' => $qb->createNamedParameter($comment->getChildrenCount()),
-				'actor_type' => $qb->createNamedParameter($comment->getActorType()),
-				'actor_id' => $qb->createNamedParameter($comment->getActorId()),
-				'message' => $qb->createNamedParameter($comment->getMessage()),
-				'verb' => $qb->createNamedParameter($comment->getVerb()),
-				'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
-				'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
-				'object_type' => $qb->createNamedParameter($comment->getObjectType()),
-				'object_id' => $qb->createNamedParameter($comment->getObjectId()),
-			])
+
+		$values = [
+			'parent_id' => $qb->createNamedParameter($comment->getParentId()),
+			'topmost_parent_id' => $qb->createNamedParameter($comment->getTopmostParentId()),
+			'children_count' => $qb->createNamedParameter($comment->getChildrenCount()),
+			'actor_type' => $qb->createNamedParameter($comment->getActorType()),
+			'actor_id' => $qb->createNamedParameter($comment->getActorId()),
+			'message' => $qb->createNamedParameter($comment->getMessage()),
+			'verb' => $qb->createNamedParameter($comment->getVerb()),
+			'creation_timestamp' => $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'),
+			'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
+			'object_type' => $qb->createNamedParameter($comment->getObjectType()),
+			'object_id' => $qb->createNamedParameter($comment->getObjectId()),
+		];
+
+		if ($tryWritingReferenceId) {
+			$values['reference_id'] = $qb->createNamedParameter($comment->getReferenceId());
+		}
+
+		$affectedRows = $qb->insert('comments')
+			->values($values)
 			->execute();
 
 		if ($affectedRows > 0) {
@@ -785,8 +810,21 @@ class Manager implements ICommentsManager {
 		$this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId()));
 		$this->uncache($comment->getId());
 
+		try {
+			$result = $this->updateQuery($comment, true);
+		} catch (InvalidFieldNameException $e) {
+			// See function insert() for explanation
+			$result = $this->updateQuery($comment, false);
+		}
+
+		$this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment);
+
+		return $result;
+	}
+
+	protected function updateQuery(IComment $comment, bool $tryWritingReferenceId): bool {
 		$qb = $this->dbConn->getQueryBuilder();
-		$affectedRows = $qb
+		$qb
 			->update('comments')
 			->set('parent_id', $qb->createNamedParameter($comment->getParentId()))
 			->set('topmost_parent_id', $qb->createNamedParameter($comment->getTopmostParentId()))
@@ -798,17 +836,19 @@ class Manager implements ICommentsManager {
 			->set('creation_timestamp', $qb->createNamedParameter($comment->getCreationDateTime(), 'datetime'))
 			->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'))
 			->set('object_type', $qb->createNamedParameter($comment->getObjectType()))
-			->set('object_id', $qb->createNamedParameter($comment->getObjectId()))
-			->where($qb->expr()->eq('id', $qb->createParameter('id')))
-			->setParameter('id', $comment->getId())
+			->set('object_id', $qb->createNamedParameter($comment->getObjectId()));
+
+		if ($tryWritingReferenceId) {
+			$qb->set('reference_id', $qb->createNamedParameter($comment->getReferenceId()));
+		}
+
+		$affectedRows = $qb->where($qb->expr()->eq('id', $qb->createNamedParameter($comment->getId())))
 			->execute();
 
 		if ($affectedRows === 0) {
 			throw new NotFoundException('Comment to update does ceased to exist');
 		}
 
-		$this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment);
-
 		return $affectedRows > 0;
 	}
 
diff --git a/lib/private/DB/MissingColumnInformation.php b/lib/private/DB/MissingColumnInformation.php
new file mode 100644
index 0000000000000000000000000000000000000000..bcf585848fd90be2c1bd1a444379714a10a08073
--- /dev/null
+++ b/lib/private/DB/MissingColumnInformation.php
@@ -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;
+	}
+}
diff --git a/lib/public/Comments/IComment.php b/lib/public/Comments/IComment.php
index aac68a4036ba10dbf672102f44438bd22b384ce7..b98a015a30e4954898685a77b20d40da0c2019d1 100644
--- a/lib/public/Comments/IComment.php
+++ b/lib/public/Comments/IComment.php
@@ -263,4 +263,21 @@ interface IComment {
 	 */
 	public function setObject($objectType, $objectId);
 
+	/**
+	 * returns the reference id of the comment
+	 *
+	 * @return string|null
+	 * @since 19.0.0
+	 */
+	public function getReferenceId(): ?string;
+
+	/**
+	 * sets (overwrites) the reference id of the comment
+	 *
+	 * @param string|null $referenceId e.g. sha256 hash sum
+	 * @return IComment
+	 * @since 19.0.0
+	 */
+	public function setReferenceId(?string $referenceId): IComment;
+
 }
diff --git a/lib/public/IDBConnection.php b/lib/public/IDBConnection.php
index 0cc29d3be46505977363344f330712ec8d9e8e08..a7aa9d448f1bf5e8595c33af87c171947477f23a 100644
--- a/lib/public/IDBConnection.php
+++ b/lib/public/IDBConnection.php
@@ -51,6 +51,8 @@ interface IDBConnection {
 
 	const ADD_MISSING_INDEXES_EVENT = self::class . '::ADD_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.