odbc_api/buffers/
description.rs

1use std::mem::size_of;
2
3use odbc_sys::{Date, Time, Timestamp};
4
5use crate::{Bit, DataType};
6
7/// Describes a column of a [`crate::buffers::ColumnarBuffer`].
8///
9/// While related to to the [`crate::DataType`] of the column this is bound to, the Buffer type is
10/// different as it does not describe the type of the data source but the format the data is going
11/// to be represented in memory. While the data source is often considered to choose the buffer type
12/// the kind of processing which is supposed to be applied to the data may be even more important
13/// if choosing the a buffer for the cursor type. E.g. if you intend to print a date to standard out
14/// it may be more reasonable to bind it as `Text` rather than `Date`.
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum BufferDesc {
17    /// Variable sized binary buffer, holding up to `length` bytes per value.
18    Binary {
19        /// Maximum number of bytes per value.
20        length: usize,
21    },
22    /// Text buffer holding strings with binary length of up to `max_str_len`.
23    ///
24    /// Consider an upper bound choosing this based on the information in a [`DataType::Varchar`]
25    /// column. E.g. PostgreSQL may return a field size of several GiB for individual values if a
26    /// column is specified as `TEXT`, or Microsoft SQL Server may return `0` for a column of type
27    /// `VARCHAR(max)`. In such situations, if values are truly that large, bulk fetching data is
28    /// not recommended, but streaming individual fields one by one. Usually though, the actual
29    /// cells of the table in the database contain much shorter values. The best thing todo is to
30    /// adapt the database schema to better reflect the actual size of the values. Lacking control
31    /// over the database schema, you can always choose a smaller buffer size than initializing the
32    /// buffer in disagreement with the database schema.
33    Text {
34        /// Maximum string length. Terminating zero is excluded, i.e. memory for it will be
35        /// implicitly allocated if required.
36        max_str_len: usize,
37    },
38    /// UTF-16 encoded text buffer holding strings with length of up to `max_str_len`. Length is in
39    /// terms of 2-Byte characters.
40    WText {
41        /// Maximum string length. Terminating zero is excluded, i.e. memory for it will be
42        /// implicitly allocated if required.
43        max_str_len: usize,
44    },
45    /// 64 bit floating point
46    F64 {
47        /// This indicates whether or not the buffer will be able to represent NULL values. This will
48        /// cause an indicator buffer to be bound.
49        nullable: bool,
50    },
51    /// 32 bit floating point
52    F32 {
53        /// This indicates whether or not the buffer will be able to represent NULL values. This will
54        /// cause an indicator buffer to be bound.
55        nullable: bool,
56    },
57    /// Describes a buffer holding [`crate::sys::Date`] values.
58    Date {
59        /// This indicates whether or not the buffer will be able to represent NULL values. This will
60        /// cause an indicator buffer to be bound.
61        nullable: bool,
62    },
63    /// Describes a buffer holding [`crate::sys::Time`] values.
64    Time {
65        /// This indicates whether or not the buffer will be able to represent NULL values. This will
66        /// cause an indicator buffer to be bound.
67        nullable: bool,
68    },
69    /// Describes a buffer holding [`crate::sys::Timestamp`] values.
70    Timestamp {
71        /// This indicates whether or not the buffer will be able to represent NULL values. This will
72        /// cause an indicator buffer to be bound.
73        nullable: bool,
74    },
75    /// Signed 8 Bit integer
76    I8 {
77        /// This indicates whether or not the buffer will be able to represent NULL values. This will
78        /// cause an indicator buffer to be bound.
79        nullable: bool,
80    },
81    /// Signed 16 Bit integer
82    I16 {
83        /// This indicates whether or not the buffer will be able to represent NULL values. This will
84        /// cause an indicator buffer to be bound.
85        nullable: bool,
86    },
87    /// Signed 32 Bit integer
88    I32 {
89        /// This indicates whether or not the buffer will be able to represent NULL values. This will
90        /// cause an indicator buffer to be bound.
91        nullable: bool,
92    },
93    /// Signed 64 Bit integer
94    I64 {
95        /// This indicates whether or not the buffer will be able to represent NULL values. This will
96        /// cause an indicator buffer to be bound.
97        nullable: bool,
98    },
99    /// Unsigned 8 Bit integer
100    U8 {
101        /// This indicates whether or not the buffer will be able to represent NULL values. This will
102        /// cause an indicator buffer to be bound.
103        nullable: bool,
104    },
105    /// Can either be zero or one
106    Bit {
107        /// This indicates whether or not the buffer will be able to represent NULL values. This will
108        /// cause an indicator buffer to be bound.
109        nullable: bool,
110    },
111}
112
113impl BufferDesc {
114    pub fn from_data_type(data_type: DataType, nullable: bool) -> Option<Self> {
115        let buffer_desc = match data_type {
116            DataType::Numeric { precision, scale }
117            | DataType::Decimal { precision, scale } if scale == 0 && precision < 3 => BufferDesc::I8 { nullable },
118            DataType::Numeric { precision, scale }
119            | DataType::Decimal { precision, scale } if scale == 0 && precision < 10 => BufferDesc::I32 { nullable },
120            DataType::Numeric { precision, scale }
121            | DataType::Decimal { precision, scale } if scale == 0 && precision < 19 => BufferDesc::I64 { nullable },
122            DataType::Integer => BufferDesc::I32 { nullable },
123            DataType::SmallInt => BufferDesc::I16 { nullable },
124            DataType::Float { precision: 0..=24 } | DataType::Real => BufferDesc::F32 { nullable },
125            DataType::Float { precision: 25..=53 } |DataType::Double => BufferDesc::F64 { nullable },
126            DataType::Date => BufferDesc::Date { nullable },
127            DataType::Time { precision: 0 } => BufferDesc::Time { nullable },
128            DataType::Timestamp { precision: _ } => BufferDesc::Timestamp { nullable },
129            DataType::BigInt => BufferDesc::I64 { nullable },
130            DataType::TinyInt => BufferDesc::I8 { nullable },
131            DataType::Bit => BufferDesc::Bit { nullable },
132            DataType::Varbinary { length }
133            | DataType::Binary { length  }
134            | DataType::LongVarbinary { length } => length.map(|l| BufferDesc::Binary { length: l.get() })?,
135            DataType::Varchar { length }
136            | DataType::WVarchar { length }
137            // Currently no special buffers for fixed lengths text implemented.
138            | DataType::WChar {length }
139            | DataType::Char { length }
140            | DataType::LongVarchar { length } => {
141                length.map(|length| BufferDesc::Text { max_str_len : length.get() } )?
142            },
143            // Specialized buffers for Numeric and decimal are not yet supported.
144            | DataType::Numeric { precision: _, scale: _ }
145            | DataType::Decimal { precision: _, scale: _ }
146            | DataType::Time { precision: _ } => BufferDesc::Text { max_str_len: data_type.display_size().unwrap().get() },
147            DataType::Unknown
148            | DataType::Float { precision: _ }
149            | DataType::Other { data_type: _, column_size: _, decimal_digits: _ } => return None,
150        };
151        Some(buffer_desc)
152    }
153
154    /// Element size of buffer if bound as a columnar row. Can be used to estimate memory for
155    /// columnar bindings.
156    pub fn bytes_per_row(&self) -> usize {
157        let size_indicator = |nullable: bool| if nullable { size_of::<isize>() } else { 0 };
158        match *self {
159            BufferDesc::Binary { length } => length + size_indicator(true),
160            BufferDesc::Text { max_str_len } => max_str_len + 1 + size_indicator(true),
161            BufferDesc::WText { max_str_len } => (max_str_len + 1) * 2 + size_indicator(true),
162            BufferDesc::F64 { nullable } => size_of::<f64>() + size_indicator(nullable),
163            BufferDesc::F32 { nullable } => size_of::<f32>() + size_indicator(nullable),
164            BufferDesc::Date { nullable } => size_of::<Date>() + size_indicator(nullable),
165            BufferDesc::Time { nullable } => size_of::<Time>() + size_indicator(nullable),
166            BufferDesc::Timestamp { nullable } => size_of::<Timestamp>() + size_indicator(nullable),
167            BufferDesc::I8 { nullable } => size_of::<i8>() + size_indicator(nullable),
168            BufferDesc::I16 { nullable } => size_of::<i16>() + size_indicator(nullable),
169            BufferDesc::I32 { nullable } => size_of::<i32>() + size_indicator(nullable),
170            BufferDesc::I64 { nullable } => size_of::<i64>() + size_indicator(nullable),
171            BufferDesc::U8 { nullable } => size_of::<u8>() + size_indicator(nullable),
172            BufferDesc::Bit { nullable } => size_of::<Bit>() + size_indicator(nullable),
173        }
174    }
175}
176
177#[cfg(test)]
178mod tests {
179
180    use super::*;
181
182    #[test]
183    #[cfg(target_pointer_width = "64")] // Indicator size is platform dependent.
184    fn bytes_per_row() {
185        assert_eq!(5 + 8, BufferDesc::Binary { length: 5 }.bytes_per_row());
186        assert_eq!(
187            5 + 1 + 8,
188            BufferDesc::Text { max_str_len: 5 }.bytes_per_row()
189        );
190        assert_eq!(
191            10 + 2 + 8,
192            BufferDesc::WText { max_str_len: 5 }.bytes_per_row()
193        );
194        assert_eq!(6, BufferDesc::Date { nullable: false }.bytes_per_row());
195        assert_eq!(6, BufferDesc::Time { nullable: false }.bytes_per_row());
196        assert_eq!(
197            16,
198            BufferDesc::Timestamp { nullable: false }.bytes_per_row()
199        );
200        assert_eq!(1, BufferDesc::Bit { nullable: false }.bytes_per_row());
201        assert_eq!(1 + 8, BufferDesc::Bit { nullable: true }.bytes_per_row());
202        assert_eq!(4, BufferDesc::F32 { nullable: false }.bytes_per_row());
203        assert_eq!(8, BufferDesc::F64 { nullable: false }.bytes_per_row());
204        assert_eq!(1, BufferDesc::I8 { nullable: false }.bytes_per_row());
205        assert_eq!(2, BufferDesc::I16 { nullable: false }.bytes_per_row());
206        assert_eq!(4, BufferDesc::I32 { nullable: false }.bytes_per_row());
207        assert_eq!(8, BufferDesc::I64 { nullable: false }.bytes_per_row());
208        assert_eq!(1, BufferDesc::U8 { nullable: false }.bytes_per_row());
209    }
210}