Skip to content

Commit 0f29876

Browse files
committed
Add a test for the postgresql parameter limit workaround
Assisted By: Claude Code
1 parent 243cf47 commit 0f29876

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

pulpcore/tests/unit/models/test_repository.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,3 +788,91 @@ 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(
829+
version1.added(base_version=version0).values_list("pk", flat=True).iterator()
830+
)
831+
assert len(added_pks) == 66000 # Critical: added() must handle >65535 items
832+
833+
# Test the removed() method returns nothing (nothing was removed)
834+
removed_pks = set(
835+
version1.removed(base_version=version0).values_list("pk", flat=True).iterator()
836+
)
837+
assert len(removed_pks) == 0
838+
839+
# Verify RepositoryVersionContentDetails
840+
rvcd_qs = RepositoryVersionContentDetails.objects.filter(
841+
repository_version=version1, content_type="core.content"
842+
)
843+
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.PRESENT).count == 66000
844+
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.ADDED).count == 66000
845+
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.REMOVED).first() is None
846+
847+
# Test 2: Remove >65535 content units - tests removed() with >65535 items
848+
with repository.new_version() as version2:
849+
version2.remove_content(Content.objects.filter(pk__in=large_pks))
850+
851+
# Test the content property returns nothing (all content was removed)
852+
content_pks = set(version2.content.values_list("pk", flat=True).iterator())
853+
assert len(content_pks) == 0
854+
855+
# Test the added() method returns nothing (nothing was added)
856+
added_pks = set(
857+
version2.added(base_version=version1).values_list("pk", flat=True).iterator()
858+
)
859+
assert len(added_pks) == 0
860+
861+
# Test the removed() method with >65535 items
862+
removed_pks = set(
863+
version2.removed(base_version=version1).values_list("pk", flat=True).iterator()
864+
)
865+
assert len(removed_pks) == 66000 # Critical: removed() must handle >65535 items
866+
867+
# Verify RepositoryVersionContentDetails
868+
rvcd_qs = RepositoryVersionContentDetails.objects.filter(
869+
repository_version=version2, content_type="core.content"
870+
)
871+
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.PRESENT).first() is None
872+
assert rvcd_qs.filter(count_type=RepositoryVersionContentDetails.ADDED).first() is None
873+
assert rvcd_qs.get(count_type=RepositoryVersionContentDetails.REMOVED).count == 66000
874+
875+
# Verify we can iterate and fetch content without errors
876+
first_100 = list(version1.content[:100].values_list("pk", flat=True))
877+
assert len(first_100) == 100
878+
assert all(pk in large_pks for pk in first_100)

0 commit comments

Comments
 (0)