Skip to content

Commit 36d1516

Browse files
committed
refactor(odbc): enhance OdbcValue and ColumnData structures
This commit introduces significant changes to the ODBC module, including the addition of the `OdbcValueVec` enum to encapsulate various ODBC types and the `ColumnData` struct to hold column values along with their type information. The `OdbcValue` and `OdbcValueRef` structures are updated to utilize `ColumnData`, improving memory management through the use of `Arc`. Additionally, utility methods for value extraction and conversion are added, streamlining the handling of ODBC data types and enhancing overall code clarity.
1 parent 094d43c commit 36d1516

File tree

16 files changed

+648
-515
lines changed

16 files changed

+648
-515
lines changed
Lines changed: 24 additions & 246 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::error::Error;
22
use crate::odbc::{
3-
connection::MaybePrepared, OdbcArgumentValue, OdbcArguments, OdbcColumn, OdbcQueryResult,
4-
OdbcRow, OdbcTypeInfo, OdbcValue,
3+
connection::MaybePrepared, ColumnData, OdbcArgumentValue, OdbcArguments, OdbcColumn,
4+
OdbcQueryResult, OdbcRow, OdbcTypeInfo, OdbcValue,
55
};
66
use either::Either;
77
use flume::{SendError, Sender};
@@ -10,6 +10,7 @@ use odbc_api::handles::{AsStatementRef, Nullability, Statement};
1010
use odbc_api::DataType;
1111
use odbc_api::{Cursor, IntoParameter, ResultSetMetadata};
1212
use std::cmp::min;
13+
use std::sync::Arc;
1314

1415
// Bulk fetch implementation using columnar buffers instead of row-by-row fetching
1516
// This provides significant performance improvements by fetching rows in batches
@@ -18,8 +19,8 @@ const BATCH_SIZE: usize = 128;
1819
const DEFAULT_TEXT_LEN: usize = 512;
1920
const DEFAULT_BINARY_LEN: usize = 1024;
2021
const DEFAULT_NUMERIC_TEXT_LEN: usize = 128;
21-
const MAX_TEXT_LEN: usize = 1024 * 1024;
22-
const MAX_BINARY_LEN: usize = 1024 * 1024;
22+
const MAX_TEXT_LEN: usize = 1024;
23+
const MAX_BINARY_LEN: usize = 1024;
2324

