Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions pulpcore/app/models/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,37 @@ def with_content(self, content):
return self.filter(content_ids__overlap=content_pks)


class RepositoryVersionManager(models.Manager):
# `RepositoryVersionQuerySet.as_manager()` does not allow us to redefine `get_queryset`.
# Sadly, we have to replicate the filtering amenities too.
#
def get_queryset(self):
# Prevent the content_ids to be automatically hydrated.
return RepositoryVersionQuerySet(self.model, using=self._db).defer("content_ids")

def complete(self):
return self.get_queryset().filter(complete=True)

def with_content(self, content):
"""
Filters repository versions that contain the provided content units.

Args:
content (django.db.models.QuerySet or list): Content queryset or list of PKs

Returns:
django.db.models.QuerySet: Repository versions which contains content.
"""
if isinstance(content, models.QuerySet):
content_pks = content.values_list("pk", flat=True)
elif not content:
return self.none()
else:
content_pks = content

return self.get_queryset().filter(content_ids__overlap=content_pks)


class RepositoryVersion(BaseModel):
"""
A version of a repository's content set.
Expand Down Expand Up @@ -936,7 +967,7 @@ class RepositoryVersion(BaseModel):
base_version (models.ForeignKey): The repository version this was created from.
"""

objects = RepositoryVersionQuerySet.as_manager()
objects = RepositoryVersionManager()

repository = models.ForeignKey(Repository, on_delete=models.CASCADE)
number = models.PositiveIntegerField(db_index=True)
Expand Down Expand Up @@ -997,15 +1028,13 @@ def get_content(self, content_qs=None):
if content_qs is None:
content_qs = Content.objects

content_ids = self.content_ids
if len(content_ids) >= 65535:
# Workaround for PostgreSQL's limit on the number of parameters in a query
content_ids = (
RepositoryVersion.objects.filter(pk=self.pk)
.annotate(cids=Func(F("content_ids"), function="unnest"))
.values_list("cids", flat=True)
)
return content_qs.filter(pk__in=content_ids)
# Try to not even attempt to evaluate the content_ids on the python side.
content_ids_subquery = (
RepositoryVersion.objects.filter(pk=self.pk)
.annotate(cids=Func(F("content_ids"), function="unnest"))
.values_list("cids", flat=True)
)
return content_qs.filter(pk__in=content_ids_subquery)

@property
def content(self):
Expand Down Expand Up @@ -1119,9 +1148,7 @@ def added(self, base_version=None):
if not base_version:
return Content.objects.filter(version_memberships__version_added=self)

return Content.objects.filter(pk__in=self.content_ids).exclude(
pk__in=base_version.content_ids
)
return Content.objects.filter(pk__in=self.content).exclude(pk__in=base_version.content)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are any instances where we are calling content.pk in rv.content_ids across different RVs, this might create N+1. I don't know that we do, just calling it out.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope not. But even if we do, i expect it to be only 1+1, because the whole content_ids field would be fetched deferred, but only once.


def removed(self, base_version=None):
"""
Expand All @@ -1134,9 +1161,7 @@ def removed(self, base_version=None):
if not base_version:
return Content.objects.filter(version_memberships__version_removed=self)

return Content.objects.filter(pk__in=base_version.content_ids).exclude(
pk__in=self.content_ids
)
return Content.objects.filter(pk__in=base_version.content).exclude(pk__in=self.content)

@dralley dralley Jul 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are now double-nested subqueries again, I feel like that might have been part of the motivation for the original change?


def contains(self, content):
"""
Expand All @@ -1145,7 +1170,7 @@ def contains(self, content):
Returns:
bool: True if the repository version contains the content, False otherwise
"""
return content.pk in self.content_ids
return content.pk in self.content

def add_content(self, content):
"""
Expand Down
Loading