Skip to content

Conversation

@DerDreschner
Copy link
Contributor

@DerDreschner DerDreschner commented Jan 8, 2026

The mail_accounts table includes several fields with references to the mail_mailboxes table. This relation wasn't declared via foreign keys previously, which means that mailboxes with a special meaning (e.g., Junk) can be deleted without removing them from the corresponding *_mailbox_id field. Inconsistent entries are being set to NULL before changing the database schema.

Testing
MariaDB [nextcloud]> SELECT drafts_mailbox_id, sent_mailbox_id, trash_mailbox_id,junk_mailbox_id,archive_mailbox_id,snooze_mailbox_id FROM oc_mail_accounts;
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
| drafts_mailbox_id | sent_mailbox_id | trash_mailbox_id | junk_mailbox_id | archive_mailbox_id | snooze_mailbox_id |
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
|                47 |              48 |               49 |              46 |                 45 |              NULL |
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
1 row in set (0.000 sec)

MariaDB [nextcloud]> DELETE FROM oc_mail_mailboxes WHERE id=46;
Query OK, 1 row affected (0.015 sec)

MariaDB [nextcloud]> DELETE FROM oc_mail_mailboxes WHERE id=48;
Query OK, 1 row affected (0.007 sec)

MariaDB [nextcloud]> SELECT drafts_mailbox_id, sent_mailbox_id, trash_mailbox_id,junk_mailbox_id,archive_mailbox_id,snooze_mailbox_id FROM oc_mail_accounts;
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
| drafts_mailbox_id | sent_mailbox_id | trash_mailbox_id | junk_mailbox_id | archive_mailbox_id | snooze_mailbox_id |
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
|                47 |              48 |               49 |              46 |                 45 |              NULL |
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
1 row in set (0.000 sec)

### MIGRATION RUN ###

MariaDB [nextcloud]> SELECT drafts_mailbox_id, sent_mailbox_id, trash_mailbox_id,junk_mailbox_id,archive_mailbox_id,snooze_mailbox_id FROM oc_mail_accounts;
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
| drafts_mailbox_id | sent_mailbox_id | trash_mailbox_id | junk_mailbox_id | archive_mailbox_id | snooze_mailbox_id |
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
|                47 |            NULL |               49 |            NULL |                 45 |              NULL |
+-------------------+-----------------+------------------+-----------------+--------------------+-------------------+
1 row in set (0.000 sec)

MariaDB [nextcloud]> SHOW CREATE TABLE oc_mail_accounts;

[...]

  PRIMARY KEY (`id`),
  KEY `mail_userid_index` (`user_id`),
  KEY `IDX_61A7E87F59A77CDC` (`drafts_mailbox_id`),
  KEY `IDX_61A7E87F75F54A4B` (`sent_mailbox_id`),
  KEY `IDX_61A7E87F1FA4CF4D` (`trash_mailbox_id`),
  KEY `IDX_61A7E87F5FDB8C8A` (`junk_mailbox_id`),
  KEY `IDX_61A7E87FA47FE329` (`archive_mailbox_id`),
  KEY `IDX_61A7E87F7587D725` (`snooze_mailbox_id`),
  CONSTRAINT `FK_61A7E87F1FA4CF4D` FOREIGN KEY (`trash_mailbox_id`) REFERENCES `oc_mail_mailboxes` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_61A7E87F59A77CDC` FOREIGN KEY (`drafts_mailbox_id`) REFERENCES `oc_mail_mailboxes` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_61A7E87F5FDB8C8A` FOREIGN KEY (`junk_mailbox_id`) REFERENCES `oc_mail_mailboxes` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_61A7E87F7587D725` FOREIGN KEY (`snooze_mailbox_id`) REFERENCES `oc_mail_mailboxes` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_61A7E87F75F54A4B` FOREIGN KEY (`sent_mailbox_id`) REFERENCES `oc_mail_mailboxes` (`id`) ON DELETE SET NULL,
  CONSTRAINT `FK_61A7E87FA47FE329` FOREIGN KEY (`archive_mailbox_id`) REFERENCES `oc_mail_mailboxes` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+------------------
+
1 row in set (0.000 sec)

MariaDB [nextcloud]> 

This fixes #11243.

@DerDreschner DerDreschner force-pushed the fix/add-foreign-key-to-mailbox-ids branch from 6162415 to 949f5cd Compare January 8, 2026 17:45
@DerDreschner DerDreschner force-pushed the fix/add-foreign-key-to-mailbox-ids branch from 949f5cd to 7ee1b92 Compare January 8, 2026 18:15
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
$schema = $schemaClosure();

if ($schema->hasTable('mail_accounts')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What's the best-practice with the hasTable() / hasColumn() / etc. checks? Is it even necessary here? I've done it because it's being done in older migrations as well, but I'm not sure why it's done there 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checks shouldn't be necessary ideally, but in case of a migration error it should be possible to re-run that migration.

Imagine a migration creates two tables: t1 and t2. Creating t2 failed (something temporary that the admin could resolve, or a patch is applied). The upgrade runs the same migration again, because the migration is not yet written to oc_migrations.
If the migration tries to create t1 no matter if it exists, it will now fail here. If the migration has a check it can skip creating t1 and creates t2.

This is why we add the table/column exists checks :)

@DerDreschner DerDreschner force-pushed the fix/add-foreign-key-to-mailbox-ids branch from 7ee1b92 to d6e5430 Compare January 9, 2026 12:46
@DerDreschner DerDreschner requested a review from GretaD as a code owner January 9, 2026 12:46
@ChristophWurst ChristophWurst merged commit 3bc285a into nextcloud:main Jan 9, 2026
43 of 46 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dangling draft/junk/sent mailbox ID after mailbox deletion

2 participants