2425
struct ColumnBinding {
2526
column: OdbcColumn,
@@ -238,21 +239,24 @@ where
238239
while let Some(batch) = row_set_cursor.fetch()? {
239240
let columns: Vec<_> = bindings.iter().map(|b| b.column.clone()).collect();
240241

242+
// Create ColumnData instances that can be shared across rows
243+
let column_data_vec: Vec<_> = bindings
244+
.iter()
245+
.enumerate()
246+
.map(|(col_index, binding)| {
247+
create_column_data(batch.column(col_index), &binding.column.type_info)
248+
})
249+
.collect();
250+
241251
for row_index in 0..batch.num_rows() {
242-
let row_values: Vec<_> = bindings
252+
let row_values: Vec<_> = column_data_vec
243253
.iter()
244-
.enumerate()
245-
.map(|(col_index, binding)| {
246-
let type_info = binding.column.type_info.clone();
247-
let value =
248-
extract_value_from_buffer(batch.column(col_index), row_index, &type_info);
249-
(type_info, value)
250-
})
254+
.map(|column_data| OdbcValue::new(Arc::clone(column_data), row_index))
251255
.collect();
252256

253257
let row = OdbcRow {
254258
columns: columns.clone(),
255-
values: row_values.into_iter().map(|(_, value)| value).collect(),
259+
values: row_values,
256260
};
257261

258262
if send_row(tx, row).is_err() {
@@ -269,237 +273,11 @@ where
269273
Ok(receiver_open)
270274
}
271275

272-
fn extract_value_from_buffer(
273-
slice: AnySlice<'_>,
274-
row_index: usize,
275-
type_info: &OdbcTypeInfo,
276-
) -> OdbcValue {
277-
match slice {
278-
AnySlice::I8(s) => OdbcValue {
279-
type_info: type_info.clone(),
280-
is_null: false,
281-
text: None,
282-
blob: None,
283-
int: Some(s[row_index] as i64),
284-
float: None,
285-
},
286-
AnySlice::I16(s) => OdbcValue {
287-
type_info: type_info.clone(),
288-
is_null: false,
289-
text: None,
290-
blob: None,
291-
int: Some(s[row_index] as i64),
292-
float: None,
293-
},
294-
AnySlice::I32(s) => OdbcValue {
295-
type_info: type_info.clone(),
296-
is_null: false,
297-
text: None,
298-
blob: None,
299-
int: Some(s[row_index] as i64),
300-
float: None,
301-
},
302-
AnySlice::I64(s) => OdbcValue {
303-
type_info: type_info.clone(),
304-
is_null: false,
305-
text: None,
306-
blob: None,
307-
int: Some(s[row_index]),
308-
float: None,
309-
},
310-
AnySlice::F32(s) => OdbcValue {
311-
type_info: type_info.clone(),
312-
is_null: false,
313-
text: None,
314-
blob: None,
315-
int: None,
316-
float: Some(s[row_index] as f64),
317-
},
318-
AnySlice::F64(s) => OdbcValue {
319-
type_info: type_info.clone(),
320-
is_null: false,
321-
text: None,
322-
blob: None,
323-
int: None,
324-
float: Some(s[row_index]),
325-
},
326-
AnySlice::Bit(s) => OdbcValue {
327-
type_info: type_info.clone(),
328-
is_null: false,
329-
text: None,
330-
blob: None,
331-
int: Some(s[row_index].0 as i64),
332-
float: None,
333-
},
334-
AnySlice::Text(s) => {
335-
let text = s
336-
.get(row_index)
337-
.map(|bytes| String::from_utf8_lossy(bytes).to_string());
338-
OdbcValue {
339-
type_info: type_info.clone(),
340-
is_null: text.is_none(),
341-
text,
342-
blob: None,
343-
int: None,
344-
float: None,
345-
}
346-
}
347-
AnySlice::Binary(s) => {
348-
let blob = s.get(row_index).map(|bytes| bytes.to_vec());
349-
OdbcValue {
350-
type_info: type_info.clone(),
351-
is_null: blob.is_none(),
352-
text: None,
353-
blob,
354-
int: None,
355-
float: None,
356-
}
357-
}
358-
AnySlice::NullableI8(s) => {
359-
let (is_null, int) = if let Some(&val) = s.get(row_index) {
360-
(false, Some(val as i64))
361-
} else {
362-
(true, None)
363-
};
364-
OdbcValue {
365-
type_info: type_info.clone(),
366-
is_null,
367-
text: None,
368-
blob: None,
369-
int,
370-
float: None,
371-
}
372-
}
373-
AnySlice::NullableI16(s) => {
374-
let (is_null, int) = if let Some(&val) = s.get(row_index) {
375-
(false, Some(val as i64))
376-
} else {
377-
(true, None)
378-
};
379-
OdbcValue {
380-
type_info: type_info.clone(),
381-
is_null,
382-
text: None,
383-
blob: None,
384-
int,
385-
float: None,
386-
}
387-
}
388-
AnySlice::NullableI32(s) => {
389-
let (is_null, int) = if let Some(&val) = s.get(row_index) {
390-
(false, Some(val as i64))
391-
} else {
392-
(true, None)
393-
};
394-
OdbcValue {
395-
type_info: type_info.clone(),
396-
is_null,
397-
text: None,
398-
blob: None,
399-
int,
400-
float: None,
401-
}
402-
}
403-
AnySlice::NullableI64(s) => {
404-
let (is_null, int) = if let Some(&val) = s.get(row_index) {
405-
(false, Some(val))
406-
} else {
407-
(true, None)
408-
};
409-
OdbcValue {
410-
type_info: type_info.clone(),
411-
is_null,
412-
text: None,
413-
blob: None,
414-
int,
415-
float: None,
416-
}
417-
}
418-
AnySlice::NullableF32(s) => {
419-
let (is_null, float) = if let Some(&val) = s.get(row_index) {
420-
(false, Some(val as f64))
421-
} else {
422-
(true, None)
423-
};
424-
OdbcValue {
425-
type_info: type_info.clone(),
426-
is_null,
427-
text: None,
428-
blob: None,
429-
int: None,
430-
float,
431-
}
432-
}
433-
AnySlice::NullableF64(s) => {
434-
let (is_null, float) = if let Some(&val) = s.get(row_index) {
435-
(false, Some(val))
436-
} else {
437-
(true, None)
438-
};
439-
OdbcValue {
440-
type_info: type_info.clone(),
441-
is_null,
442-
text: None,
443-
blob: None,
444-
int: None,
445-
float,
446-
}
447-
}
448-
AnySlice::NullableBit(s) => {
449-
let (is_null, int) = if let Some(&val) = s.get(row_index) {
450-
(false, Some(val.0 as i64))
451-
} else {
452-
(true, None)
453-
};
454-
OdbcValue {
455-
type_info: type_info.clone(),
456-
is_null,
457-
text: None,
458-
blob: None,
459-
int,
460-
float: None,
461-
}
462-
}
463-
AnySlice::Date(s) => {
464-
let text = s.get(row_index).map(|date| format!("{:?}", date));
465-
OdbcValue {
466-
type_info: type_info.clone(),
467-
is_null: text.is_none(),
468-
text,
469-
blob: None,
470-
int: None,
471-
float: None,
472-
}
473-
}
474-
AnySlice::Time(s) => {
475-
let text = s.get(row_index).map(|time| format!("{:?}", time));
476-
OdbcValue {
477-
type_info: type_info.clone(),
478-
is_null: text.is_none(),
479-
text,
480-
blob: None,
481-
int: None,
482-
float: None,
483-
}
484-
}
485-
AnySlice::Timestamp(s) => {
486-
let text = s.get(row_index).map(|ts| format!("{:?}", ts));
487-
OdbcValue {
488-
type_info: type_info.clone(),
489-
is_null: text.is_none(),
490-
text,
491-
blob: None,
492-
int: None,
493-
float: None,
494-
}
495-
}
496-
_ => OdbcValue {
497-
type_info: type_info.clone(),
498-
is_null: true,
499-
text: None,
500-
blob: None,
501-
int: None,
502-
float: None,
503-
},
504-
}
276+
fn create_column_data(slice: AnySlice<'_>, type_info: &OdbcTypeInfo) -> Arc<ColumnData> {
277+
use crate::odbc::value::convert_any_slice_to_value_vec;
278+
279+
Arc::new(ColumnData {
280+
values: convert_any_slice_to_value_vec(slice),
281+
type_info: type_info.clone(),
282+
})
505283
}

sqlx-core/src/odbc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub use row::OdbcRow;
4747
pub use statement::{OdbcStatement, OdbcStatementMetadata};
4848
pub use transaction::OdbcTransactionManager;
4949
pub use type_info::{DataTypeExt, OdbcTypeInfo};
50-
pub use value::{OdbcValue, OdbcValueRef};
50+
pub use value::{ColumnData, OdbcValue, OdbcValueRef, OdbcValueType, OdbcValueVec};
5151

5252
/// An alias for [`Pool`][crate::pool::Pool], specialized for ODBC.
5353
pub type OdbcPool = crate::pool::Pool<Odbc>;

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

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::encode::Encode;
33
use crate::error::BoxDynError;
44
use crate::odbc::{DataTypeExt, Odbc, OdbcArgumentValue, OdbcTypeInfo, OdbcValueRef};
55
use crate::types::Type;
6-
use bigdecimal::{BigDecimal, FromPrimitive};
6+
use bigdecimal::BigDecimal;
77
use odbc_api::DataType;
88
use std::str::FromStr;
99

@@ -36,19 +36,22 @@ impl<'q> Encode<'q, Odbc> for BigDecimal {
3636

3737
impl<'r> Decode<'r, Odbc> for BigDecimal {
3838
fn decode(value: OdbcValueRef<'r>) -> Result<Self, BoxDynError> {
39-
if let Some(int) = value.int {
40-
return Ok(BigDecimal::from(int));
39+
if let Some(int) = value.int::<i64>() {
40+
Ok(BigDecimal::from(int))
41+
} else if let Some(float) = value.float::<f64>() {
42+
Ok(BigDecimal::try_from(float)?)
43+
} else if let Some(text) = value.text() {
44+
let text = text.trim();
45+
Ok(BigDecimal::from_str(text).map_err(|e| format!("bad decimal text: {}", e))?)
46+
} else if let Some(bytes) = value.blob() {
47+
if let Ok(s) = std::str::from_utf8(bytes) {
48+
Ok(BigDecimal::parse_bytes(s.as_bytes(), 10)
49+
.ok_or(format!("bad base10 bytes: {:?}", bytes))?)
50+
} else {
51+
Err(format!("bad utf8 bytes: {:?}", bytes).into())
52+
}
53+
} else {
54+
Err(format!("ODBC: cannot decode BigDecimal: {:?}", value).into())
4155
}
42-
if let Some(float) = value.float {
43-
return Ok(BigDecimal::from_f64(float).ok_or(format!("bad float: {}", float))?);
44-
}
45-
if let Some(text) = value.text {
46-
return Ok(BigDecimal::from_str(text).map_err(|e| format!("bad decimal text: {}", e))?);
47-
}
48-
if let Some(bytes) = value.blob {
49-
return Ok(BigDecimal::parse_bytes(bytes, 10)
50-
.ok_or(format!("bad base10 bytes: {:?}", bytes))?);
51-
}
52-
Err(format!("ODBC: cannot decode BigDecimal: {:?}", value).into())
5356
}
5457
}

0 commit comments

Comments
 (0)