From 1b295222794dd168979b25f09b29ba209c3812e6 Mon Sep 17 00:00:00 2001 From: Yongteng Lei Date: Tue, 24 Mar 2026 20:24:24 +0800 Subject: [PATCH] Fix: migrate_add_unique_email silently skips unique constraint (#13744) ### What problem does this PR solve? Fix migrate_add_unique_email-silently-skips-unique-constraint-when-non-unique-user_email-index-exists. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue) --- api/db/db_models.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/api/db/db_models.py b/api/db/db_models.py index 9ad52d4312..97a05c6cde 100644 --- a/api/db/db_models.py +++ b/api/db/db_models.py @@ -1369,7 +1369,7 @@ def alter_db_rename_column(migrator, table_name, old_column_name, new_column_nam def migrate_add_unique_email(migrator): """Deduplicates user emails and add UNIQUE constraint to email column (idempotent)""" - # step 0: check if UNIQUE index on email already exists + # step 0: check existing index state on user.email and prepare for unique constraint try: if settings.DATABASE_TYPE.upper() == "POSTGRES": cursor = DB.execute_sql(""" @@ -1378,21 +1378,33 @@ def migrate_add_unique_email(migrator): WHERE tablename = 'user' AND indexname = 'user_email' """) + result = cursor.fetchone() + if result and result[0] > 0: + logging.info("UNIQUE index on user.email already exists, skipping migration") + return else: + # Fetch the first index on email: tells us both the name and whether it's unique. + # non_unique=0 means unique, non_unique=1 means non-unique. cursor = DB.execute_sql(""" - SELECT COUNT(*) + SELECT index_name, non_unique FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'user' - AND index_name = 'user_email' - AND non_unique = 0 + AND column_name = 'email' + LIMIT 1 """) - result = cursor.fetchone() - if result and result[0] > 0: - logging.info("UNIQUE index on user.email already exists, skipping migration") - return + row = cursor.fetchone() + if row: + index_name, non_unique = row + if non_unique == 0: + logging.info("UNIQUE index on user.email already exists, skipping migration") + return + # Non-unique index exists (e.g. from old peewee index=True); drop it so + # the upcoming ADD UNIQUE INDEX does not hit MySQL error 1061 "Duplicate key name". + DB.execute_sql(f"ALTER TABLE `user` DROP INDEX `{index_name}`") + logging.info(f"Dropped non-unique index '{index_name}' on user.email before adding unique index") except Exception as ex: - logging.warning("Failed to check if UNIQUE index exists on user.email: %s, continuing with migration", ex) + logging.warning(f"Failed to check/prepare email index on user table: {ex}, continuing with migration") # step 1: rename duplicate rows so the UNIQUE constraint can be applied try: