2626from pulpcore .app .loggers import deprecation_logger
2727from pulpcore .exceptions .validation import InvalidSignatureError
2828
29- POSTGRES_MAX_QUERY_PARAMS = 65535
30-
3129
3230class AnyArray (Lookup ):
33- """PostgreSQL `= ANY(%s)` lookup that passes a list as a single array parameter.
31+ """PostgreSQL `` = ANY(%s)` ` lookup that passes a list as a single array parameter.
3432
3533 psycopg3 adapts the Python list into a PostgreSQL array, so the entire list
3634 counts as **one** bind parameter regardless of size. This avoids the
37- protocol-level 65535 -parameter limit that `IN ($1, $2, …)` hits.
35+ protocol-level 65,535 -parameter limit that `` IN ($1, $2, …)` ` hits.
3836 """
3937
4038 lookup_name = "any_array"
@@ -51,20 +49,23 @@ def as_sql(self, compiler, connection):
5149
5250
5351def safe_in (field_name , values ):
54- """Build a `Q` object for `field__in` that is safe for arbitrarily large lists.
52+ """Build a ``Q`` object for filtering by a list of values that is safe at any size.
53+
54+ Uses PostgreSQL's ``= ANY(array)`` syntax so the entire list is sent as a
55+ single bind parameter, avoiding the 65,535-parameter protocol limit that
56+ Django's default ``__in`` lookup hits with large lists.
5557
56- * If *values* is already a queryset (or other non-collection type), the
57- normal `__in` lookup is used — Django turns it into a subquery.
58- * If the collection has fewer than 65 535 items, `__in` is used as-is.
59- * Otherwise `__any_array` is used so the whole list travels as a single
60- PostgreSQL array parameter.
58+ Use this for values that are **already materialised in Python** (e.g.
59+ parsed from JSON, accumulated in a loop). When the IDs live in a database
60+ column, prefer a subquery instead — it keeps the data server-side and
61+ avoids the round-trip entirely.
62+
63+ If *values* is a queryset (or other non-collection type), falls back to
64+ the normal ``__in`` lookup, which Django turns into a subquery.
6165 """
6266 if not isinstance (values , (list , set , tuple , frozenset )):
6367 return Q (** {f"{ field_name } __in" : values })
64- values = list (values )
65- if len (values ) < POSTGRES_MAX_QUERY_PARAMS :
66- return Q (** {f"{ field_name } __in" : values })
67- return Q (** {f"{ field_name } __any_array" : values })
68+ return Q (** {f"{ field_name } __any_array" : list (values )})
6869
6970
7071# a little cache so viewset_for_model doesn't have to iterate over every app every time
0 commit comments