sqlx_core_oldapi/odbc/
value.rs

1use crate::odbc::{Odbc, OdbcBatch, OdbcTypeInfo};
2use crate::type_info::TypeInfo;
3use crate::value::{Value, ValueRef};
4use odbc_api::buffers::{AnySlice, NullableSlice};
5use odbc_api::sys::NULL_DATA;
6use std::borrow::Cow;
7use std::sync::Arc;
8
9/// Enum containing owned column data for all supported ODBC types
10#[derive(Debug, Clone)]
11pub enum OdbcValueVec {
12    // Integer types
13    TinyInt(Vec<i8>),
14    SmallInt(Vec<i16>),
15    Integer(Vec<i32>),
16    BigInt(Vec<i64>),
17
18    // Floating point types
19    Real(Vec<f32>),
20    Double(Vec<f64>),
21
22    // Bit type
23    Bit(Vec<bool>),
24
25    // Text types (inherently nullable in ODBC)
26    Text(Vec<String>),
27
28    // Binary types (inherently nullable in ODBC)
29    Binary(Vec<Vec<u8>>),
30
31    // Date/Time types
32    Date(Vec<odbc_api::sys::Date>),
33    Time(Vec<odbc_api::sys::Time>),
34    Timestamp(Vec<odbc_api::sys::Timestamp>),
35}
36
37/// Container for column data with type information
38#[derive(Debug, Clone)]
39pub struct ColumnData {
40    pub values: OdbcValueVec,
41    pub type_info: OdbcTypeInfo,
42    pub nulls: Vec<bool>,
43}
44
45#[derive(Debug)]
46pub struct OdbcValueRef<'r> {
47    pub(crate) batch: &'r OdbcBatch,
48    pub(crate) row_index: usize,
49    pub(crate) column_index: usize,
50}
51
52#[derive(Debug, Clone)]
53pub struct OdbcValue {
54    pub(crate) batch: Arc<OdbcBatch>,
55    pub(crate) row_index: usize,
56    pub(crate) column_index: usize,
57}
58
59impl<'r> ValueRef<'r> for OdbcValueRef<'r> {
60    type Database = Odbc;
61
62    fn to_owned(&self) -> OdbcValue {
63        OdbcValue {
64            batch: Arc::new(self.batch.clone()),
65            row_index: self.row_index,
66            column_index: self.column_index,
67        }
68    }
69
70    fn type_info(&self) -> Cow<'_, OdbcTypeInfo> {
71        Cow::Borrowed(&self.batch.column_data[self.column_index].type_info)
72    }
73
74    fn is_null(&self) -> bool {
75        value_vec_is_null(&self.batch.column_data[self.column_index], self.row_index)
76    }
77}
78
79impl Value for OdbcValue {
80    type Database = Odbc;
81
82    fn as_ref(&self) -> OdbcValueRef<'_> {
83        OdbcValueRef {
84            batch: &self.batch,
85            row_index: self.row_index,
86            column_index: self.column_index,
87        }
88    }
89
90    fn type_info(&self) -> Cow<'_, OdbcTypeInfo> {
91        Cow::Borrowed(&self.batch.column_data[self.column_index].type_info)
92    }
93
94    fn is_null(&self) -> bool {
95        value_vec_is_null(&self.batch.column_data[self.column_index], self.row_index)
96    }
97}
98
99/// Utility methods for OdbcValue
100impl OdbcValue {
101    /// Create a new OdbcValue from batch, row index, and column index
102    pub fn new(batch: Arc<OdbcBatch>, row_index: usize, column_index: usize) -> Self {
103        Self {
104            batch,
105            row_index,
106            column_index,
107        }
108    }
109
110    /// Get the raw value from the column data
111    pub fn get_raw(&self) -> Option<OdbcValueType> {
112        value_vec_get_raw(&self.batch.column_data[self.column_index], self.row_index)
113    }
114
115    /// Try to get the value as i64
116    pub fn as_int<T: TryFromInt>(&self) -> Option<T> {
117        value_vec_int(&self.batch.column_data[self.column_index], self.row_index)
118    }
119
120    /// Try to get the value as f64
121    pub fn as_f64(&self) -> Option<f64> {
122        value_vec_float(&self.batch.column_data[self.column_index], self.row_index)
123    }
124
125    /// Try to get the value as string
126    pub fn as_str(&self) -> Option<Cow<'_, str>> {
127        value_vec_text(&self.batch.column_data[self.column_index], self.row_index)
128            .map(Cow::Borrowed)
129    }
130
131    /// Try to get the value as bytes
132    pub fn as_bytes(&self) -> Option<Cow<'_, [u8]>> {
133        value_vec_blob(&self.batch.column_data[self.column_index], self.row_index)
134            .map(Cow::Borrowed)
135    }
136}
137
138/// Utility methods for OdbcValueRef
139impl<'r> OdbcValueRef<'r> {
140    /// Create a new OdbcValueRef from batch, row index, and column index
141    pub fn new(batch: &'r OdbcBatch, row_index: usize, column_index: usize) -> Self {
142        Self {
143            batch,
144            row_index,
145            column_index,
146        }
147    }
148
149    /// Get the raw value from the column data
150    pub fn get_raw(&self) -> Option<OdbcValueType> {
151        value_vec_get_raw(&self.batch.column_data[self.column_index], self.row_index)
152    }
153
154    /// Try to get the value as i64
155    pub fn int<T: TryFromInt>(&self) -> Option<T> {
156        value_vec_int(&self.batch.column_data[self.column_index], self.row_index)
157    }
158
159    pub fn try_int<T: TryFromInt + crate::types::Type<Odbc>>(&self) -> crate::error::Result<T> {
160        self.int::<T>().ok_or_else(|| {
161            crate::error::Error::Decode(Box::new(crate::error::MismatchedTypeError {
162                rust_type: std::any::type_name::<T>().to_string(),
163                rust_sql_type: T::type_info().name().to_string(),
164                sql_type: self.batch.column_data[self.column_index]
165                    .type_info
166                    .name()
167                    .to_string(),
168                source: Some(format!("ODBC: cannot decode {:?}", self).into()),
169            }))
170        })
171    }
172
173    pub fn try_float<T: TryFromFloat + crate::types::Type<Odbc>>(&self) -> crate::error::Result<T> {
174        self.float::<T>().ok_or_else(|| {
175            crate::error::Error::Decode(Box::new(crate::error::MismatchedTypeError {
176                rust_type: std::any::type_name::<T>().to_string(),
177                rust_sql_type: T::type_info().name().to_string(),
178                sql_type: self.batch.column_data[self.column_index]
179                    .type_info
180                    .name()
181                    .to_string(),
182                source: Some(format!("ODBC: cannot decode {:?}", self).into()),
183            }))
184        })
185    }
186
187    /// Try to get the value as f64
188    pub fn float<T: TryFromFloat>(&self) -> Option<T> {
189        value_vec_float(&self.batch.column_data[self.column_index], self.row_index)
190    }
191
192    /// Try to get the value as string slice
193    pub fn text(&self) -> Option<&'r str> {
194        value_vec_text(&self.batch.column_data[self.column_index], self.row_index)
195    }
196
197    /// Try to get the value as binary slice
198    pub fn blob(&self) -> Option<&'r [u8]> {
199        value_vec_blob(&self.batch.column_data[self.column_index], self.row_index)
200    }
201
202    /// Try to get the raw ODBC Date value
203    pub fn date(&self) -> Option<odbc_api::sys::Date> {
204        if self.is_null() {
205            None
206        } else {
207            match &self.batch.column_data[self.column_index].values {
208                OdbcValueVec::Date(raw_values) => raw_values.get(self.row_index).copied(),
209                _ => None,
210            }
211        }
212    }
213
214    /// Try to get the raw ODBC Time value
215    pub fn time(&self) -> Option<odbc_api::sys::Time> {
216        if self.is_null() {
217            None
218        } else {
219            match &self.batch.column_data[self.column_index].values {
220                OdbcValueVec::Time(raw_values) => raw_values.get(self.row_index).copied(),
221                _ => None,
222            }
223        }
224    }
225
226    /// Try to get the raw ODBC Timestamp value
227    pub fn timestamp(&self) -> Option<odbc_api::sys::Timestamp> {
228        if self.is_null() {
229            None
230        } else {
231            match &self.batch.column_data[self.column_index].values {
232                OdbcValueVec::Timestamp(raw_values) => raw_values.get(self.row_index).copied(),
233                _ => None,
234            }
235        }
236    }
237}
238
239/// Individual ODBC value type
240#[derive(Debug, Clone)]
241pub enum OdbcValueType {
242    TinyInt(i8),
243    SmallInt(i16),
244    Integer(i32),
245    BigInt(i64),
246    Real(f32),
247    Double(f64),
248    Bit(bool),
249    Text(String),
250    Binary(Vec<u8>),
251    Date(odbc_api::sys::Date),
252    Time(odbc_api::sys::Time),
253    Timestamp(odbc_api::sys::Timestamp),
254}
255
256/// Generic helper function to handle non-nullable slices
257fn handle_non_nullable_slice<T: Copy>(
258    slice: &[T],
259    constructor: fn(Vec<T>) -> OdbcValueVec,
260) -> (OdbcValueVec, Vec<bool>) {
261    let vec = slice.to_vec();
262    (constructor(vec), vec![false; slice.len()])
263}
264
265/// Generic helper function to handle nullable slices with custom default values
266fn handle_nullable_slice<'a, T: Default + Copy>(
267    slice: NullableSlice<'a, T>,
268    constructor: fn(Vec<T>) -> OdbcValueVec,
269) -> (OdbcValueVec, Vec<bool>) {
270    let size = slice.size_hint().1.unwrap_or(0);
271    let mut values = Vec::with_capacity(size);
272    let mut nulls = Vec::with_capacity(size);
273    for opt in slice {
274        values.push(opt.copied().unwrap_or_default());
275        nulls.push(opt.is_none());
276    }
277    (constructor(values), nulls)
278}
279
280/// Generic helper function to handle nullable slices with NULL_DATA indicators
281fn handle_nullable_with_indicators<T: Default + Copy>(
282    raw_values: &[T],
283    indicators: &[isize],
284    constructor: fn(Vec<T>) -> OdbcValueVec,
285) -> (OdbcValueVec, Vec<bool>) {
286    let nulls = indicators.iter().map(|&ind| ind == NULL_DATA).collect();
287    (constructor(raw_values.to_vec()), nulls)
288}
289
290/// Convert AnySlice to owned OdbcValueVec and nulls vector, preserving original types
291pub fn convert_any_slice_to_value_vec(slice: AnySlice<'_>) -> (OdbcValueVec, Vec<bool>) {
292    match slice {
293        // Non-nullable integer types
294        AnySlice::I8(s) => handle_non_nullable_slice(s, OdbcValueVec::TinyInt),
295        AnySlice::I16(s) => handle_non_nullable_slice(s, OdbcValueVec::SmallInt),
296        AnySlice::I32(s) => handle_non_nullable_slice(s, OdbcValueVec::Integer),
297        AnySlice::I64(s) => handle_non_nullable_slice(s, OdbcValueVec::BigInt),
298
299        // Non-nullable floating point types
300        AnySlice::F32(s) => handle_non_nullable_slice(s, OdbcValueVec::Real),
301        AnySlice::F64(s) => handle_non_nullable_slice(s, OdbcValueVec::Double),
302
303        // Non-nullable other types
304        AnySlice::Bit(s) => {
305            let vec: Vec<bool> = s.iter().map(|bit| bit.as_bool()).collect();
306            (OdbcValueVec::Bit(vec), vec![false; s.len()])
307        }
308        AnySlice::Date(s) => handle_non_nullable_slice(s, OdbcValueVec::Date),
309        AnySlice::Time(s) => handle_non_nullable_slice(s, OdbcValueVec::Time),
310        AnySlice::Timestamp(s) => handle_non_nullable_slice(s, OdbcValueVec::Timestamp),
311
312        // Nullable integer types
313        AnySlice::NullableI8(s) => handle_nullable_slice(s, OdbcValueVec::TinyInt),
314        AnySlice::NullableI16(s) => handle_nullable_slice(s, OdbcValueVec::SmallInt),
315        AnySlice::NullableI32(s) => handle_nullable_slice(s, OdbcValueVec::Integer),
316        AnySlice::NullableI64(s) => handle_nullable_slice(s, OdbcValueVec::BigInt),
317        AnySlice::NullableF32(s) => handle_nullable_slice(s, OdbcValueVec::Real),
318        AnySlice::NullableF64(s) => handle_nullable_slice(s, OdbcValueVec::Double),
319        AnySlice::NullableBit(s) => {
320            let values: Vec<Option<odbc_api::Bit>> = s.map(|opt| opt.copied()).collect();
321            let nulls = values.iter().map(|opt| opt.is_none()).collect();
322            (
323                OdbcValueVec::Bit(
324                    values
325                        .into_iter()
326                        .map(|opt| opt.is_some_and(|bit| bit.as_bool()))
327                        .collect(),
328                ),
329                nulls,
330            )
331        }
332
333        // Text and binary types (inherently nullable)
334        AnySlice::Text(s) => {
335            let mut values = Vec::with_capacity(s.len());
336            let mut nulls = Vec::with_capacity(s.len());
337            for bytes_opt in s.iter() {
338                nulls.push(bytes_opt.is_none());
339                values.push(String::from_utf8_lossy(bytes_opt.unwrap_or_default()).into_owned());
340            }
341            (OdbcValueVec::Text(values), nulls)
342        }
343        AnySlice::Binary(s) => {
344            let mut values = Vec::with_capacity(s.len());
345            let mut nulls = Vec::with_capacity(s.len());
346            for bytes_opt in s.iter() {
347                nulls.push(bytes_opt.is_none());
348                values.push(bytes_opt.unwrap_or_default().to_vec());
349            }
350            (OdbcValueVec::Binary(values), nulls)
351        }
352
353        // Nullable date/time types with NULL_DATA indicators
354        AnySlice::NullableDate(s) => {
355            let (raw_values, indicators) = s.raw_values();
356            handle_nullable_with_indicators(raw_values, indicators, OdbcValueVec::Date)
357        }
358        AnySlice::NullableTime(s) => {
359            let (raw_values, indicators) = s.raw_values();
360            handle_nullable_with_indicators(raw_values, indicators, OdbcValueVec::Time)
361        }
362        AnySlice::NullableTimestamp(s) => {
363            let (raw_values, indicators) = s.raw_values();
364            handle_nullable_with_indicators(raw_values, indicators, OdbcValueVec::Timestamp)
365        }
366
367        _ => panic!("Unsupported AnySlice variant"),
368    }
369}
370
371fn value_vec_is_null(column_data: &ColumnData, row_index: usize) -> bool {
372    column_data.nulls.get(row_index).copied().unwrap_or(false)
373}
374
375macro_rules! impl_get_raw_arm_copy {
376    ($vec:expr, $row_index:expr, $variant:ident, $type:ty) => {
377        $vec.get($row_index).copied().map(OdbcValueType::$variant)
378    };
379}
380
381fn value_vec_get_raw(column_data: &ColumnData, row_index: usize) -> Option<OdbcValueType> {
382    if value_vec_is_null(column_data, row_index) {
383        return None;
384    }
385    match &column_data.values {
386        OdbcValueVec::TinyInt(v) => v.get(row_index).map(|&val| OdbcValueType::TinyInt(val)),
387        OdbcValueVec::SmallInt(v) => v.get(row_index).map(|&val| OdbcValueType::SmallInt(val)),
388        OdbcValueVec::Integer(v) => v.get(row_index).map(|&val| OdbcValueType::Integer(val)),
389        OdbcValueVec::BigInt(v) => v.get(row_index).map(|&val| OdbcValueType::BigInt(val)),
390        OdbcValueVec::Real(v) => v.get(row_index).map(|&val| OdbcValueType::Real(val)),
391        OdbcValueVec::Double(v) => v.get(row_index).map(|&val| OdbcValueType::Double(val)),
392        OdbcValueVec::Bit(v) => v.get(row_index).map(|&val| OdbcValueType::Bit(val)),
393        OdbcValueVec::Text(v) => v.get(row_index).cloned().map(OdbcValueType::Text),
394        OdbcValueVec::Binary(v) => v.get(row_index).cloned().map(OdbcValueType::Binary),
395        OdbcValueVec::Date(v) => impl_get_raw_arm_copy!(v, row_index, Date, odbc_api::sys::Date),
396        OdbcValueVec::Time(v) => impl_get_raw_arm_copy!(v, row_index, Time, odbc_api::sys::Time),
397        OdbcValueVec::Timestamp(v) => {
398            impl_get_raw_arm_copy!(v, row_index, Timestamp, odbc_api::sys::Timestamp)
399        }
400    }
401}
402
403pub trait TryFromInt:
404    TryFrom<u8>
405    + TryFrom<i16>
406    + TryFrom<i32>
407    + TryFrom<i64>
408    + TryFrom<i8>
409    + TryFrom<u16>
410    + TryFrom<u32>
411    + TryFrom<u64>
412    + std::str::FromStr
413{
414}
415
416impl<
417        T: TryFrom<u8>
418            + TryFrom<i16>
419            + TryFrom<i32>
420            + TryFrom<i64>
421            + TryFrom<i8>
422            + TryFrom<u16>
423            + TryFrom<u32>
424            + TryFrom<u64>
425            + std::str::FromStr,
426    > TryFromInt for T
427{
428}
429
430macro_rules! impl_int_conversion {
431    ($vec:expr, $row_index:expr, $type:ty) => {
432        <$type>::try_from(*$vec.get($row_index)?).ok()
433    };
434    ($vec:expr, $row_index:expr, $type:ty, text) => {
435        if let Some(Some(text)) = $vec.get($row_index) {
436            text.trim().parse().ok()
437        } else {
438            None
439        }
440    };
441}
442
443fn value_vec_int<T: TryFromInt>(column_data: &ColumnData, row_index: usize) -> Option<T> {
444    if value_vec_is_null(column_data, row_index) {
445        return None;
446    }
447    match &column_data.values {
448        OdbcValueVec::TinyInt(v) => impl_int_conversion!(v, row_index, T),
449        OdbcValueVec::SmallInt(v) => impl_int_conversion!(v, row_index, T),
450        OdbcValueVec::Integer(v) => impl_int_conversion!(v, row_index, T),
451        OdbcValueVec::BigInt(v) => impl_int_conversion!(v, row_index, T),
452        OdbcValueVec::Bit(v) => T::try_from(*v.get(row_index)? as u8).ok(),
453        OdbcValueVec::Text(v) => v.get(row_index).and_then(|text| text.trim().parse().ok()),
454        _ => None,
455    }
456}
457
458pub trait TryFromFloat: TryFrom<f32> + TryFrom<f64> {}
459
460impl<T: TryFrom<f32> + TryFrom<f64>> TryFromFloat for T {}
461
462macro_rules! impl_float_conversion {
463    ($vec:expr, $row_index:expr, $type:ty) => {
464        <$type>::try_from(*$vec.get($row_index)?).ok()
465    };
466}
467
468fn value_vec_float<T: TryFromFloat>(column_data: &ColumnData, row_index: usize) -> Option<T> {
469    if value_vec_is_null(column_data, row_index) {
470        return None;
471    }
472    match &column_data.values {
473        OdbcValueVec::Real(v) => impl_float_conversion!(v, row_index, T),
474        OdbcValueVec::Double(v) => impl_float_conversion!(v, row_index, T),
475        _ => None,
476    }
477}
478
479fn value_vec_text(column_data: &ColumnData, row_index: usize) -> Option<&str> {
480    if value_vec_is_null(column_data, row_index) {
481        return None;
482    }
483    match &column_data.values {
484        OdbcValueVec::Text(v) => v.get(row_index).map(|s| s.as_str()),
485        _ => None,
486    }
487}
488
489fn value_vec_blob(column_data: &ColumnData, row_index: usize) -> Option<&[u8]> {
490    if value_vec_is_null(column_data, row_index) {
491        return None;
492    }
493    match &column_data.values {
494        OdbcValueVec::Binary(v) => v.get(row_index).map(|b| b.as_slice()),
495        _ => None,
496    }
497}
498
499// Decode implementations have been moved to the types module
500
501#[cfg(feature = "any")]
502impl<'r> From<OdbcValueRef<'r>> for crate::any::AnyValueRef<'r> {
503    fn from(value: OdbcValueRef<'r>) -> Self {
504        crate::any::AnyValueRef {
505            type_info: crate::any::AnyTypeInfo::from(
506                value.batch.column_data[value.column_index]
507                    .type_info
508                    .clone(),
509            ),
510            kind: crate::any::value::AnyValueRefKind::Odbc(value),
511        }
512    }
513}
514
515#[cfg(feature = "any")]
516impl From<OdbcValue> for crate::any::AnyValue {
517    fn from(value: OdbcValue) -> Self {
518        crate::any::AnyValue {
519            type_info: crate::any::AnyTypeInfo::from(
520                value.batch.column_data[value.column_index]
521                    .type_info
522                    .clone(),
523            ),
524            kind: crate::any::value::AnyValueKind::Odbc(value),
525        }
526    }
527}