88 lines
3.6 KiB
Diff
88 lines
3.6 KiB
Diff
# UNDF: UNDF-2026-000000043
|
||
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()
|
||
+ }
|
||
}
|
||
}
|
||
}
|