|
| 1 | +""" |
| 2 | +Widen existing integer custom-object columns from 32-bit to 64-bit (bigint). |
| 3 | +
|
| 4 | +Integer fields previously mapped to Django's IntegerField, i.e. a 32-bit signed |
| 5 | +PostgreSQL ``integer`` column (max 2_147_483_647). They now map to BigIntegerField |
| 6 | +(``bigint``). New columns are created as bigint automatically by the schema editor, |
| 7 | +but columns on already-created custom_objects_* tables must be widened in place. |
| 8 | +
|
| 9 | +``integer -> bigint`` is a lossless widening (every int32 value fits in int64), so |
| 10 | +no USING clause or data transformation is needed. The conversion rewrites the table |
| 11 | +under an ACCESS EXCLUSIVE lock; this is fine for typical custom-object table sizes. |
| 12 | +
|
| 13 | +The reverse is intentionally a no-op: narrowing bigint back to integer could fail or |
| 14 | +lose data for any value outside the 32-bit range. |
| 15 | +
|
| 16 | +See issue #532. |
| 17 | +""" |
| 18 | + |
| 19 | +from django.db import migrations |
| 20 | + |
| 21 | + |
| 22 | +def widen_integer_columns(apps, schema_editor): |
| 23 | + """ALTER every integer-typed custom-object column to bigint, in place.""" |
| 24 | + CustomObjectTypeField = apps.get_model("netbox_custom_objects", "CustomObjectTypeField") |
| 25 | + |
| 26 | + # Drive off field metadata (not blind introspection of every custom_objects_* |
| 27 | + # column) so we only touch user integer fields, never base-model columns |
| 28 | + # inherited from NetBox mixins. After migration 0014 all field names are |
| 29 | + # lowercase and the scalar column name equals the field name exactly. |
| 30 | + integer_fields = CustomObjectTypeField.objects.filter(type="integer") |
| 31 | + |
| 32 | + with schema_editor.connection.cursor() as cursor: |
| 33 | + for field in integer_fields: |
| 34 | + table_name = f"custom_objects_{field.custom_object_type_id}" |
| 35 | + column_name = field.name |
| 36 | + |
| 37 | + # Idempotent + safe: only act on a column that exists and is still a |
| 38 | + # 32-bit integer. A no-op on fresh installs (already bigint) and on |
| 39 | + # partial re-runs. |
| 40 | + cursor.execute( |
| 41 | + """ |
| 42 | + SELECT 1 FROM information_schema.columns |
| 43 | + WHERE table_schema = current_schema() |
| 44 | + AND table_name = %s |
| 45 | + AND column_name = %s |
| 46 | + AND data_type = 'integer' |
| 47 | + """, |
| 48 | + [table_name, column_name], |
| 49 | + ) |
| 50 | + if cursor.fetchone(): |
| 51 | + cursor.execute( |
| 52 | + f'ALTER TABLE "{table_name}" ALTER COLUMN "{column_name}" TYPE bigint' |
| 53 | + ) |
| 54 | + |
| 55 | + |
| 56 | +class Migration(migrations.Migration): |
| 57 | + |
| 58 | + dependencies = [ |
| 59 | + ("netbox_custom_objects", "0014_fix_mixed_case_field_names"), |
| 60 | + ] |
| 61 | + |
| 62 | + operations = [ |
| 63 | + migrations.RunPython(widen_integer_columns, migrations.RunPython.noop), |
| 64 | + ] |
0 commit comments