Skip to content

Commit e9eb8d0

Browse files
committed
refactor(odbc): introduce OdbcBatch for reduced cloning
This commit refactors the OdbcRow structure to utilize an OdbcBatch, allowing for better management of column data and row indices. The OdbcBatch encapsulates the columns and their associated data, streamlining the retrieval process and enhancing the overall architecture of the ODBC module.
1 parent 698cb00 commit e9eb8d0

File tree

5 files changed

+158
-113
lines changed

5 files changed

+158
-113
lines changed

sqlx-core/src/odbc/connection/odbc_bridge.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::decode_column_name;
22
use crate::error::Error;
33
use crate::odbc::{
4-
connection::MaybePrepared, ColumnData, OdbcArgumentValue, OdbcArguments, OdbcColumn,
5-
OdbcQueryResult, OdbcRow, OdbcTypeInfo, OdbcValue,
4+
connection::MaybePrepared, ColumnData, OdbcArgumentValue, OdbcArguments, OdbcBatch, OdbcColumn,
5+
OdbcQueryResult, OdbcRow, OdbcTypeInfo,
66
};
77
use either::Either;
88
use flume::{SendError, Sender};
@@ -247,26 +247,24 @@ where
247247
let mut receiver_open = true;
248248

249249
while let Some(batch) = row_set_cursor.fetch()? {
250-
let columns: Vec<_> = bindings.iter().map(|b| b.column.clone()).collect();
251-
252250
// Create ColumnData instances that can be shared across rows
253-
let column_data_vec: Vec<_> = bindings
251+
let column_data: Vec<_> = bindings
254252
.iter()
255253
.enumerate()
256254
.map(|(col_index, binding)| {
257-
create_column_data(batch.column(col_index), &binding.column.type_info)
255+
create_column_data(batch.column(col_index), &binding.column)
258256
})
259257
.collect();
260258

261-
for row_index in 0..batch.num_rows() {
262-
let row_values: Vec<_> = column_data_vec
263-
.iter()
264-
.map(|column_data| OdbcValue::new(Arc::clone(column_data), row_index))
265-
.collect();
259+
let odbc_batch = Arc::new(OdbcBatch {
260+
columns: bindings.iter().map(|b| b.column.clone()).collect(),
261+
column_data,
262+
});
266263

264+
for row_index in 0..batch.num_rows() {
267265
let row = OdbcRow {
268-
columns: columns.clone(),
269-
values: row_values,
266+
row_index,
267+
batch: Arc::clone(&odbc_batch),
270268
};
271269

272270
if send_row(tx, row).is_err() {
@@ -283,13 +281,13 @@ where
283281
Ok(receiver_open)
284282
}
285283

286-
fn create_column_data(slice: AnySlice<'_>, type_info: &OdbcTypeInfo) -> Arc<ColumnData> {
284+
fn create_column_data(slice: AnySlice<'_>, column: &OdbcColumn) -> Arc<ColumnData> {
287285
use crate::odbc::value::convert_any_slice_to_value_vec;
288286

289287
let (values, nulls) = convert_any_slice_to_value_vec(slice);
290288
Arc::new(ColumnData {
291289
values,
292-
type_info: type_info.clone(),
290+
type_info: column.type_info.clone(),
293291
nulls,
294292
})
295293
}

sqlx-core/src/odbc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub use connection::OdbcConnection;
4343
pub use database::Odbc;
4444
pub use options::OdbcConnectOptions;
4545
pub use query_result::OdbcQueryResult;
46-
pub use row::OdbcRow;
46+
pub use row::{OdbcBatch, OdbcRow};
4747
pub use statement::{OdbcStatement, OdbcStatementMetadata};
4848
pub use transaction::OdbcTransactionManager;
4949
pub use type_info::{DataTypeExt, OdbcTypeInfo};

sqlx-core/src/odbc/row.rs

Lines changed: 57 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
use crate::column::ColumnIndex;
22
use crate::database::HasValueRef;
33
use crate::error::Error;
4-
use crate::odbc::{Odbc, OdbcColumn, OdbcValue};
4+
use crate::odbc::{Odbc, OdbcColumn, OdbcValueRef};
55
use crate::row::Row;
6-
use crate::value::Value;
6+
use std::sync::Arc;
77

88
#[derive(Debug, Clone)]
9-
pub struct OdbcRow {
9+
pub struct OdbcBatch {
1010
pub(crate) columns: Vec<OdbcColumn>,
11-
pub(crate) values: Vec<OdbcValue>,
11+
pub(crate) column_data: Vec<Arc<crate::odbc::ColumnData>>,
12+
}
13+
14+
#[derive(Debug, Clone)]
15+
pub struct OdbcRow {
16+
pub(crate) row_index: usize,
17+
pub(crate) batch: Arc<OdbcBatch>,
1218
}
1319

1420
impl Row for OdbcRow {
1521
type Database = Odbc;
1622

1723
fn columns(&self) -> &[OdbcColumn] {
18-
&self.columns
24+
&self.batch.columns
1925
}
2026

2127
fn try_get_raw<I>(
@@ -25,21 +31,21 @@ impl Row for OdbcRow {
2531
where
2632
I: ColumnIndex<Self>,
2733
{
28-
let idx = index.index(self)?;
29-
let value = &self.values[idx];
30-
Ok(value.as_ref())
34+
let column_index = index.index(self)?;
35+
Ok(OdbcValueRef::new(&self.batch, self.row_index, column_index))
3136
}
3237
}
3338

3439
impl ColumnIndex<OdbcRow> for &str {
3540
fn index(&self, row: &OdbcRow) -> Result<usize, Error> {
3641
// Try exact match first (for performance)
37-
if let Some(pos) = row.columns.iter().position(|col| col.name == *self) {
42+
if let Some(pos) = row.batch.columns.iter().position(|col| col.name == *self) {
3843
return Ok(pos);
3944
}
4045

4146
// Fall back to case-insensitive match (for databases like Snowflake)
42-
row.columns
47+
row.batch
48+
.columns
4349
.iter()
4450
.position(|col| col.name.eq_ignore_ascii_case(self))
4551
.ok_or_else(|| Error::ColumnNotFound((*self).into()))
@@ -56,6 +62,7 @@ mod private {
5662
impl From<OdbcRow> for crate::any::AnyRow {
5763
fn from(row: OdbcRow) -> Self {
5864
let columns = row
65+
.batch
5966
.columns
6067
.iter()
6168
.map(|col| crate::any::AnyColumn {
@@ -81,58 +88,48 @@ mod tests {
8188
use std::sync::Arc;
8289

8390
fn create_test_row() -> OdbcRow {
84-
use crate::odbc::OdbcValue;
91+
let columns = vec![
92+
OdbcColumn {
93+
name: "lowercase_col".to_string(),
94+
type_info: OdbcTypeInfo::new(DataType::Integer),
95+
ordinal: 0,
96+
},
97+
OdbcColumn {
98+
name: "UPPERCASE_COL".to_string(),
99+
type_info: OdbcTypeInfo::new(DataType::Varchar { length: None }),
100+
ordinal: 1,
101+
},
102+
OdbcColumn {
103+
name: "MixedCase_Col".to_string(),
104+
type_info: OdbcTypeInfo::new(DataType::Double),
105+
ordinal: 2,
106+
},
107+
];
108+
109+
let column_data = vec![
110+
ColumnData {
111+
values: OdbcValueVec::NullableBigInt(vec![Some(42)]),
112+
type_info: OdbcTypeInfo::new(DataType::Integer),
113+
nulls: vec![false],
114+
},
115+
ColumnData {
116+
values: OdbcValueVec::Text(vec![Some("test".to_string())]),
117+
type_info: OdbcTypeInfo::new(DataType::Varchar { length: None }),
118+
nulls: vec![false],
119+
},
120+
ColumnData {
121+
values: OdbcValueVec::NullableDouble(vec![Some(std::f64::consts::PI)]),
122+
type_info: OdbcTypeInfo::new(DataType::Double),
123+
nulls: vec![false],
124+
},
125+
];
85126

86127
OdbcRow {
87-
columns: vec![
88-
OdbcColumn {
89-
name: "lowercase_col".to_string(),
90-
type_info: OdbcTypeInfo::new(DataType::Integer),
91-
ordinal: 0,
92-
},
93-
OdbcColumn {
94-
name: "UPPERCASE_COL".to_string(),
95-
type_info: OdbcTypeInfo::new(DataType::Varchar { length: None }),
96-
ordinal: 1,
97-
},
98-
OdbcColumn {
99-
name: "MixedCase_Col".to_string(),
100-
type_info: OdbcTypeInfo::new(DataType::Double),
101-
ordinal: 2,
102-
},
103-
],
104-
values: vec![
105-
{
106-
let column = ColumnData {
107-
values: OdbcValueVec::NullableBigInt(vec![Some(42)]),
108-
type_info: OdbcTypeInfo::new(DataType::Integer),
109-
};
110-
OdbcValue {
111-
column_data: Arc::new(column),
112-
row_index: 0,
113-
}
114-
},
115-
{
116-
let column = ColumnData {
117-
values: OdbcValueVec::Text(vec![Some("test".to_string())]),
118-
type_info: OdbcTypeInfo::new(DataType::Varchar { length: None }),
119-
};
120-
OdbcValue {
121-
column_data: Arc::new(column),
122-
row_index: 0,
123-
}
124-
},
125-
{
126-
let column = ColumnData {
127-
values: OdbcValueVec::NullableDouble(vec![Some(std::f64::consts::PI)]),
128-
type_info: OdbcTypeInfo::new(DataType::Double),
129-
};
130-
OdbcValue {
131-
column_data: Arc::new(column),
132-
row_index: 0,
133-
}
134-
},
135-
],
128+
row_index: 0,
129+
batch: Arc::new(OdbcBatch {
130+
columns,
131+
column_data,
132+
}),
136133
}
137134
}
138135

sqlx-core/src/odbc/types/chrono.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ impl<'r> Decode<'r, Odbc> for NaiveDate {
254254

255255
Err(format!(
256256
"ODBC: cannot decode NaiveDate from value with type '{}'",
257-
value.column_data.type_info.name()
257+
value.batch.columns[value.column_index].type_info.name()
258258
)
259259
.into())
260260
}

0 commit comments

Comments
 (0)