java-topology/defects/diesel/patch/diesel-0001-sqlite-row-column-name-hashmap.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

87 lines
3.5 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.

diff --git a/diesel/src/sqlite/connection/row.rs b/diesel/src/sqlite/connection/row.rs
index xxxxxxx..xxxxxxx 100644
--- a/diesel/src/sqlite/connection/row.rs
+++ b/diesel/src/sqlite/connection/row.rs
@@ -1,5 +1,6 @@
use super::owned_row::OwnedSqliteRow;
use super::sqlite_value::{OwnedSqliteValue, SqliteValue};
use super::stmt::StatementUse;
use crate::backend::Backend;
use crate::row::{Field, IntoOwnedRow, PartialRow, Row, RowIndex, RowSealed};
use crate::sqlite::Sqlite;
use alloc::borrow::ToOwned;
use alloc::rc::Rc;
use alloc::string::String;
+use alloc::collections::BTreeMap;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::{Ref, RefCell};
@@ -22,8 +23,12 @@ pub(super) enum PrivateSqliteRow<'stmt, 'query> {
Duplicated {
values: Vec<Option<OwnedSqliteValue>>,
column_names: Rc<[Option<String>]>,
+ // diesel-0001 fix: index map for O(1) column-name → index lookup.
+ // Previously column_names.iter().position() is O(N_cols) per call.
+ // Called once per named-column access (row.get("name")), so a query
+ // deserializing M named columns across R rows costs O(R×M²) without fix
+ // and O(R×M) with fix.
+ column_name_index: Rc<BTreeMap<String, usize>>,
},
}
@@ -48,13 +53,23 @@ impl<'stmt, 'query> PrivateSqliteRow<'stmt, 'query> {
let column_names = if let Some(column_names) = column_names {
column_names.clone()
} else {
let c: Rc<[Option<String>]> = Rc::from(
(0..stmt.column_count())
.map(|idx| stmt.field_name(idx).map(|s| s.to_owned()))
.collect::<Vec<_>>(),
);
*column_names = Some(c.clone());
c
};
+ let column_name_index: Rc<BTreeMap<String, usize>> = Rc::new(
+ column_names
+ .iter()
+ .enumerate()
+ .filter_map(|(i, n)| n.as_ref().map(|name| (name.clone(), i)))
+ .collect(),
+ );
PrivateSqliteRow::Duplicated {
values: (0..stmt.column_count())
.map(|idx| stmt.copy_value(idx))
.collect(),
column_names,
+ column_name_index,
}
}
PrivateSqliteRow::Duplicated {
values,
column_names,
+ column_name_index,
} => PrivateSqliteRow::Duplicated {
values: values
.iter()
.map(|v| v.as_ref().map(|v| v.duplicate()))
.collect(),
column_names: column_names.clone(),
+ column_name_index: column_name_index.clone(),
},
}
}
@@ -173,8 +188,12 @@ impl<'idx> RowIndex<&'idx str> for SqliteRow<'_, '_> {
fn idx(&self, field_name: &'idx str) -> Option<usize> {
match &mut *self.inner.borrow_mut() {
PrivateSqliteRow::Direct(stmt) => stmt.index_for_column_name(field_name),
- PrivateSqliteRow::Duplicated { column_names, .. } => column_names
- .iter()
- .position(|n| n.as_ref().map(|s| s as &str) == Some(field_name)),
+ PrivateSqliteRow::Duplicated { column_name_index, .. } => {
+ column_name_index.get(field_name).copied()
+ }
}
}
}