AAP-83319 — Replace OR with UNION in team list RBAC query (#16554)

The TeamAccess.filtered_queryset() method combined two access paths
(org membership and direct read permission) using Q(…) | Q(…), which
PostgreSQL could not optimize — it scanned all teams for each OR
branch. Replacing the OR with UNION lets each branch use the
RoleEvaluation 3-column index independently. The UNION result is
wrapped in pk__in= so the outer queryset remains compatible with
BaseAccess.get_queryset()'s select_related() call.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dirk Julich
2026-07-29 20:44:17 +02:00
committed by GitHub
parent 78a55b25ec
commit f1a3e13df7

View File

@@ -1232,9 +1232,11 @@ class TeamAccess(BaseAccess):
Organization.access_qs(self.user, 'change').exists() or Organization.access_qs(self.user, 'audit').exists() Organization.access_qs(self.user, 'change').exists() or Organization.access_qs(self.user, 'audit').exists()
): ):
return self.model.objects.all() return self.model.objects.all()
return self.model.objects.filter( org_member_teams = (
Q(organization__in=Organization.accessible_pk_qs(self.user, 'member_role')) | Q(pk__in=self.model.accessible_pk_qs(self.user, 'read_role')) self.model.objects.filter(organization__in=Organization.accessible_pk_qs(self.user, 'member_role')).order_by().values_list('pk', flat=True)
) )
direct_read_teams = self.model.objects.filter(pk__in=self.model.accessible_pk_qs(self.user, 'read_role')).order_by().values_list('pk', flat=True)
return self.model.objects.filter(pk__in=org_member_teams.union(direct_read_teams))
@check_superuser @check_superuser
def can_add(self, data): def can_add(self, data):