1use crate::error::Error;
2use crate::odbc::{Odbc, OdbcBatch, OdbcTypeInfo};
3use crate::type_info::TypeInfo;
4use crate::value::{Value, ValueRef};
5use odbc_api::buffers::{AnyColumnBufferSlice, BufferDesc, NullableSlice};
6use odbc_api::handles::CDataMut;
7use odbc_api::parameter::CElement;
8use odbc_api::{DataType, Nullable};
9use std::borrow::Cow;
10use std::sync::Arc;
11
12#[derive(Debug, Clone)]
14pub enum OdbcValueVec {
15 TinyInt(Vec<i8>),
17 SmallInt(Vec<i16>),
18 Integer(Vec<i32>),
19 BigInt(Vec<i64>),
20
21 Real(Vec<f32>),
23 Double(Vec<f64>),
24
25 Bit(Vec<bool>),
27
28 Text(Vec<String>),
30
31 Binary(Vec<Vec<u8>>),
33
34 Date(Vec<odbc_api::sys::Date>),
36 Time(Vec<odbc_api::sys::Time>),
37 Timestamp(Vec<odbc_api::sys::Timestamp>),
38}
39
40impl OdbcValueVec {
41 pub(crate) fn with_capacity_for_type(data_type: DataType, capacity: usize) -> Self {
42 match data_type {
43 DataType::TinyInt => OdbcValueVec::TinyInt(Vec::with_capacity(capacity)),
44 DataType::SmallInt => OdbcValueVec::SmallInt(Vec::with_capacity(capacity)),
45 DataType::Integer | DataType::BigInt => {
47 OdbcValueVec::BigInt(Vec::with_capacity(capacity))
48 }
49 DataType::Real => OdbcValueVec::Real(Vec::with_capacity(capacity)),
50 DataType::Float { .. } | DataType::Double => {
51 OdbcValueVec::Double(Vec::with_capacity(capacity))
52 }
53 DataType::Bit => OdbcValueVec::Bit(Vec::with_capacity(capacity)),
54 DataType::Date => OdbcValueVec::Date(Vec::with_capacity(capacity)),
55 DataType::Time { .. } => OdbcValueVec::Time(Vec::with_capacity(capacity)),
56 DataType::Timestamp { .. } => OdbcValueVec::Timestamp(Vec::with_capacity(capacity)),
57 DataType::Binary { .. }
58 | DataType::Varbinary { .. }
59 | DataType::LongVarbinary { .. } => OdbcValueVec::Binary(Vec::with_capacity(capacity)),
60 _ => OdbcValueVec::Text(Vec::with_capacity(capacity)),
61 }
62 }
63
64 pub(crate) fn push_from_cursor_row(
65 &mut self,
66 cursor_row: &mut odbc_api::CursorRow<'_>,
67 col_index: u16,
68 nulls: &mut Vec<bool>,
69 ) -> Result<(), odbc_api::Error> {
70 match self {
71 OdbcValueVec::TinyInt(v) => push_get_data(cursor_row, col_index, v, nulls),
72 OdbcValueVec::SmallInt(v) => push_get_data(cursor_row, col_index, v, nulls),
73 OdbcValueVec::Integer(v) => push_get_data(cursor_row, col_index, v, nulls),
74 OdbcValueVec::BigInt(v) => push_get_data(cursor_row, col_index, v, nulls),
75 OdbcValueVec::Real(v) => push_get_data(cursor_row, col_index, v, nulls),
76 OdbcValueVec::Double(v) => push_get_data(cursor_row, col_index, v, nulls),
77 OdbcValueVec::Bit(v) => push_bit(cursor_row, col_index, v, nulls),
78 OdbcValueVec::Date(v) => push_get_data(cursor_row, col_index, v, nulls),
79 OdbcValueVec::Time(v) => push_get_data(cursor_row, col_index, v, nulls),
80 OdbcValueVec::Timestamp(v) => push_get_data(cursor_row, col_index, v, nulls),
81 OdbcValueVec::Binary(v) => push_binary(cursor_row, col_index, v, nulls),
82 OdbcValueVec::Text(v) => push_text(cursor_row, col_index, v, nulls),
83 }
84 }
85}
86
87fn push_get_data<T: Default + Copy + CElement + CDataMut>(
88 cursor_row: &mut odbc_api::CursorRow<'_>,
89 col_index: u16,
90 vec: &mut Vec<T>,
91 nulls: &mut Vec<bool>,
92) -> Result<(), odbc_api::Error>
93where
94 Nullable<T>: CElement + CDataMut,
95{
96 let mut tmp = Nullable::null();
97 cursor_row.get_data(col_index, &mut tmp)?;
98 let option = tmp.into_opt();
99 nulls.push(option.is_none());
100 vec.push(option.unwrap_or_default());
101 Ok(())
102}
103
104fn push_binary(
105 cursor_row: &mut odbc_api::CursorRow<'_>,
106 col_index: u16,
107 vec: &mut Vec<Vec<u8>>,
108 nulls: &mut Vec<bool>,
109) -> Result<(), odbc_api::Error> {
110 let mut buf = Vec::new();
111 let is_not_null = cursor_row.get_binary(col_index, &mut buf)?;
112 nulls.push(!is_not_null);
113 vec.push(buf);
114 Ok(())
115}
116
117fn push_text(
118 cursor_row: &mut odbc_api::CursorRow<'_>,
119 col_index: u16,
120 vec: &mut Vec<String>,
121 nulls: &mut Vec<bool>,
122) -> Result<(), odbc_api::Error> {
123 let mut buf = Vec::<u16>::new();
124 let is_not_null = cursor_row.get_wide_text(col_index, &mut buf)?;
125 vec.push(String::from_utf16_lossy(&buf).to_string());
126 nulls.push(!is_not_null);
127 Ok(())
128}
129
130fn push_bit(
131 cursor_row: &mut odbc_api::CursorRow<'_>,
132 col_index: u16,
133 vec: &mut Vec<bool>,
134 nulls: &mut Vec<bool>,
135) -> Result<(), odbc_api::Error> {
136 let mut bit_val = Nullable::<odbc_api::Bit>::null();
137 cursor_row.get_data(col_index, &mut bit_val)?;
138 match bit_val.into_opt() {
139 Some(bit) => {
140 nulls.push(false);
141 vec.push(bit.as_bool());
142 }
143 None => {
144 nulls.push(true);
145 vec.push(false);
146 }
147 }
148 Ok(())
149}
150
151#[derive(Debug, Clone)]
153pub struct ColumnData {
154 pub values: OdbcValueVec,
155 pub type_info: OdbcTypeInfo,
156 pub nulls: Vec<bool>,
157}
158
159#[derive(Debug)]
160pub struct OdbcValueRef<'r> {
161 pub(crate) batch: &'r OdbcBatch,
162 pub(crate) row_index: usize,
163 pub(crate) column_index: usize,
164}
165
166#[derive(Debug, Clone)]
167pub struct OdbcValue {
168 pub(crate) batch: Arc<OdbcBatch>,
169 pub(crate) row_index: usize,
170 pub(crate) column_index: usize,
171}
172
173impl<'r> ValueRef<'r> for OdbcValueRef<'r> {
174 type Database = Odbc;
175
176 fn to_owned(&self) -> OdbcValue {
177 OdbcValue {
178 batch: Arc::new(self.batch.clone()),
179 row_index: self.row_index,
180 column_index: self.column_index,
181 }
182 }
183
184 fn type_info(&self) -> Cow<'_, OdbcTypeInfo> {
185 Cow::Borrowed(&self.batch.column_data[self.column_index].type_info)
186 }
187
188 fn is_null(&self) -> bool {
189 value_vec_is_null(&self.batch.column_data[self.column_index], self.row_index)
190 }
191}
192
193impl Value for OdbcValue {
194 type Database = Odbc;
195
196 fn as_ref(&self) -> OdbcValueRef<'_> {
197 OdbcValueRef {
198 batch: &self.batch,
199 row_index: self.row_index,
200 column_index: self.column_index,
201 }
202 }
203
204 fn type_info(&self) -> Cow<'_, OdbcTypeInfo> {
205 Cow::Borrowed(&self.batch.column_data[self.column_index].type_info)
206 }
207
208 fn is_null(&self) -> bool {
209 value_vec_is_null(&self.batch.column_data[self.column_index], self.row_index)
210 }
211}
212
213impl OdbcValue {
215 pub fn new(batch: Arc<OdbcBatch>, row_index: usize, column_index: usize) -> Self {
217 Self {
218 batch,
219 row_index,
220 column_index,
221 }
222 }
223
224 pub fn get_raw(&self) -> Option<OdbcValueType> {
226 value_vec_get_raw(&self.batch.column_data[self.column_index], self.row_index)
227 }
228
229 pub fn as_int<T: TryFromInt>(&self) -> Option<T> {
231 value_vec_int(&self.batch.column_data[self.column_index], self.row_index)
232 }
233
234 pub fn as_f64(&self) -> Option<f64> {
236 value_vec_float(&self.batch.column_data[self.column_index], self.row_index)
237 }
238
239 pub fn as_str(&self) -> Option<Cow<'_, str>> {
241 value_vec_text(&self.batch.column_data[self.column_index], self.row_index)
242 .map(Cow::Borrowed)
243 }
244
245 pub fn as_bytes(&self) -> Option<Cow<'_, [u8]>> {
247 value_vec_blob(&self.batch.column_data[self.column_index], self.row_index)
248 .map(Cow::Borrowed)
249 }
250}
251
252impl<'r> OdbcValueRef<'r> {
254 pub fn new(batch: &'r OdbcBatch, row_index: usize, column_index: usize) -> Self {
256 Self {
257 batch,
258 row_index,
259 column_index,
260 }
261 }
262
263 pub fn get_raw(&self) -> Option<OdbcValueType> {
265 value_vec_get_raw(&self.batch.column_data[self.column_index], self.row_index)
266 }
267
268 pub fn int<T: TryFromInt>(&self) -> Option<T> {
270 value_vec_int(&self.batch.column_data[self.column_index], self.row_index)
271 }
272
273 pub fn try_int<T: TryFromInt + crate::types::Type<Odbc>>(&self) -> crate::error::Result<T> {
274 self.int::<T>().ok_or_else(|| {
275 crate::error::Error::Decode(Box::new(crate::error::MismatchedTypeError {
276 rust_type: std::any::type_name::<T>().to_string(),
277 rust_sql_type: T::type_info().name().to_string(),
278 sql_type: self.batch.column_data[self.column_index]
279 .type_info
280 .name()
281 .to_string(),
282 source: Some(format!("ODBC: cannot decode {:?}", self).into()),
283 }))
284 })
285 }
286
287 pub fn try_float<T: TryFromFloat + crate::types::Type<Odbc>>(&self) -> crate::error::Result<T> {
288 self.float::<T>().ok_or_else(|| {
289 crate::error::Error::Decode(Box::new(crate::error::MismatchedTypeError {
290 rust_type: std::any::type_name::<T>().to_string(),
291 rust_sql_type: T::type_info().name().to_string(),
292 sql_type: self.batch.column_data[self.column_index]
293 .type_info
294 .name()
295 .to_string(),
296 source: Some(format!("ODBC: cannot decode {:?}", self).into()),
297 }))
298 })
299 }
300
301 pub fn float<T: TryFromFloat>(&self) -> Option<T> {
303 value_vec_float(&self.batch.column_data[self.column_index], self.row_index)
304 }
305
306 pub fn text(&self) -> Option<&'r str> {
308 value_vec_text(&self.batch.column_data[self.column_index], self.row_index)
309 }
310
311 pub fn blob(&self) -> Option<&'r [u8]> {
313 value_vec_blob(&self.batch.column_data[self.column_index], self.row_index)
314 }
315
316 pub fn date(&self) -> Option<odbc_api::sys::Date> {
318 if self.is_null() {
319 None
320 } else {
321 match &self.batch.column_data[self.column_index].values {
322 OdbcValueVec::Date(raw_values) => raw_values.get(self.row_index).copied(),
323 _ => None,
324 }
325 }
326 }
327
328 pub fn time(&self) -> Option<odbc_api::sys::Time> {
330 if self.is_null() {
331 None
332 } else {
333 match &self.batch.column_data[self.column_index].values {
334 OdbcValueVec::Time(raw_values) => raw_values.get(self.row_index).copied(),
335 _ => None,
336 }
337 }
338 }
339
340 pub fn timestamp(&self) -> Option<odbc_api::sys::Timestamp> {
342 if self.is_null() {
343 None
344 } else {
345 match &self.batch.column_data[self.column_index].values {
346 OdbcValueVec::Timestamp(raw_values) => raw_values.get(self.row_index).copied(),
347 _ => None,
348 }
349 }
350 }
351}
352
353#[derive(Debug, Clone)]
355pub enum OdbcValueType {
356 TinyInt(i8),
357 SmallInt(i16),
358 Integer(i32),
359 BigInt(i64),
360 Real(f32),
361 Double(f64),
362 Bit(bool),
363 Text(String),
364 Binary(Vec<u8>),
365 Date(odbc_api::sys::Date),
366 Time(odbc_api::sys::Time),
367 Timestamp(odbc_api::sys::Timestamp),
368}
369
370fn handle_non_nullable_slice_with<T: Copy, U>(
371 slice: &[T],
372 constructor: fn(Vec<U>) -> OdbcValueVec,
373 convert: impl FnMut(T) -> U,
374) -> (OdbcValueVec, Vec<bool>) {
375 let values = slice.iter().copied().map(convert).collect();
376 (constructor(values), vec![false; slice.len()])
377}
378
379fn handle_nullable_slice_with<T: Copy, U: Default>(
380 slice: NullableSlice<'_, T>,
381 constructor: fn(Vec<U>) -> OdbcValueVec,
382 mut convert: impl FnMut(T) -> U,
383) -> (OdbcValueVec, Vec<bool>) {
384 let size = slice.size_hint().1.unwrap_or(0);
385 let mut values = Vec::with_capacity(size);
386 let mut nulls = Vec::with_capacity(size);
387 for opt in slice {
388 let is_null = opt.is_none();
389 values.push(opt.copied().map(&mut convert).unwrap_or_default());
390 nulls.push(is_null);
391 }
392 (constructor(values), nulls)
393}
394
395fn handle_buffer_slice<T: Copy + odbc_api::Pod, U: Default>(
396 slice: &AnyColumnBufferSlice<'_>,
397 desc: BufferDesc,
398 nullable: bool,
399 constructor: fn(Vec<U>) -> OdbcValueVec,
400 convert: impl FnMut(T) -> U,
401) -> Result<(OdbcValueVec, Vec<bool>), Error> {
402 if nullable {
403 Ok(handle_nullable_slice_with(
404 expect_slice(slice.as_nullable_slice::<T>(), desc)?,
405 constructor,
406 convert,
407 ))
408 } else {
409 Ok(handle_non_nullable_slice_with(
410 expect_slice(slice.as_slice::<T>(), desc)?,
411 constructor,
412 convert,
413 ))
414 }
415}
416
417fn buffer_slice_mismatch(desc: BufferDesc) -> Error {
418 Error::Protocol(format!(
419 "ODBC column buffer {desc:?} did not match fetched slice"
420 ))
421}
422
423fn expect_slice<T>(slice: Option<T>, desc: BufferDesc) -> Result<T, Error> {
424 slice.ok_or_else(|| buffer_slice_mismatch(desc))
425}
426
427fn handle_optional_values<T, U: Default>(
428 len: usize,
429 values: impl IntoIterator<Item = Option<T>>,
430 constructor: fn(Vec<U>) -> OdbcValueVec,
431 mut convert: impl FnMut(T) -> U,
432) -> (OdbcValueVec, Vec<bool>) {
433 let mut converted = Vec::with_capacity(len);
434 let mut nulls = Vec::with_capacity(len);
435
436 for value in values {
437 let is_null = value.is_none();
438 converted.push(value.map(&mut convert).unwrap_or_default());
439 nulls.push(is_null);
440 }
441
442 (constructor(converted), nulls)
443}
444
445pub(crate) fn convert_dyn_slice_to_value_vec(
447 slice: AnyColumnBufferSlice<'_>,
448 desc: BufferDesc,
449) -> Result<(OdbcValueVec, Vec<bool>), Error> {
450 Ok(match desc {
451 BufferDesc::I8 { nullable } => {
452 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::TinyInt, |value| value)?
453 }
454 BufferDesc::I16 { nullable } => {
455 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::SmallInt, |value| {
456 value
457 })?
458 }
459 BufferDesc::I32 { nullable } => {
460 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::Integer, |value| value)?
461 }
462 BufferDesc::I64 { nullable } => {
463 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::BigInt, |value| value)?
464 }
465 BufferDesc::U8 { nullable } => {
466 handle_buffer_slice::<u8, i64>(&slice, desc, nullable, OdbcValueVec::BigInt, i64::from)?
467 }
468 BufferDesc::F32 { nullable } => {
469 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::Real, |value| value)?
470 }
471 BufferDesc::F64 { nullable } => {
472 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::Double, |value| value)?
473 }
474 BufferDesc::Bit { nullable } => handle_buffer_slice(
475 &slice,
476 desc,
477 nullable,
478 OdbcValueVec::Bit,
479 |value: odbc_api::Bit| value.as_bool(),
480 )?,
481 BufferDesc::Date { nullable } => {
482 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::Date, |value| value)?
483 }
484 BufferDesc::Time { nullable } => {
485 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::Time, |value| value)?
486 }
487 BufferDesc::Timestamp { nullable } => {
488 handle_buffer_slice(&slice, desc, nullable, OdbcValueVec::Timestamp, |value| {
489 value
490 })?
491 }
492 BufferDesc::Text { .. } => {
493 let s = expect_slice(slice.as_text(), desc)?;
494 handle_optional_values(s.len(), s.iter(), OdbcValueVec::Text, |bytes| {
495 String::from_utf8_lossy(bytes).into_owned()
496 })
497 }
498 BufferDesc::WText { .. } => {
499 let s = expect_slice(slice.as_wide_text(), desc)?;
500 handle_optional_values(s.len(), s.iter(), OdbcValueVec::Text, |chars| {
501 String::from_utf16_lossy(chars.into())
502 })
503 }
504 BufferDesc::Binary { .. } => {
505 let s = expect_slice(slice.as_binary(), desc)?;
506 handle_optional_values(s.len(), s.iter(), OdbcValueVec::Binary, |bytes| {
507 bytes.to_vec()
508 })
509 }
510 BufferDesc::Numeric => {
511 return Err(Error::Protocol(format!(
512 "unsupported ODBC buffer descriptor: {desc:?}"
513 )));
514 }
515 })
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 #[test]
523 fn converts_unsigned_tinyint_slices() {
524 let (values, nulls) =
525 handle_non_nullable_slice_with(&[0, 255], OdbcValueVec::BigInt, i64::from);
526
527 assert_eq!(nulls, vec![false, false]);
528 assert!(matches!(values, OdbcValueVec::BigInt(values) if values == vec![0, 255]));
529 }
530}
531
532fn value_vec_is_null(column_data: &ColumnData, row_index: usize) -> bool {
533 column_data.nulls.get(row_index).copied().unwrap_or(false)
534}
535
536macro_rules! impl_get_raw_arm_copy {
537 ($vec:expr, $row_index:expr, $variant:ident, $type:ty) => {
538 $vec.get($row_index).copied().map(OdbcValueType::$variant)
539 };
540}
541
542fn value_vec_get_raw(column_data: &ColumnData, row_index: usize) -> Option<OdbcValueType> {
543 if value_vec_is_null(column_data, row_index) {
544 return None;
545 }
546 match &column_data.values {
547 OdbcValueVec::TinyInt(v) => v.get(row_index).map(|&val| OdbcValueType::TinyInt(val)),
548 OdbcValueVec::SmallInt(v) => v.get(row_index).map(|&val| OdbcValueType::SmallInt(val)),
549 OdbcValueVec::Integer(v) => v.get(row_index).map(|&val| OdbcValueType::Integer(val)),
550 OdbcValueVec::BigInt(v) => v.get(row_index).map(|&val| OdbcValueType::BigInt(val)),
551 OdbcValueVec::Real(v) => v.get(row_index).map(|&val| OdbcValueType::Real(val)),
552 OdbcValueVec::Double(v) => v.get(row_index).map(|&val| OdbcValueType::Double(val)),
553 OdbcValueVec::Bit(v) => v.get(row_index).map(|&val| OdbcValueType::Bit(val)),
554 OdbcValueVec::Text(v) => v.get(row_index).cloned().map(OdbcValueType::Text),
555 OdbcValueVec::Binary(v) => v.get(row_index).cloned().map(OdbcValueType::Binary),
556 OdbcValueVec::Date(v) => impl_get_raw_arm_copy!(v, row_index, Date, odbc_api::sys::Date),
557 OdbcValueVec::Time(v) => impl_get_raw_arm_copy!(v, row_index, Time, odbc_api::sys::Time),
558 OdbcValueVec::Timestamp(v) => {
559 impl_get_raw_arm_copy!(v, row_index, Timestamp, odbc_api::sys::Timestamp)
560 }
561 }
562}
563
564pub trait TryFromInt:
565 TryFrom<u8>
566 + TryFrom<i16>
567 + TryFrom<i32>
568 + TryFrom<i64>
569 + TryFrom<i8>
570 + TryFrom<u16>
571 + TryFrom<u32>
572 + TryFrom<u64>
573 + std::str::FromStr
574{
575}
576
577impl<
578 T: TryFrom<u8>
579 + TryFrom<i16>
580 + TryFrom<i32>
581 + TryFrom<i64>
582 + TryFrom<i8>
583 + TryFrom<u16>
584 + TryFrom<u32>
585 + TryFrom<u64>
586 + std::str::FromStr,
587 > TryFromInt for T
588{
589}
590
591macro_rules! impl_int_conversion {
592 ($vec:expr, $row_index:expr, $type:ty) => {
593 <$type>::try_from(*$vec.get($row_index)?).ok()
594 };
595 ($vec:expr, $row_index:expr, $type:ty, text) => {
596 if let Some(Some(text)) = $vec.get($row_index) {
597 text.trim().parse().ok()
598 } else {
599 None
600 }
601 };
602}
603
604fn value_vec_int<T: TryFromInt>(column_data: &ColumnData, row_index: usize) -> Option<T> {
605 if value_vec_is_null(column_data, row_index) {
606 return None;
607 }
608 match &column_data.values {
609 OdbcValueVec::TinyInt(v) => impl_int_conversion!(v, row_index, T),
610 OdbcValueVec::SmallInt(v) => impl_int_conversion!(v, row_index, T),
611 OdbcValueVec::Integer(v) => impl_int_conversion!(v, row_index, T),
612 OdbcValueVec::BigInt(v) => impl_int_conversion!(v, row_index, T),
613 OdbcValueVec::Bit(v) => T::try_from(*v.get(row_index)? as u8).ok(),
614 OdbcValueVec::Text(v) => v.get(row_index).and_then(|text| text.trim().parse().ok()),
615 _ => None,
616 }
617}
618
619pub trait TryFromFloat: TryFrom<f32> + TryFrom<f64> {}
620
621impl<T: TryFrom<f32> + TryFrom<f64>> TryFromFloat for T {}
622
623macro_rules! impl_float_conversion {
624 ($vec:expr, $row_index:expr, $type:ty) => {
625 <$type>::try_from(*$vec.get($row_index)?).ok()
626 };
627}
628
629fn value_vec_float<T: TryFromFloat>(column_data: &ColumnData, row_index: usize) -> Option<T> {
630 if value_vec_is_null(column_data, row_index) {
631 return None;
632 }
633 match &column_data.values {
634 OdbcValueVec::Real(v) => impl_float_conversion!(v, row_index, T),
635 OdbcValueVec::Double(v) => impl_float_conversion!(v, row_index, T),
636 _ => None,
637 }
638}
639
640fn value_vec_text(column_data: &ColumnData, row_index: usize) -> Option<&str> {
641 if value_vec_is_null(column_data, row_index) {
642 return None;
643 }
644 match &column_data.values {
645 OdbcValueVec::Text(v) => v.get(row_index).map(|s| s.as_str()),
646 _ => None,
647 }
648}
649
650fn value_vec_blob(column_data: &ColumnData, row_index: usize) -> Option<&[u8]> {
651 if value_vec_is_null(column_data, row_index) {
652 return None;
653 }
654 match &column_data.values {
655 OdbcValueVec::Binary(v) => v.get(row_index).map(|b| b.as_slice()),
656 _ => None,
657 }
658}
659
660#[cfg(feature = "any")]
661impl<'r> From<OdbcValueRef<'r>> for crate::any::AnyValueRef<'r> {
662 fn from(value: OdbcValueRef<'r>) -> Self {
663 crate::any::AnyValueRef {
664 type_info: crate::any::AnyTypeInfo::from(
665 value.batch.column_data[value.column_index]
666 .type_info
667 .clone(),
668 ),
669 kind: crate::any::value::AnyValueRefKind::Odbc(value),
670 }
671 }
672}
673
674#[cfg(feature = "any")]
675impl From<OdbcValue> for crate::any::AnyValue {
676 fn from(value: OdbcValue) -> Self {
677 crate::any::AnyValue {
678 type_info: crate::any::AnyTypeInfo::from(
679 value.batch.column_data[value.column_index]
680 .type_info
681 .clone(),
682 ),
683 kind: crate::any::value::AnyValueKind::Odbc(value),
684 }
685 }
686}