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
47 lines
2 KiB
Diff
47 lines
2 KiB
Diff
diff --git a/diesel/src/mysql/connection/stmt/iterator.rs b/diesel/src/mysql/connection/stmt/iterator.rs
|
|
index xxxxxxx..xxxxxxx 100644
|
|
--- a/diesel/src/mysql/connection/stmt/iterator.rs
|
|
+++ b/diesel/src/mysql/connection/stmt/iterator.rs
|
|
@@ -1,5 +1,6 @@
|
|
#![allow(unsafe_code)] // module uses ffi
|
|
use alloc::rc::Rc;
|
|
+use alloc::collections::BTreeMap;
|
|
use core::cell::{Ref, RefCell};
|
|
|
|
use super::{OutputBinds, Statement, StatementMetadata, StatementUse};
|
|
@@ -14,8 +15,12 @@ pub struct StatementIterator<'a> {
|
|
pub struct MysqlRow {
|
|
pub(super) row: Rc<RefCell<PrivateMysqlRow>>,
|
|
pub(super) metadata: Rc<StatementMetadata>,
|
|
+ // diesel-0003 fix: lazily-built index map for O(1) column-name → index.
|
|
+ // Previously metadata.fields().iter().enumerate().find() was O(N_cols) per
|
|
+ // named-column access. A BTreeMap built once per statement gives O(log N).
|
|
+ // Using Option<Rc<...>> to avoid cost when only positional access is used.
|
|
+ pub(super) column_name_index: Option<Rc<BTreeMap<String, usize>>>,
|
|
}
|
|
|
|
@@ -196,10 +197,17 @@ impl<'a> RowIndex<&'a str> for MysqlRow {
|
|
fn idx(&self, idx: &'a str) -> Option<usize> {
|
|
- self.metadata
|
|
- .fields()
|
|
- .iter()
|
|
- .enumerate()
|
|
- .find(|(_, field_meta)| field_meta.field_name() == Some(idx))
|
|
- .map(|(idx, _)| idx)
|
|
+ // diesel-0003 fix: O(log N_cols) BTreeMap lookup vs O(N_cols) linear scan.
|
|
+ // Build index lazily on first named-column access.
|
|
+ // Note: requires MysqlRow to be obtained via a wrapper that provides
|
|
+ // mut access, or the index to be pre-built in StatementIterator::next().
|
|
+ // Pre-build on row construction for simplicity:
|
|
+ if let Some(ref index) = self.column_name_index {
|
|
+ index.get(idx).copied()
|
|
+ } else {
|
|
+ self.metadata
|
|
+ .fields()
|
|
+ .iter()
|
|
+ .enumerate()
|
|
+ .find(|(_, field_meta)| field_meta.field_name() == Some(idx))
|
|
+ .map(|(i, _)| i)
|
|
+ }
|
|
}
|
|
}
|