diff --git a/posthog/api/test/__snapshots__/test_decide.ambr b/posthog/api/test/__snapshots__/test_decide.ambr index 5caca4e947152..a3d7eafe4ea90 100644 --- a/posthog/api/test/__snapshots__/test_decide.ambr +++ b/posthog/api/test/__snapshots__/test_decide.ambr @@ -3890,6 +3890,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id", "posthog_team"."id", @@ -4260,6 +4261,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -4693,6 +4695,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -4986,6 +4989,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -5171,6 +5175,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id", "posthog_team"."id", @@ -5602,6 +5607,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -5834,6 +5840,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -6165,6 +6172,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -6344,6 +6352,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -6475,6 +6484,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id", "posthog_team"."id", @@ -6906,6 +6916,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -7138,6 +7149,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -7465,6 +7477,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" @@ -7640,6 +7653,7 @@ "posthog_hogfunction"."inputs", "posthog_hogfunction"."encrypted_inputs", "posthog_hogfunction"."filters", + "posthog_hogfunction"."mappings", "posthog_hogfunction"."masking", "posthog_hogfunction"."template_id" FROM "posthog_hogfunction" diff --git a/posthog/management/commands/delete_persons.py b/posthog/management/commands/delete_persons.py index e03b31b55b699..da4177224ee55 100644 --- a/posthog/management/commands/delete_persons.py +++ b/posthog/management/commands/delete_persons.py @@ -1,9 +1,9 @@ import logging +from django.db import connection import structlog from django.core.management.base import BaseCommand -from posthog.models.person.person import Person logger = structlog.get_logger(__name__) logger.setLevel(logging.INFO) @@ -40,27 +40,54 @@ def run(options, sync: bool = False): logger.info(f"-> Team ID: {team_id}") if person_ids: logger.info(f"-> Person IDs: {person_ids}") - logger.info(f"-> Limit: {limit}") - - list_query = Person.objects.filter(team_id=team_id) - - if person_ids: - list_query = list_query.filter(id__in=person_ids) - - list_query = list_query.order_by("id")[:limit] + logger.info(f"-> Limit: {limit} ") + + select_query = f""" + SELECT id + FROM posthog_person + WHERE team_id=%(team_id)s {f"AND id IN ({person_ids})" if person_ids else ""} + ORDER BY id ASC + LIMIT %(limit)s + """ + + delete_query_person_distinct_ids = f""" + WITH to_delete AS ({select_query}) + DELETE FROM posthog_persondistinctid + WHERE team_id = %(team_id)s AND person_id IN (SELECT id FROM to_delete); + """ + + delete_query_person_override = f""" + WITH to_delete AS ({select_query}) + DELETE FROM posthog_personoverride + WHERE team_id = %(team_id)s AND (old_person_id IN (SELECT id FROM to_delete) OR override_person_id IN (SELECT id FROM to_delete)); + """ + + delete_query_person = f""" + WITH to_delete AS ({select_query}) + DELETE FROM posthog_person + WHERE team_id = %(team_id)s AND id IN (SELECT id FROM to_delete); + """ + + with connection.cursor() as cursor: + prepared_person_distinct_ids_query = cursor.mogrify( + delete_query_person_distinct_ids, {"team_id": team_id, "limit": limit, "person_ids": person_ids} + ) + prepared_person_override_query = cursor.mogrify( + delete_query_person_override, {"team_id": team_id, "limit": limit, "person_ids": person_ids} + ) + prepared_person_query = cursor.mogrify( + delete_query_person, {"team_id": team_id, "limit": limit, "person_ids": person_ids} + ) - num_to_delete = list_query.count() + logger.info(f"Delete query to run:") + logger.info(prepared_person_distinct_ids_query) + logger.info(prepared_person_override_query) + logger.info(prepared_person_query) if not live_run: - logger.info(f"Dry run. Would have deleted {num_to_delete} people.") - logger.info("Set --live-run to actually delete.") - return exit(0) - - if num_to_delete == 0: - logger.info("No people to delete") + logger.info(f"Dry run. Set --live-run to actually delete.") return exit(0) - logger.info(f"Will run the deletion for {num_to_delete} people.") confirm = input("Type 'delete' to confirm: ") if confirm != "delete": @@ -70,6 +97,12 @@ def run(options, sync: bool = False): logger.info(f"Executing delete query...") # distinct_ids are deleted by cascade - Person.objects.filter(team_id=team_id, id__in=list_query.values_list("id", flat=True)).delete() + with connection.cursor() as cursor: + cursor.execute(delete_query_person_distinct_ids, {"team_id": team_id, "limit": limit, "person_ids": person_ids}) + logger.info(f"Deleted {cursor.rowcount} distinct_ids") + cursor.execute(delete_query_person_override, {"team_id": team_id, "limit": limit, "person_ids": person_ids}) + logger.info(f"Deleted {cursor.rowcount} person overrides") + cursor.execute(delete_query_person, {"team_id": team_id, "limit": limit, "person_ids": person_ids}) + logger.info(f"Deleted {cursor.rowcount} persons") logger.info("Done")