odbc_api/buffers/item.rs
1use odbc_sys::{Date, Time, Timestamp};
2
3use super::BufferDesc;
4use crate::{BindParamDesc, Bit, Pod};
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: Pod {
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 fn buffer_desc(nullable: bool) -> BufferDesc;
54}
55
56macro_rules! impl_item {
57 ($t:ident, $bind:ident, $plain:ident, $null:ident) => {
58 impl Item for $t {
59 fn bind_param_desc(nullable: bool) -> BindParamDesc {
60 BindParamDesc::$bind(nullable)
61 }
62
63 fn buffer_desc(nullable: bool) -> BufferDesc {
64 BufferDesc::$plain { nullable }
65 }
66 }
67 };
68}
69
70impl_item!(f64, f64, F64, NullableF64);
71impl_item!(f32, f32, F32, NullableF32);
72impl_item!(u8, u8, U8, NullableU8);
73impl_item!(i8, i8, I8, NullableI8);
74impl_item!(i16, i16, I16, NullableI16);
75impl_item!(i32, i32, I32, NullableI32);
76impl_item!(i64, i64, I64, NullableI64);
77impl_item!(Date, date, Date, NullableDate);
78impl_item!(Time, time, Time, NullableTime);
79impl_item!(Bit, bit, Bit, NullableBit);
80
81/// Since buffer shapes are the same for all time/timestamp types independent of the precision and
82/// we do not know the precise SQL type. In order to still be able to bind time/timestamp buffers as
83/// input without requiring the user to separately specify the precision, we declare 100 nanosecond
84/// precision. This was the highest precision still supported by MSSQL in the tests.
85const DEFAULT_TIME_PRECISION: i16 = 7;
86
87impl Item for Timestamp {
88 fn bind_param_desc(nullable: bool) -> BindParamDesc {
89 BindParamDesc::timestamp(nullable, DEFAULT_TIME_PRECISION)
90 }
91
92 fn buffer_desc(nullable: bool) -> BufferDesc {
93 BufferDesc::Timestamp { nullable }
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use crate::{
100 BindParamDesc,
101 buffers::item::DEFAULT_TIME_PRECISION,
102 sys::{Date, Time, Timestamp},
103 };
104
105 use super::{Bit, Item as _};
106
107 #[test]
108 fn item_to_bind_param_desc() {
109 assert_eq!(BindParamDesc::i32(false), i32::bind_param_desc(false));
110 assert_eq!(BindParamDesc::i32(true), i32::bind_param_desc(true));
111 assert_eq!(BindParamDesc::f64(false), f64::bind_param_desc(false));
112 assert_eq!(BindParamDesc::f32(false), f32::bind_param_desc(false));
113 assert_eq!(BindParamDesc::u8(false), u8::bind_param_desc(false));
114 assert_eq!(BindParamDesc::i8(false), i8::bind_param_desc(false));
115 assert_eq!(BindParamDesc::i16(false), i16::bind_param_desc(false));
116 assert_eq!(BindParamDesc::i64(false), i64::bind_param_desc(false));
117 assert_eq!(BindParamDesc::date(false), Date::bind_param_desc(false));
118 assert_eq!(BindParamDesc::bit(false), Bit::bind_param_desc(false));
119 assert_eq!(BindParamDesc::time(false), Time::bind_param_desc(false));
120 assert_eq!(
121 BindParamDesc::timestamp(false, DEFAULT_TIME_PRECISION),
122 Timestamp::bind_param_desc(false)
123 );
124 }
125}