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}