Skip to main content

sqlx_core_oldapi/odbc/
value.rs

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