java-topology/defects/sqlalchemy/patch/sqlalchemy-0003-evaluated-keys-set.md

1.6 KiB
Raw Blame History

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:

  1. c.key not in evaluated_keys — O(K) membership scan inside a set comprehension over prefetch_cols (one test per column)
  2. .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 18731889)

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_prefetch comprehension, O(P×K) for .difference()
  • After: O(C) + O(P) — both operations drop to O(1) membership