mirror of
https://github.com/nextcloud/mail.git
synced 2026-01-13 20:23:59 +00:00
Merge pull request #12220 from DerDreschner/fix/add-foreign-key-to-mailbox-ids
fix(database): Add foreign keys to `*_mailbox_id` fields in `mail_accounts` table
This commit is contained in:
137
lib/Migration/Version5007Date20260108124422.php
Normal file
137
lib/Migration/Version5007Date20260108124422.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Mail\Migration;
|
||||
|
||||
use Closure;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use OCP\DB\Exception;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Migration\Attributes\ModifyColumn;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use Override;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
#[ModifyColumn(
|
||||
table: 'mail_accounts',
|
||||
description: 'Remove invalid mailbox_id from *_mailbox_id columns and add foreign keys to ensure consistency')
|
||||
]
|
||||
class Version5007Date20260108124422 extends SimpleMigrationStep {
|
||||
public function __construct(
|
||||
private IDBConnection $db,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mailboxType
|
||||
*/
|
||||
private function removeInconsistentMailboxEntries(string $mailboxType): void {
|
||||
$selectQueryBuilder = $this->db->getQueryBuilder();
|
||||
|
||||
$selectQueryBuilder->select('accounts.id')
|
||||
->from('mail_accounts', 'accounts')
|
||||
->leftJoin(
|
||||
'accounts',
|
||||
'mail_mailboxes',
|
||||
'mailboxes',
|
||||
$selectQueryBuilder->expr()->eq(
|
||||
"accounts.{$mailboxType}_mailbox_id",
|
||||
'mailboxes.id',
|
||||
IQueryBuilder::PARAM_INT
|
||||
)
|
||||
)
|
||||
->where($selectQueryBuilder->expr()->isNull('mailboxes.id'))
|
||||
->andWhere($selectQueryBuilder->expr()->isNotNull("accounts.{$mailboxType}_mailbox_id"));
|
||||
|
||||
try {
|
||||
$affectedRows = $selectQueryBuilder->executeQuery()->fetchAll();
|
||||
|
||||
foreach ($affectedRows as $row) {
|
||||
$updateQueryBuilder = $this->db->getQueryBuilder();
|
||||
$updateQueryBuilder->update('mail_accounts')
|
||||
->set(
|
||||
"{$mailboxType}_mailbox_id",
|
||||
$updateQueryBuilder->createNamedParameter(null, IQueryBuilder::PARAM_NULL)
|
||||
)
|
||||
->where(
|
||||
$updateQueryBuilder->expr()->eq(
|
||||
'id',
|
||||
$updateQueryBuilder->createNamedParameter($row['id'], IQueryBuilder::PARAM_INT)
|
||||
)
|
||||
);
|
||||
$updateQueryBuilder->executeStatement();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error("Emptying inconsistent {$mailboxType}_mailbox_id field failed", [
|
||||
'exception' => $e
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Table $accountsTable
|
||||
* @param Table $mailboxesTable
|
||||
* @param string $mailboxType
|
||||
* @return void
|
||||
*/
|
||||
private function addMailboxKey(Table $accountsTable, Table $mailboxesTable, string $mailboxType): void {
|
||||
$accountsTable->addForeignKeyConstraint(
|
||||
$mailboxesTable,
|
||||
["{$mailboxType}_mailbox_id"],
|
||||
['id'],
|
||||
[
|
||||
'onDelete' => 'SET NULL',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure
|
||||
* @param array $options
|
||||
*/
|
||||
#[Override]
|
||||
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
|
||||
$this->removeInconsistentMailboxEntries('drafts');
|
||||
$this->removeInconsistentMailboxEntries('sent');
|
||||
$this->removeInconsistentMailboxEntries('trash');
|
||||
$this->removeInconsistentMailboxEntries('junk');
|
||||
$this->removeInconsistentMailboxEntries('archive');
|
||||
$this->removeInconsistentMailboxEntries('snooze');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param Closure(): ISchemaWrapper $schemaClosure
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
*/
|
||||
#[Override]
|
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if ($schema->hasTable('mail_accounts')) {
|
||||
$accountsTable = $schema->getTable('mail_accounts');
|
||||
$mailboxesTable = $schema->getTable('mail_mailboxes');
|
||||
|
||||
$this->addMailboxKey($accountsTable, $mailboxesTable, 'drafts');
|
||||
$this->addMailboxKey($accountsTable, $mailboxesTable, 'sent');
|
||||
$this->addMailboxKey($accountsTable, $mailboxesTable, 'trash');
|
||||
$this->addMailboxKey($accountsTable, $mailboxesTable, 'junk');
|
||||
$this->addMailboxKey($accountsTable, $mailboxesTable, 'archive');
|
||||
$this->addMailboxKey($accountsTable, $mailboxesTable, 'snooze');
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
@ -35,6 +35,7 @@
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\MySQLPlatform" />
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\PostgreSQL94Platform" />
|
||||
<referencedClass name="Doctrine\DBAL\Platforms\SqlitePlatform" />
|
||||
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
||||
<referencedClass name="Doctrine\DBAL\Types\Type" />
|
||||
<referencedClass name="Doctrine\DBAL\Types\Types" />
|
||||
<referencedClass name="IPLib\Factory" />
|
||||
|
||||
Reference in New Issue
Block a user