diff --git a/crates/storage-pg/.sqlx/query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json b/crates/storage-pg/.sqlx/query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json
similarity index 64%
rename from crates/storage-pg/.sqlx/query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json
rename to crates/storage-pg/.sqlx/query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json
index f415823cc..9567c1555 100644
--- a/crates/storage-pg/.sqlx/query-94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443.json
+++ b/crates/storage-pg/.sqlx/query-7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE username = $1\n ) AS \"exists!\"\n ",
+ "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE LOWER(username) = LOWER($1)\n ) AS \"exists!\"\n ",
"describe": {
"columns": [
{
@@ -18,5 +18,5 @@
null
]
},
- "hash": "94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443"
+ "hash": "7f8335cc94347bc3a15afe7051658659347a1bf71dd62335df046708f19c967e"
}
diff --git a/crates/storage-pg/.sqlx/query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json b/crates/storage-pg/.sqlx/query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json
similarity index 88%
rename from crates/storage-pg/.sqlx/query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json
rename to crates/storage-pg/.sqlx/query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json
index 52c7ab0bc..171a83623 100644
--- a/crates/storage-pg/.sqlx/query-48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455.json
+++ b/crates/storage-pg/.sqlx/query-d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "\n SELECT user_id\n , username\n , created_at\n , locked_at\n , deactivated_at\n , can_request_admin\n FROM users\n WHERE username = $1\n ",
+ "query": "\n SELECT user_id\n , username\n , created_at\n , locked_at\n , deactivated_at\n , can_request_admin\n FROM users\n WHERE LOWER(username) = LOWER($1)\n ",
"describe": {
"columns": [
{
@@ -48,5 +48,5 @@
false
]
},
- "hash": "48213d718a256a12540c0aec595ca3e436be423f2d0c868700c6397745ed0455"
+ "hash": "d2a4f5c01603463b78198529d295f7f121769ea5730d01c20c0ddbcdc79a5716"
}
diff --git a/crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql b/crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql
new file mode 100644
index 000000000..625e07e1b
--- /dev/null
+++ b/crates/storage-pg/migrations/20250410121612_users_lower_username_idx.sql
@@ -0,0 +1,10 @@
+-- no-transaction
+-- Copyright 2025 New Vector Ltd.
+--
+-- SPDX-License-Identifier: AGPL-3.0-only
+-- Please see LICENSE in the repository root for full details.
+
+-- Create an index on the username column, lower-cased, so that we can lookup
+-- usernames in a case-insensitive manner.
+CREATE INDEX CONCURRENTLY users_lower_username_idx
+ ON users (LOWER(username));
diff --git a/crates/storage-pg/src/user/mod.rs b/crates/storage-pg/src/user/mod.rs
index 2f5036134..659a2172a 100644
--- a/crates/storage-pg/src/user/mod.rs
+++ b/crates/storage-pg/src/user/mod.rs
@@ -165,6 +165,9 @@ impl UserRepository for PgUserRepository<'_> {
err,
)]
async fn find_by_username(&mut self, username: &str) -> Result