From 83240706b282905e1995c32deee41a92d9b7cc01 Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Thu, 22 Oct 2026 12:04:01 +0200 Subject: [PATCH] fix(cabana): bulk lifecycle actions change every selected administrator actionDB returned the Unscoped instance of the action's session, whose conditions accumulate, so a bulk activate, deactivate or restore of two applicable rows wrote only the first (the second update matched "id = A AND id = B") while still counting it. A bulk deactivate therefore left the other administrators active and signed in (D-10, D-12). actionDB now ends with a plain Session, so every query starts anew. Found by TestAdministratorsLifecycle. --- modules/cabana/backend_users_admin.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/cabana/backend_users_admin.go b/modules/cabana/backend_users_admin.go index 9b31014f..e3181c74 100644 --- a/modules/cabana/backend_users_admin.go +++ b/modules/cabana/backend_users_admin.go @@ -296,13 +296,16 @@ func (c usersAdmin) lifecycles() []usersLifecycle { } } -// actionDB is the action's write transaction as a fresh, unscoped session. +// actionDB is the action's write transaction as a fresh, unscoped session +// that every query starts from anew. The final Session matters: Unscoped +// returns an instance whose conditions accumulate, so a bulk action's +// second "id = ?" update would otherwise match no row. func actionDB(ctx context.Context) (*gorm.DB, error) { tx, ok := TxFromContext(ctx) if !ok { return nil, errors.New("cabana: administrators: an action needs its transaction") } - return tx.Session(&gorm.Session{NewDB: true}).WithContext(ctx).Unscoped(), nil + return tx.Session(&gorm.Session{NewDB: true}).WithContext(ctx).Unscoped().Session(&gorm.Session{}), nil } // AdminRecordActions are Activate, Deactivate and Restore on the preview