Skip to main content

odbc_api/buffers/
item.rs

1use odbc_sys::{Date, Time, Timestamp};
2
3use super::{AnySlice, AnySliceMut, BufferDesc, NullableSlice, NullableSliceMut};
4use crate::{BindParamDesc, Bit};
5
6/// Can either be extracted as a slice or a [`NullableSlice`] from an [`AnySlice`]. This allows
7/// the user to avoid matching on all possible variants of an [`AnySlice`] in case the
8/// buffered type is known at compile time.
9///
10/// Usually used in generic code. E.g.:
11///
12/// ```
13/// use odbc_api::{Connection, buffers::Item};
14///
15/// fn insert_tuple2_vec<A: Item, B: Item>(
16///     conn: &Connection<'_>,
17///     insert_sql: &str,
18///     source: &[(A, B)],
19/// ) {
20///     let mut prepared = conn.prepare(insert_sql).unwrap();
21///     // Number of rows submitted in one round trip
22///     let capacity = source.len();
23///     // We do not need a nullable buffer since elements of source are not optional
24///     let descriptions = [A::bind_param_desc(false), B::bind_param_desc(false)];
25///     let mut inserter = prepared.column_inserter(capacity, descriptions).unwrap();
26///     // We send everything in one go.
27///     inserter.set_num_rows(source.len());
28///     // Now let's copy the row based tuple into the columnar structure
29///     for (index, (a, b)) in source.iter().enumerate() {
30///         inserter.column_mut(0).as_slice::<A>().unwrap()[index] = *a;
31///         inserter.column_mut(1).as_slice::<B>().unwrap()[index] = *b;
32///     }
33///     inserter.execute().unwrap();
34/// }
35/// ```
36pub trait Item: Sized + Copy {
37    /// Useful to instantiate a [`crate::ColumnarBulkInserter`] in generic code.
38    fn bind_param_desc(nullable: bool) -> BindParamDesc;
39
40    /// Can be used to instantiate a [`super::ColumnarBuffer`]. This is useful to allocate the
41    /// correct buffers in generic code.
42    ///
43    /// # Example:
44    ///
45    /// Specification:
46    ///
47    /// ```
48    /// use odbc_api::buffers::{Item, BufferDesc};
49    ///
50    /// assert_eq!(BufferDesc::I64{ nullable: true }, i64::buffer_desc(true));
51    /// assert_eq!(BufferDesc::I64{ nullable: false }, i64::buffer_desc(false));
52    /// ```
53    #[deprecated(note = "Use `bind_param_desc` instead")]
54    fn buffer_desc(nullable: bool) -> BufferDesc;
55
56    /// Extract the array type from an [`AnySlice`].
57    fn as_slice(variant: AnySlice<'_>) -> Option<&[Self]>;
58    /// Extract the typed nullable buffer from an [`AnySlice`].
59    fn as_nullable_slice(variant: AnySlice<'_>) -> Option<NullableSlice<'_, Self>>;
60
61    /// Extract the array type from an [`AnySliceMut`].
62    fn as_slice_mut(variant: AnySliceMut<'_>) -> Option<&'_ mut [Self]>;
63
64    /// Extract the typed nullable buffer from an [`AnySliceMut`].
65    fn as_nullable_slice_mut(variant: AnySliceMut<'_>) -> Option<NullableSliceMut<'_, Self>>;
66}
67
68macro_rules! impl_item {
69    ($t:ident, $bind:ident, $plain:ident, $null:ident) => {
70        impl Item for $t {
71            fn bind_param_desc(nullable: bool) -> BindParamDesc {
72                BindParamDesc::$bind(nullable)
73            }
74
75            fn buffer_desc(nullable: bool) -> BufferDesc {
76                BufferDesc::$plain { nullable }
77            }
78
79            fn as_slice(variant: AnySlice<'_>) -> Option<&[Self]> {
80                match variant {
81                    AnySlice::$plain(vals) => Some(vals),
82                    _ => None,
83                }
84            }
85
86            fn as_nullable_slice(variant: AnySlice<'_>) -> Option<NullableSlice<'_, Self>> {
87                match variant {
88                    AnySlice::$null(vals) => Some(vals),
89                    _ => None,
90                }
91            }
92
93            fn as_slice_mut(variant: AnySliceMut<'_>) -> Option<&'_ mut [Self]> {
94                match variant {
95                    AnySliceMut::$plain(vals) => Some(vals),
96                    _ => None,
97                }
98            }
99
100            fn as_nullable_slice_mut(
101                variant: AnySliceMut<'_>,
102            ) -> Option<NullableSliceMut<'_, Self>> {
103                match variant {
104                    AnySliceMut::$null(vals) => Some(vals),
105                    _ => None,
106                }
107            }
108        }
109    };
110}
111
112impl_item!(f64, f64, F64, NullableF64);
113impl_item!(f32, f32, F32, NullableF32);
114impl_item!(u8, u8, U8, NullableU8);
115impl_item!(i8, i8, I8, NullableI8);
116impl_item!(i16, i16, I16, NullableI16);
117impl_item!(i32, i32, I32, NullableI32);
118impl_item!(i64, i64, I64, NullableI64);
119impl_item!(Date, date, Date, NullableDate);
120impl_item!(Bit, bit, Bit, NullableBit);
121
122/// Since buffer shapes are the same for all time/timestamp types independent of the precision and
123/// we do not know the precise SQL type. In order to still be able to bind time/timestamp buffers as
124/// input without requiring the user to separately specify the precision, we declare 100 nanosecond
125/// precision. This was the highest precision still supported by MSSQL in the tests.
126const DEFAULT_TIME_PRECISION: i16 = 7;
127
128impl Item for Time {
129    fn bind_param_desc(nullable: bool) -> BindParamDesc {
130        BindParamDesc::time(nullable, DEFAULT_TIME_PRECISION)
131    }
132
133    fn buffer_desc(nullable: bool) -> BufferDesc {
134        BufferDesc::Time { nullable }
135    }
136
137    fn as_slice(variant: AnySlice<'_>) -> Option<&[Self]> {
138        if let AnySlice::Time(vals) = variant {
139            Some(vals)
140        } else {
141            None
142        }
143    }
144
145    fn as_nullable_slice(variant: AnySlice<'_>) -> Option<NullableSlice<'_, Self>> {
146        if let AnySlice::NullableTime(vals) = variant {
147            Some(vals)
148        } else {
149            None
150        }
151    }
152
153    fn as_slice_mut(variant: AnySliceMut<'_>) -> Option<&'_ mut [Self]> {
154        if let AnySliceMut::Time(vals) = variant {
155            Some(vals)
156        } else {
157            None
158        }
159    }
160
161    fn as_nullable_slice_mut(variant: AnySliceMut<'_>) -> Option<NullableSliceMut<'_, Self>> {
162        if let AnySliceMut::NullableTime(vals) = variant {
163            Some(vals)
164        } else {
165            None
166        }
167    }
168}
169
170impl Item for Timestamp {
171    fn bind_param_desc(nullable: bool) -> BindParamDesc {
172        BindParamDesc::timestamp(nullable, DEFAULT_TIME_PRECISION)
173    }
174
175    fn buffer_desc(nullable: bool) -> BufferDesc {
176        BufferDesc::Timestamp { nullable }
177    }
178
179    fn as_slice(variant: AnySlice<'_>) -> Option<&[Self]> {
180        if let AnySlice::Timestamp(vals) = variant {
181            Some(vals)
182        } else {
183            None
184        }
185    }
186
187    fn as_nullable_slice(variant: AnySlice<'_>) -> Option<NullableSlice<'_, Self>> {
188        if let AnySlice::NullableTimestamp(vals) = variant {
189            Some(vals)
190        } else {
191            None
192        }
193    }
194
195    fn as_slice_mut(variant: AnySliceMut<'_>) -> Option<&'_ mut [Self]> {
196        if let AnySliceMut::Timestamp(vals) = variant {
197            Some(vals)
198        } else {
199            None
200        }
201    }
202
203    fn as_nullable_slice_mut(variant: AnySliceMut<'_>) -> Option<NullableSliceMut<'_, Self>> {
204        if let AnySliceMut::NullableTimestamp(vals) = variant {
205            Some(vals)
206        } else {
207            None
208        }
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use crate::{
215        BindParamDesc,
216        sys::{Date, Time, Timestamp},
217    };
218
219    use super::{Bit, Item as _};
220
221    #[test]
222    fn item_to_bind_param_desc() {
223        assert_eq!(BindParamDesc::i32(false), i32::bind_param_desc(false));
224        assert_eq!(BindParamDesc::i32(true), i32::bind_param_desc(true));
225        assert_eq!(BindParamDesc::f64(false), f64::bind_param_desc(false));
226        assert_eq!(BindParamDesc::f32(false), f32::bind_param_desc(false));
227        assert_eq!(BindParamDesc::u8(false), u8::bind_param_desc(false));
228        assert_eq!(BindParamDesc::i8(false), i8::bind_param_desc(false));
229        assert_eq!(BindParamDesc::i16(false), i16::bind_param_desc(false));
230        assert_eq!(BindParamDesc::i64(false), i64::bind_param_desc(false));
231        assert_eq!(BindParamDesc::date(false), Date::bind_param_desc(false));
232        assert_eq!(BindParamDesc::bit(false), Bit::bind_param_desc(false));
233        assert_eq!(BindParamDesc::time(false, 7), Time::bind_param_desc(false));
234        assert_eq!(
235            BindParamDesc::timestamp(false, 7),
236            Timestamp::bind_param_desc(false)
237        );
238    }
239}