From c2a8eac65fb3e1f807b889ff2c96334b2f4ca99e Mon Sep 17 00:00:00 2001 From: Constantin Graf Date: Thu, 21 May 2026 23:22:27 +0200 Subject: [PATCH] Add migration to lower case the user emails --- ...026_05_21_000002_lowercase_user_emails.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 database/migrations/2026_05_21_000002_lowercase_user_emails.php diff --git a/database/migrations/2026_05_21_000002_lowercase_user_emails.php b/database/migrations/2026_05_21_000002_lowercase_user_emails.php new file mode 100644 index 00000000..15f75848 --- /dev/null +++ b/database/migrations/2026_05_21_000002_lowercase_user_emails.php @@ -0,0 +1,64 @@ +selectRaw('LOWER(email) as normalized_email') + ->selectRaw('COUNT(*) as user_count') + ->selectRaw("STRING_AGG(id::text || ' <' || email || '>', ', ' ORDER BY email) as users") + ->where('is_placeholder', false) + ->groupByRaw('LOWER(email)') + ->havingRaw('COUNT(*) > 1') + ->orderBy('normalized_email') + ->get(); + + if ($duplicateEmails->isNotEmpty()) { + $duplicateEmailMessage = $duplicateEmails + ->take(20) + ->map(fn (\stdClass $duplicateEmail): string => sprintf( + '%s (%d users: %s)', + $duplicateEmail->normalized_email, + $duplicateEmail->user_count, + $duplicateEmail->users, + )) + ->implode('; '); + + $remainingDuplicateCount = $duplicateEmails->count() - 20; + $remainingDuplicateMessage = $remainingDuplicateCount > 0 + ? sprintf('; and %d more duplicate normalized emails', $remainingDuplicateCount) + : ''; + + throw new RuntimeException( + 'Cannot lowercase users.email because doing so would create duplicate non-placeholder user emails and violate the unique index on users.email for non-placeholder users. Resolve these case-insensitive duplicates first: '. + $duplicateEmailMessage. + $remainingDuplicateMessage + ); + } + + DB::table('users') + ->whereRaw('email <> LOWER(email)') + ->update([ + 'email' => DB::raw('LOWER(email)'), + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +};