From 371c4f57d3ab208fc261e159fa86ca1dc80cb334 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 23 Jul 2026 17:59:03 -0700 Subject: [PATCH] Fix: fix broken migration in 3.0.1 (#13242) --- .../0001_1_clamp_mailrule_maximum_age.py | 27 -------------- .../0002_optimize_integer_field_sizes.py | 16 +++++++- src/paperless_mail/tests/test_migrations.py | 37 +++++++++++++++++++ 3 files changed, 52 insertions(+), 28 deletions(-) delete mode 100644 src/paperless_mail/migrations/0001_1_clamp_mailrule_maximum_age.py create mode 100644 src/paperless_mail/tests/test_migrations.py diff --git a/src/paperless_mail/migrations/0001_1_clamp_mailrule_maximum_age.py b/src/paperless_mail/migrations/0001_1_clamp_mailrule_maximum_age.py deleted file mode 100644 index 4b195b746..000000000 --- a/src/paperless_mail/migrations/0001_1_clamp_mailrule_maximum_age.py +++ /dev/null @@ -1,27 +0,0 @@ -# Split out of 0002_optimize_integer_field_sizes.py into its own migration -# (own transaction). Running this UPDATE in the same transaction as the -# later ALTER TABLE on paperless_mail_mailrule can fail on PostgreSQL with -# "cannot ALTER TABLE because it has pending trigger events", since the -# update queues FK trigger events against that table that are still pending -# when the subsequent AlterField tries to rewrite it. Committing the clamp -# here first avoids that. -from django.db import migrations - - -def clamp_mailrule_maximum_age(apps, schema_editor): - # Clamp the maximum_age field of MailRule because of PositiveIntegerField --> PositiveSmallIntegerField - MailRule = apps.get_model("paperless_mail", "MailRule") - MailRule.objects.filter(maximum_age__gt=32767).update(maximum_age=32767) - - -class Migration(migrations.Migration): - dependencies = [ - ("paperless_mail", "0001_squashed"), - ] - - operations = [ - migrations.RunPython( - clamp_mailrule_maximum_age, - migrations.RunPython.noop, - ), - ] diff --git a/src/paperless_mail/migrations/0002_optimize_integer_field_sizes.py b/src/paperless_mail/migrations/0002_optimize_integer_field_sizes.py index f42dafe36..0542d1429 100644 --- a/src/paperless_mail/migrations/0002_optimize_integer_field_sizes.py +++ b/src/paperless_mail/migrations/0002_optimize_integer_field_sizes.py @@ -4,9 +4,19 @@ from django.db import migrations from django.db import models +def clamp_mailrule_maximum_age(apps, schema_editor): + # Clamp the maximum_age field of MailRule because of PositiveIntegerField --> PositiveSmallIntegerField + MailRule = apps.get_model("paperless_mail", "MailRule") + MailRule.objects.filter(maximum_age__gt=32767).update(maximum_age=32767) + + class Migration(migrations.Migration): + # The data update must commit before PostgreSQL rewrites the table, otherwise + # pending foreign-key trigger events can block the subsequent ALTER TABLE. + atomic = False + dependencies = [ - ("paperless_mail", "0001_1_clamp_mailrule_maximum_age"), + ("paperless_mail", "0001_squashed"), ] operations = [ @@ -112,6 +122,10 @@ class Migration(migrations.Migration): verbose_name="consumption scope", ), ), + migrations.RunPython( + clamp_mailrule_maximum_age, + migrations.RunPython.noop, + ), migrations.AlterField( model_name="mailrule", name="maximum_age", diff --git a/src/paperless_mail/tests/test_migrations.py b/src/paperless_mail/tests/test_migrations.py new file mode 100644 index 000000000..a683c89fa --- /dev/null +++ b/src/paperless_mail/tests/test_migrations.py @@ -0,0 +1,37 @@ +import importlib + +from django.db import migrations + + +def test_optimize_integer_fields_preserves_released_migration_history(): + migration_module = importlib.import_module( + "paperless_mail.migrations.0002_optimize_integer_field_sizes", + ) + + assert migration_module.Migration.dependencies == [ + ("paperless_mail", "0001_squashed"), + ] + + +def test_mailrule_maximum_age_is_clamped_before_non_atomic_alter(): + migration_module = importlib.import_module( + "paperless_mail.migrations.0002_optimize_integer_field_sizes", + ) + migration = migration_module.Migration + + clamp_index = next( + index + for index, operation in enumerate(migration.operations) + if isinstance(operation, migrations.RunPython) + and operation.code is migration_module.clamp_mailrule_maximum_age + ) + alter_index = next( + index + for index, operation in enumerate(migration.operations) + if isinstance(operation, migrations.AlterField) + and operation.model_name == "mailrule" + and operation.name == "maximum_age" + ) + + assert migration.atomic is False + assert clamp_index < alter_index