mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-20 23:52:05 -04:00
0dcfa5e9ea
ORM result row fetching now processes rows as plain tuples rather than
constructing :class:`.Row` objects, as ORM loaders use position-based
access and do not require the :class:`.Row` interface. :class:`.Row`
construction is still used when engine-level debug logging is enabled so
that individual rows can be logged. Benchmarks show a 3-16% improvement in
ORM entity load times depending on query shape. Pull request courtesy
Oliver Parker.
Adds Result._all_interim_rows(), which returns the remaining rows as processed plain tuples, applying result processors and tuple filters but skipping Row object construction. ORM loading uses this for its row fetch; its row getters are position-based itemgetters that accept any tuple-like row. Results that require row logging or have scalar sources fall back to Row construction.
Adds tests covering the new behavior: rows are plain tuples with result processors applied, and Row construction still occurs when engine-level row logging is enabled, at both the Result and ORM loading level.
Benchmarked on an in-memory SQLite database with this change alone (median of 30 runs, ms); this path is used by all ORM entity loads:
plain_small (500 rows x 5 cols) 2.43 -> 2.28 -6%
plain_wide (2000 rows x 25 cols) 8.17 -> 7.24 -11%
joined_o2m (500 x 10) 18.56 -> 16.95 -9%
selectin_o2m (500 x 10) 18.45 -> 17.79 -4%
selectin_nested (50 x 10 x 10) 18.67 -> 17.33 -7%
selectin_m2m (500 x 10) 12.70 -> 11.53 -9%
selectin_m2o (5000 -> 200) 15.83 -> 15.29 -3%
selectin_o2m_few_big (20 x 500) 32.96 -> 31.29 -5%
subquery_o2m (500 x 10) 21.51 -> 18.16 -16%
Fixes: #13363
Closes: #13365
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/13365
Pull-request-sha: 46f4125ead
Change-Id: Ia91a89c8cac78d4790391bc5b171b76d4aaca71b