Browse Source

Fixed #34058 -- Changed sequence types when altering pre-Django 4.1 auto fields on PostgreSQL.

Thanks Anders Kaseorg for the report.

Thanks Florian Apolloner for pair programming.

Regression in 2eea361eff58dd98c409c5227064b901f41bd0d6.
Mariusz Felisiak 2 years ago
parent
commit
19e6efa50b
3 changed files with 63 additions and 1 deletions
  1. 24 1
      django/db/backends/postgresql/schema.py
  2. 4 0
      docs/releases/4.1.2.txt
  3. 35 0
      tests/schema/tests.py

+ 24 - 1
django/db/backends/postgresql/schema.py

@@ -13,7 +13,7 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
         "UPDATE %(table)s SET %(column)s = %(default)s WHERE %(column)s IS NULL"
         "; SET CONSTRAINTS ALL IMMEDIATE"
     )
-
+    sql_alter_sequence_type = "ALTER SEQUENCE IF EXISTS %(sequence)s AS %(type)s"
     sql_delete_sequence = "DROP SEQUENCE IF EXISTS %(sequence)s CASCADE"
 
     sql_create_index = (
@@ -208,6 +208,29 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
                     [],
                 ),
             ]
+        elif new_is_auto and old_is_auto and old_internal_type != new_internal_type:
+            fragment, _ = super()._alter_column_type_sql(
+                model, old_field, new_field, new_type
+            )
+            column = strip_quotes(new_field.column)
+            sequence_name = f"{table}_{column}_seq"
+            db_types = {
+                "AutoField": "integer",
+                "BigAutoField": "bigint",
+                "SmallAutoField": "smallint",
+            }
+            return fragment, [
+                # Alter the sequence type if exists (Django 4.1+ identity
+                # columns don't have it).
+                (
+                    self.sql_alter_sequence_type
+                    % {
+                        "sequence": self.quote_name(sequence_name),
+                        "type": db_types[new_internal_type],
+                    },
+                    [],
+                ),
+            ]
         else:
             return super()._alter_column_type_sql(model, old_field, new_field, new_type)
 

+ 4 - 0
docs/releases/4.1.2.txt

@@ -35,3 +35,7 @@ Bugfixes
 * Fixed a regression in Django 4.1 where the ``--debug-mode`` argument to
   ``test`` did not work when running parallel tests with the
   ``multiprocessing`` start method ``spawn`` (:ticket:`34010`).
+
+* Fixed a regression in Django 4.1 that didn't alter a sequence type when
+  altering type of pre-Django 4.1 serial columns on PostgreSQL
+  (:ticket:`34058`).

+ 35 - 0
tests/schema/tests.py

@@ -1873,6 +1873,41 @@ class SchemaTests(TransactionTestCase):
         with connection.schema_editor() as editor:
             editor.alter_field(SmallIntegerPK, old_field, new_field, strict=True)
 
+    @isolate_apps("schema")
+    @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
+    def test_alter_serial_auto_field_to_bigautofield(self):
+        class SerialAutoField(Model):
+            id = SmallAutoField(primary_key=True)
+
+            class Meta:
+                app_label = "schema"
+
+        table = SerialAutoField._meta.db_table
+        column = SerialAutoField._meta.get_field("id").column
+        with connection.cursor() as cursor:
+            cursor.execute(
+                f'CREATE TABLE "{table}" '
+                f'("{column}" smallserial NOT NULL PRIMARY KEY)'
+            )
+        try:
+            old_field = SerialAutoField._meta.get_field("id")
+            new_field = BigAutoField(primary_key=True)
+            new_field.model = SerialAutoField
+            new_field.set_attributes_from_name("id")
+            with connection.schema_editor() as editor:
+                editor.alter_field(SerialAutoField, old_field, new_field, strict=True)
+            with connection.cursor() as cursor:
+                cursor.execute(
+                    "SELECT data_type FROM pg_sequences WHERE sequencename = %s",
+                    [f"{table}_{column}_seq"],
+                )
+                row = cursor.fetchone()
+                sequence_data_type = row[0] if row and row[0] else None
+                self.assertEqual(sequence_data_type, "bigint")
+        finally:
+            with connection.cursor() as cursor:
+                cursor.execute(f'DROP TABLE "{table}"')
+
     def test_alter_int_pk_to_int_unique(self):
         """
         Should be able to rename an IntegerField(primary_key=True) to