f7cloud_client/apps/mail/lib/Migration/Version1130Date20220412111833.php
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

204 lines
7.4 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2022 F7cloud GmbH and F7cloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Mail\Migration;
use Closure;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Types\Type;
use OCP\DB\ISchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\Types;
use OCP\IDBConnection;
use OCP\ITempManager;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
use Psr\Log\LoggerInterface;
class Version1130Date20220412111833 extends SimpleMigrationStep {
private IDBConnection $connection;
private LoggerInterface $logger;
private array $recipients = [];
private string $backupPath;
public function __construct(IDBConnection $connection, LoggerInterface $logger, ITempManager $tempManager) {
$this->connection = $connection;
$this->logger = $logger;
$tempBaseDir = $tempManager->getTempBaseDir();
$this->backupPath = tempnam($tempBaseDir, 'mail_recipients_backup');
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
#[\Override]
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
// Keep recipients backup
$qb1 = $this->connection->getQueryBuilder();
$qb1->select('local_message_id', 'message_id', 'type', 'label', 'email')
->from('mail_recipients')
->where($qb1->expr()->isNotNull('local_message_id'));
$result = $qb1->executeQuery();
$this->recipients = $result->fetchAll();
$result->closeCursor();
$backupFile = fopen($this->backupPath, 'rb+');
if (is_resource($backupFile)) {
$this->logger->warning(
'Migration Version1130Date20220412111833 is going to truncate the mail_recipients table. '
. 'We made a backup of the outbox recipients and try to restore them later. When the migration fails restore the data manually. '
. 'Path to backup file: ' . $this->backupPath
);
fputcsv($backupFile, ['local_message_id', 'message_id', 'type', 'label', 'email']);
foreach ($this->recipients as $recipient) {
fputcsv($backupFile, array_values($recipient));
}
fclose($backupFile);
}
// Truncate recipients table
$sql1 = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*mail_recipients`', false);
$this->connection->executeStatement($sql1);
// Truncate and change primary key type for messages table
$sql2 = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*mail_messages`', false);
$this->connection->executeStatement($sql2);
// Truncate message_tags table
$sql3 = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*mail_message_tags`', false);
$this->connection->executeStatement($sql3);
// unset all locks for the mailboxes table
$qb2 = $this->connection->getQueryBuilder();
$qb2->update('mail_mailboxes')
->set('sync_new_lock', $qb2->createNamedParameter(null, IQueryBuilder::PARAM_NULL))
->set('sync_changed_lock', $qb2->createNamedParameter(null, IQueryBuilder::PARAM_NULL))
->set('sync_vanished_lock', $qb2->createNamedParameter(null, IQueryBuilder::PARAM_NULL))
->set('sync_new_token', $qb2->createNamedParameter(null, IQueryBuilder::PARAM_NULL))
->set('sync_changed_token', $qb2->createNamedParameter(null, IQueryBuilder::PARAM_NULL))
->set('sync_vanished_token', $qb2->createNamedParameter(null, IQueryBuilder::PARAM_NULL));
$qb2->executeStatement();
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
*/
#[\Override]
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
// bigint and primary key with autoincrement is not possible on sqlite: https://github.com/f7cloud/server/commit/f57e334f8395e3b5c046b6d28480d798453e4866
$isSqlite = $this->connection->getDatabasePlatform() instanceof SqlitePlatform;
// Remove old unnamed attachments FK
$attachmentsTable = $schema->getTable('mail_attachments');
$fks = $attachmentsTable->getForeignKeys();
foreach ($fks as $fk) {
$attachmentsTable->removeForeignKey($fk->getName());
}
$recipientsTable = $schema->getTable('mail_recipients');
$fks = $recipientsTable->getForeignKeys();
foreach ($fks as $fk) {
$recipientsTable->removeForeignKey($fk->getName());
}
if (!$isSqlite) {
// Change primary column to bigint
$recipientsTable->modifyColumn('id', [
'type' => Type::getType(Types::BIGINT),
]);
}
// Add new named FKs to attachments and recipients
$recipientsTable->addForeignKeyConstraint($schema->getTable('mail_local_messages'), ['local_message_id'], ['id'], ['onDelete' => 'CASCADE'], 'recipient_local_message');
$attachmentsTable->addForeignKeyConstraint($schema->getTable('mail_local_messages'), ['local_message_id'], ['id'], ['onDelete' => 'CASCADE'], 'attachment_local_message');
$messagesTable = $schema->getTable('mail_messages');
if (!$isSqlite) {
// Change primary column to bigint
$messagesTable->modifyColumn('id', [
'type' => Type::getType(Types::BIGINT),
]);
}
// Since we have instances where these indices were set manually, remove them first if they exist
$indices = $messagesTable->getIndexes();
foreach ($indices as $index) {
if (!$index->isPrimary()) {
$messagesTable->dropIndex($index->getName());
}
}
// Add named indices
$messagesTable->addIndex(['mailbox_id', 'flag_important', 'flag_deleted', 'flag_seen'], 'mail_messages_id_flags');
$messagesTable->addIndex(['mailbox_id', 'flag_deleted', 'flag_flagged'], 'mail_messages_id_flags2');
// Dropped in Version3600Date20240205180726 because mail_messages_mailbox_id is redundant with mail_messages_mb_id_uid
// $messagesTable->addIndex(['mailbox_id'], 'mail_messages_mailbox_id');
// mail_messages_msgid_idx was added later and may not exist until optional indices are created
$messagesTable->addIndex(
['message_id'],
'mail_messages_msgid_idx',
[],
['lengths' => [128]],
);
// Postgres doesn't allow for shortened indices, so let's skip the last index.
if ($this->connection->getDatabasePlatform() instanceof PostgreSQL94Platform) {
return $schema;
}
$messagesTable->addIndex(['mailbox_id', 'thread_root_id', 'sent_at'], 'mail_msg_thrd_root_snt_idx', [], ['lengths' => [null, 64, null]]);
return $schema;
}
/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*
* @return void
*/
#[\Override]
public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options) {
$qb1 = $this->connection->getQueryBuilder();
$qb1->insert('mail_recipients')
->values([
'local_message_id' => $qb1->createParameter('local_message_id'),
'message_id' => $qb1->createParameter('message_id'),
'type' => $qb1->createParameter('type'),
'label' => $qb1->createParameter('label'),
'email' => $qb1->createParameter('email'),
]);
foreach ($this->recipients as $recipient) {
$qb1->setParameters($recipient);
$qb1->executeStatement();
}
if (is_file($this->backupPath)) {
$this->logger->warning('Migration Version1130Date20220412111833 completed. Going to delete backup file: ' . $this->backupPath);
@unlink($this->backupPath);
}
}
}