Skip to content

Commit cd6685e

Browse files
committed
Add a test for the postgresql parameter limit workaround
Assisted By: Claude Code
1 parent f14f893 commit cd6685e

1 file changed

Lines changed: 84 additions & 0 deletions

File tree

pulpcore/tests/unit/models/test_repository.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,3 +788,87 @@ def test_batch_operations_preserve_correctness(repository, db):
788788
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.PRESENT).count == 40
789789
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.ADDED).first() is None
790790
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.REMOVED).count == 60
791+
792+
793+
def test_postgresql_parameter_limit(db, repository):
794+
"""
795+
Test repository operations with >65535 content units to verify PostgreSQL parameter limit
796+
workaround.
797+
798+
PostgreSQL limits queries to 65535 parameters. This test verifies that content, added(),
799+
and removed() all handle >65535 items correctly.
800+
801+
Queries MUST be evaluated via .iterator() because psycopg3 uses client-side binding for
802+
regular queries (inlining params into the SQL string, bypassing the limit) but server-side
803+
binding for server-side cursors (.iterator()), which enforces the 65,535 parameter cap.
804+
Without .iterator() the test passes even when the fix is absent.
805+
"""
806+
# Create 66000 content units (exceeds PostgreSQL's 65535 parameter limit)
807+
large_content_set = [Content(pulp_type="core.content") for _ in range(66000)]
808+
Content.objects.bulk_create(large_content_set, batch_size=2000)
809+
large_pks = sorted([c.pk for c in large_content_set])
810+
811+
version0 = repository.latest_version()
812+
813+
# Test 1: Add >65535 content units - tests added() and content with >65535 items
814+
with repository.new_version() as version1:
815+
version1.add_content(Content.objects.filter(pk__in=large_pks))
816+
817+
# Verify content_ids exceeds the PostgreSQL parameter limit threshold
818+
assert isinstance(version1.content_ids, list)
819+
assert len(version1.content_ids) >= 65535
820+
821+
# Test the content property with >65535 items
822+
# .iterator() forces a server-side cursor — the only way to reliably trigger the
823+
# 65,535-parameter limit in psycopg3.
824+
content_pks = set(version1.content.values_list("pk", flat=True).iterator())
825+
assert len(content_pks) == 66000
826+
827+
# Test the added() method with >65535 items
828+
added_pks = set(version1.added(base_version=version0).values_list("pk", flat=True).iterator())
829+
assert len(added_pks) == 66000 # Critical: added() must handle >65535 items
830+
831+
# Test the removed() method returns nothing (nothing was removed)
832+
removed_pks = set(
833+
version1.removed(base_version=version0).values_list("pk", flat=True).iterator()
834+
)
835+
assert len(removed_pks) == 0
836+
837+
# Verify RepositoryVersionContentDetails
838+
rvcd_qs = RepositoryVersionContentDetails.objects.filter(
839+
repository_version=version1, content_type="core.content"
840+
)
841+
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.PRESENT).count == 66000
842+
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.ADDED).count == 66000
843+
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.REMOVED).first() is None
844+
845+
# Test 2: Remove >65535 content units - tests removed() with >65535 items
846+
with repository.new_version() as version2:
847+
version2.remove_content(Content.objects.filter(pk__in=large_pks))
848+
849+
# Test the content property returns nothing (all content was removed)
850+
content_pks = set(version2.content.values_list("pk", flat=True).iterator())
851+
assert len(content_pks) == 0
852+
853+
# Test the added() method returns nothing (nothing was added)
854+
added_pks = set(version2.added(base_version=version1).values_list("pk", flat=True).iterator())
855+
assert len(added_pks) == 0
856+
857+
# Test the removed() method with >65535 items
858+
removed_pks = set(
859+
version2.removed(base_version=version1).values_list("pk", flat=True).iterator()
860+
)
861+
assert len(removed_pks) == 66000 # Critical: removed() must handle >65535 items
862+
863+
# Verify RepositoryVersionContentDetails
864+
rvcd_qs = RepositoryVersionContentDetails.objects.filter(
865+
repository_version=version2, content_type="core.content"
866+
)
867+
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.PRESENT).first() is None
868+
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.ADDED).first() is None
869+
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.REMOVED).count == 66000
870+
871+
# Verify we can iterate and fetch content without errors
872+
first_100 = list(version1.content[:100].values_list("pk", flat=True))
873+
assert len(first_100) == 100
874+
assert all(pk in large_pks for pk in first_100)

0 commit comments

Comments
 (0)