1.6 KiB
1.6 KiB
UNDF: UNDF-2026-000000542
sqlalchemy-0003: evaluated_keys list → set in _apply_evaluators()
Severity
MEDIUM
Location
lib/sqlalchemy/orm/bulk_persistence.py — _apply_evaluators() (approx. line 1873)
Description
evaluated_keys = list(value_evaluators.keys()) converts a dict's key view
to a plain list. The list is then used for:
c.key not in evaluated_keys— O(K) membership scan inside a set comprehension overprefetch_cols(one test per column).difference(evaluated_keys)— called on a set; this is O(P×K) rather than the O(P) it would be with a set argument
Fix: evaluated_keys = set(value_evaluators) (or just use value_evaluators
directly for membership, since dict in is O(1)).
Defective code (lines 1873–1889)
evaluated_keys = list(value_evaluators.keys()) # ← plain list
to_prefetch = {
c
for c in prefetch_cols
if c.key in effective_params
and c in mapper._columntoproperty
and c.key not in evaluated_keys # ← O(K) linear scan per column
}
to_expire = {
mapper._columntoproperty[c].key
for c in postfetch_cols
if c in mapper._columntoproperty
}.difference(evaluated_keys) # ← set.difference(list) is O(P×K)
Fix
--- a/lib/sqlalchemy/orm/bulk_persistence.py
+++ b/lib/sqlalchemy/orm/bulk_persistence.py
@@ -1873,1 +1873,1 @@
- evaluated_keys = list(value_evaluators.keys())
+ evaluated_keys = set(value_evaluators)
Complexity
- Before: O(C×K) for the
to_prefetchcomprehension, O(P×K) for.difference() - After: O(C) + O(P) — both operations drop to O(1) membership