# Generated by Django 1.11.23 on 2019-08-21 21:43 import time from django.db import connection, migrations from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django.db.migrations.state import StateApps from django.db.models import Min from psycopg2.sql import SQL BATCH_SIZE = 10000 def sql_copy_id_to_bigint_id(id_range_lower_bound: int, id_range_upper_bound: int) -> None: query = SQL( """ UPDATE zerver_usermessage SET bigint_id = id WHERE id BETWEEN %(lower_bound)s AND %(upper_bound)s """ ) with connection.cursor() as cursor: cursor.execute( query, { "lower_bound": id_range_lower_bound, "upper_bound": id_range_upper_bound, }, ) def copy_id_to_bigid(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor) -> None: UserMessage = apps.get_model("zerver", "UserMessage") if not UserMessage.objects.exists(): # Nothing to do return # TODO: is the below lookup fast enough, considering there's no index on bigint_id? first_uncopied_id = UserMessage.objects.filter(bigint_id__isnull=True).aggregate(Min("id"))[ "id__min" ] # Note: the below id can fall in a segment # where bigint_id = id already, but it's not a big problem # this will just do some redundant UPDATEs. last_id = UserMessage.objects.latest("id").id id_range_lower_bound = first_uncopied_id id_range_upper_bound = first_uncopied_id + BATCH_SIZE while id_range_upper_bound <= last_id: sql_copy_id_to_bigint_id(id_range_lower_bound, id_range_upper_bound) id_range_lower_bound = id_range_upper_bound + 1 id_range_upper_bound = id_range_lower_bound + BATCH_SIZE time.sleep(0.1) if last_id > id_range_lower_bound: # Copy for the last batch. sql_copy_id_to_bigint_id(id_range_lower_bound, last_id) class Migration(migrations.Migration): atomic = False dependencies = [ ("zerver", "0238_usermessage_bigint_id"), ] operations = [ migrations.RunSQL( """ CREATE FUNCTION zerver_usermessage_bigint_id_to_id_trigger_function() RETURNS trigger AS $$ BEGIN NEW.bigint_id = NEW.id; RETURN NEW; END $$ LANGUAGE 'plpgsql'; CREATE TRIGGER zerver_usermessage_bigint_id_to_id_trigger BEFORE INSERT ON zerver_usermessage FOR EACH ROW EXECUTE PROCEDURE zerver_usermessage_bigint_id_to_id_trigger_function(); """ ), migrations.RunPython(copy_id_to_bigid, elidable=True), migrations.RunSQL( """ CREATE UNIQUE INDEX CONCURRENTLY zerver_usermessage_bigint_id_idx ON zerver_usermessage (bigint_id); """ ), ]