fix: increase performance of ListUser by login name ignore case (#12350)

# Which Problems Are Solved

Login v2 uses the `users.v2.ListUsers`-endpoint to get a user by login
name. This query had an inefficient `WHERE`-clause.

# How the Problems Are Solved

Update the view and use a specific clause for this query.

# Additional information

Added in https://github.com/zitadel/zitadel/pull/10475

---------

Co-authored-by: Marco A. <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
Silvan
2026-06-30 11:20:21 +00:00
committed by GitHub
co-authored by Marco A. Livio Spring
parent 561da54ff7
commit f9995ee39c
11 changed files with 137 additions and 27 deletions
+27
View File
@@ -0,0 +1,27 @@
package setup
import (
"context"
_ "embed"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore"
)
var (
//go:embed 72.sql
addColumnsToLoginNamesView string
)
type AddColumnsToLoginNamesView struct {
dbClient *database.DB
}
func (mig *AddColumnsToLoginNamesView) Execute(ctx context.Context, _ eventstore.Event) error {
_, err := mig.dbClient.ExecContext(ctx, addColumnsToLoginNamesView)
return err
}
func (mig *AddColumnsToLoginNamesView) String() string {
return "72_add_columns_to_login_names_view"
}
+42
View File
@@ -0,0 +1,42 @@
CREATE OR REPLACE VIEW projections.login_names3 AS (
SELECT
u.id AS user_id
, CASE
WHEN p.must_be_domain THEN CONCAT(u.user_name, '@', d.name)
ELSE u.user_name
END AS login_name
, COALESCE(d.is_primary, TRUE) AS is_primary
, u.instance_id
, u.resource_owner
, CASE
WHEN p.must_be_domain THEN CONCAT(u.user_name_lower, '@', d.name_lower)
ELSE u.user_name_lower
END AS login_name_lower
FROM
projections.login_names3_users AS u
LEFT JOIN LATERAL (
SELECT
must_be_domain
, is_default
FROM
projections.login_names3_policies AS p
WHERE
(
p.instance_id = u.instance_id
AND NOT p.is_default
AND p.resource_owner = u.resource_owner
) OR (
p.instance_id = u.instance_id
AND p.is_default
)
ORDER BY
p.is_default -- custom first
LIMIT 1
) AS p ON TRUE
LEFT JOIN
projections.login_names3_domains d
ON
p.must_be_domain
AND u.resource_owner = d.resource_owner
AND u.instance_id = d.instance_id
);
+1
View File
@@ -191,6 +191,7 @@ type Steps struct {
s69CacheTablesLogged *CacheTablesLogged
s70AddEventStoreCommandEnforceOwner *AddEventStoreCommandEnforceOwnerColumn
s71JWTProvideAddAudienceColumn *JWTProvideAddAudienceColumn
s72AddColumnsToLoginNamesView *AddColumnsToLoginNamesView
RelationalTables *TransactionalTables
}
+2
View File
@@ -254,6 +254,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
steps.s69CacheTablesLogged = &CacheTablesLogged{dbClient: dbClient}
steps.s70AddEventStoreCommandEnforceOwner = &AddEventStoreCommandEnforceOwnerColumn{dbClient: dbClient}
steps.s71JWTProvideAddAudienceColumn = &JWTProvideAddAudienceColumn{dbClient: dbClient}
steps.s72AddColumnsToLoginNamesView = &AddColumnsToLoginNamesView{dbClient: dbClient}
err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil, nil)
if err != nil {
@@ -378,6 +379,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
steps.s66SessionRecoveryCodeCheckedAt,
steps.s68TargetAddPayloadTypeColumn,
steps.s71JWTProvideAddAudienceColumn,
steps.s72AddColumnsToLoginNamesView,
} {
setupErr = executeMigration(ctx, eventstoreClient, step, "migration failed")
if setupErr != nil {