java-topology/defects/gorm/patch/gorm-0001-sortcallbacks-map-index.patch
russell@unturf.com d4ed2dff91 ORM wave: 24 defects patched across 10 ORMs (157 sites, 62 ecosystems)
Hibernate (5 HIGH): addColumn/addReferencedColumn/addIndex ArrayList→LinkedHashSet (19x)
  FK second-pass LinkedHashSet, orderHierarchy LinkedHashSet
MyBatis (1 MEDIUM): sortConstructorMappings indexOf→HashMap (12x)
EF Core (2 HIGH + 1 MEDIUM): FindGenerationProperty HashSet (250x),
  AddPrincipals HashSet (250x), FK discovery HashSet (6x)
Diesel (3 MEDIUM): SQLite/MySQL row position()→BTreeMap (51x)
SQLAlchemy (2 HIGH): _values_bindparam Set (500x), evaluated_keys Set (500x)
Peewee (1 MEDIUM): _SortedFieldList.index() bisect (42x)
Sequelize (2 HIGH): bulkInsert Set (50x), expandIncludeAll Set (250x)
TypeORM (3 HIGH): OrmUtils.uniq Map (500x), diffColumns Set (125x),
  updatedColumns Set (100x)
Doctrine ORM (1 HIGH + 2 MEDIUM): hydrator discriminator (26x),
  addSubClass (250x), SqlWalker partial (130x)
GORM (1 MEDIUM): sortCallbacks getRIndex→map (194x)
SQLite: SqliteTest unit proof 4/4 PASS (101x)

Unit tests: all PASS — Hibernate/MyBatis/EfCore/Diesel/SQLAlchemy/Peewee/
  Sequelize/TypeORM/Doctrine/GORM
Whitepaper: 157 sites, 62 ecosystems; PDF 752K
2026-03-27 13:34:26 -04:00

53 lines
2 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Fixes gorm-0001: callbacks.go getRIndex linear scan in sortCallbacks.
--- a/callbacks.go
+++ b/callbacks.go
@@ DEFECT gorm-0001: callbacks.go:252 getRIndex() — O(N) linear scan
-// getRIndex finds the last index of str in strs — O(N) linear scan.
-func getRIndex(strs []string, str string) int {
- for i := len(strs) - 1; i >= 0; i-- {
- if strs[i] == str {
- return i
- }
- }
- return -1
-}
+// getRIndex — replaced by map-based O(1) lookup in sortCallbacks.
+// FIX gorm-0001: getRIndex is no longer needed with map[string]int indices.
func sortCallbacks(cs []*callback) (fns []*callback, err error) {
- var (
- names, sorted []string
- sortCallback func(*callback) error
- )
- for _, c := range cs {
- names = append(names, c.name)
- }
-
- sortCallback = func(c *callback) error {
- // ... multiple getRIndex(names, ...) and getRIndex(sorted, ...) calls
- // Each is O(N); called 13 times per callback; O(N^2) total per sort.
- }
+ // FIX gorm-0001: build name→index map once for O(1) lookups in sort step.
+ namesMap := make(map[string]int, len(cs))
+ for i, c := range cs {
+ namesMap[c.name] = i
+ }
+ sortedMap := make(map[string]int, len(cs))
+ sortCallback = func(c *callback) error {
+ if _, exists := sortedMap[c.name]; exists {
+ return nil
+ }
+ // Replace all getRIndex(names, dep) calls with namesMap[dep] — O(1)
+ // Replace all getRIndex(sorted, dep) calls with sortedMap[dep] — O(1)
+ // ...
+ }
# BEFORE: getRIndex(names, x) = O(N) × 13 calls per callback × N callbacks = O(N²) per sort.
# sortCallbacks is called on every Register()/Remove()/Replace().
# For N=26 default callbacks: ~8,788 comparisons per startup.
# AFTER: namesMap[x] = O(1). Full sort is O(N). Total startup: O(N²) → O(N log N).
# Triggered by: GORM startup (init default callbacks), plugin registration, test setup.