@@ -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