Skip to main content

tds_driver/tds/codec/token/
token_row.rs

1mod bytes_mut_with_data_columns;
2mod into_row;
3use crate::tds::codec::encode::Encode;
4use crate::{tds::codec::ColumnData, BytesMutWithTypeInfo, SqlReadBytes, TokenType};
5use bytes::BufMut;
6pub(crate) use bytes_mut_with_data_columns::BytesMutWithDataColumns;
7use futures_util::io::AsyncReadExt;
8pub use into_row::IntoRow;
9
10/// A row of data.
11#[derive(Debug, Default, Clone)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct TokenRow<'a> {
14    data: Vec<ColumnData<'a>>,
15}
16
17impl<'a> IntoIterator for TokenRow<'a> {
18    type Item = ColumnData<'a>;
19    type IntoIter = std::vec::IntoIter<Self::Item>;
20
21    fn into_iter(self) -> Self::IntoIter {
22        self.data.into_iter()
23    }
24}
25
26impl<'a> Encode<BytesMutWithDataColumns<'a>> for TokenRow<'a> {
27    fn encode(self, dst: &mut BytesMutWithDataColumns<'a>) -> crate::Result<()> {
28        dst.put_u8(TokenType::Row as u8);
29
30        if self.data.len() != dst.data_columns().len() {
31            return Err(crate::Error::BulkInput(
32                format!(
33                    "Expecting {} columns but {} were given",
34                    dst.data_columns().len(),
35                    self.data.len()
36                )
37                .into(),
38            ));
39        }
40
41        for (value, column) in self.data.into_iter().zip(dst.data_columns()) {
42            let mut dst_ti = BytesMutWithTypeInfo::new(dst).with_type_info(&column.base.ty);
43            value.encode(&mut dst_ti)?
44        }
45
46        Ok(())
47    }
48}
49
50impl<'a> TokenRow<'a> {
51    /// Creates a new empty row.
52    pub const fn new() -> Self {
53        Self { data: Vec::new() }
54    }
55
56    /// Creates a new empty row with allocated capacity.
57    pub fn with_capacity(capacity: usize) -> Self {
58        Self {
59            data: Vec::with_capacity(capacity),
60        }
61    }
62
63    /// Clears the row, removing all column values.
64    ///
65    /// Note that this method has no effect on the allocated capacity of the row.
66    pub fn clear(&mut self) {
67        self.data.clear();
68    }
69
70    /// The number of columns.
71    pub fn len(&self) -> usize {
72        self.data.len()
73    }
74
75    /// Returns an iterator over column values.
76    pub fn iter(&self) -> std::slice::Iter<'_, ColumnData<'a>> {
77        self.data.iter()
78    }
79
80    /// True if row has no columns.
81    pub fn is_empty(&self) -> bool {
82        self.data.is_empty()
83    }
84
85    /// Gets the columnar data with the given index. `None` if index out of
86    /// bounds.
87    pub fn get(&self, index: usize) -> Option<&ColumnData<'a>> {
88        self.data.get(index)
89    }
90
91    /// Adds a new value to the row.
92    pub fn push(&mut self, value: ColumnData<'a>) {
93        self.data.push(value);
94    }
95}
96
97impl TokenRow<'static> {
98    /// Normal row. We'll read the metadata what we've cached and parse columns
99    /// based on that.
100    pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
101    where
102        R: SqlReadBytes + Unpin,
103    {
104        let col_meta = src.context().last_meta().ok_or_else(|| {
105            crate::Error::Protocol("ROW token arrived before any COLMETADATA".into())
106        })?;
107
108        let mut row = Self {
109            data: Vec::with_capacity(col_meta.columns.len()),
110        };
111
112        for column in col_meta.columns.iter() {
113            let data = ColumnData::decode(src, &column.base.ty).await?;
114            row.data.push(data);
115        }
116
117        Ok(row)
118    }
119
120    /// SQL Server has packed nulls on this row type. We'll read what columns
121    /// are null from the bitmap.
122    pub(crate) async fn decode_nbc<R>(src: &mut R) -> crate::Result<Self>
123    where
124        R: SqlReadBytes + Unpin,
125    {
126        let col_meta = src.context().last_meta().ok_or_else(|| {
127            crate::Error::Protocol("NBCROW token arrived before any COLMETADATA".into())
128        })?;
129        let row_bitmap = RowBitmap::decode(src, col_meta.columns.len()).await?;
130
131        let mut row = Self {
132            data: Vec::with_capacity(col_meta.columns.len()),
133        };
134
135        for (i, column) in col_meta.columns.iter().enumerate() {
136            let data = if row_bitmap.is_null(i) {
137                column.base.null_value()
138            } else {
139                ColumnData::decode(src, &column.base.ty).await?
140            };
141
142            row.data.push(data);
143        }
144
145        Ok(row)
146    }
147}
148
149/// A bitmap of null values in the row. Sometimes SQL Server decides to pack the
150/// null values in the row, calling it the NBCROW. In this kind of tokens the row
151/// itself skips the null columns completely, but they can be found from the bitmap
152/// stored in the beginning of the token.
153///
154/// One byte can store eight bits of information. Bits with value of one being null.
155///
156/// If our row has eight columns, and our byte in bits is:
157///
158/// ```ignore
159/// 1 0 0 1 0 1 0 0
160/// ```
161///
162/// This would mean columns 0, 3 and 5 are null and should not be parsed at all.
163/// For more than eight columns, more bits need to be reserved for the bitmap
164/// (see the size calculation).
165enum RowBitmap {
166    /// Up to 256 columns, so most rows don't allocate.
167    Inline([u8; 32]),
168    Heap(Vec<u8>),
169}
170
171impl RowBitmap {
172    /// Is the given column index null or not.
173    #[inline]
174    fn is_null(&self, i: usize) -> bool {
175        let data = match self {
176            Self::Inline(data) => &data[..],
177            Self::Heap(data) => &data[..],
178        };
179
180        data[i / 8] & (1 << (i % 8)) > 0
181    }
182
183    /// Decode the bitmap data from the beginning of the row. Only doable if the
184    /// type is `NbcRowToken`.
185    async fn decode<R>(src: &mut R, columns: usize) -> crate::Result<Self>
186    where
187        R: SqlReadBytes + Unpin,
188    {
189        let size = columns.div_ceil(8);
190        let mut bitmap = if size <= 32 {
191            Self::Inline([0; 32])
192        } else {
193            Self::Heap(vec![0; size])
194        };
195        let data = match &mut bitmap {
196            Self::Inline(data) => &mut data[..size],
197            Self::Heap(data) => &mut data[..],
198        };
199        src.read_exact(data).await?;
200
201        Ok(bitmap)
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use crate::{BaseMetaDataColumn, ColumnFlag, FixedLenType, MetaDataColumn, TypeInfo};
209    use bytes::BytesMut;
210
211    #[tokio::test]
212    async fn wrong_number_of_columns_will_fail() {
213        let row = (true, 5).into_row();
214        let columns = vec![MetaDataColumn {
215            base: BaseMetaDataColumn {
216                flags: ColumnFlag::Nullable.into(),
217                ty: TypeInfo::FixedLen(FixedLenType::Bit),
218            },
219            col_name: Default::default(),
220        }];
221        let mut buf = BytesMut::new();
222        let mut buf_with_columns = BytesMutWithDataColumns::new(&mut buf, &columns);
223
224        row.encode(&mut buf_with_columns)
225            .expect_err("wrong number of columns");
226    }
227
228    #[tokio::test]
229    async fn row_before_colmetadata_is_protocol_error() {
230        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
231        // No COLMETADATA has been seen, so last_meta() is None: decoding a ROW
232        // must be a protocol error rather than an unwrap() panic.
233        let buf = BytesMut::new();
234        let err = TokenRow::decode(&mut buf.into_sql_read_bytes())
235            .await
236            .expect_err("ROW before COLMETADATA must error");
237        assert!(matches!(err, crate::Error::Protocol(_)));
238    }
239
240    #[test]
241    fn basic_container_operations() {
242        let mut row = TokenRow::new();
243        assert!(row.is_empty());
244        assert_eq!(row.len(), 0);
245        assert_eq!(row.get(0), None);
246
247        row.push(ColumnData::I32(Some(1)));
248        row.push(ColumnData::I32(Some(2)));
249        assert_eq!(row.len(), 2);
250        assert!(!row.is_empty());
251        assert_eq!(row.get(0), Some(&ColumnData::I32(Some(1))));
252        assert_eq!(row.get(5), None);
253
254        let collected: Vec<_> = row.iter().collect();
255        assert_eq!(collected.len(), 2);
256
257        row.clear();
258        assert!(row.is_empty());
259
260        let with_cap = TokenRow::with_capacity(4);
261        assert!(with_cap.is_empty());
262    }
263
264    #[test]
265    fn with_capacity_preallocates() {
266        // with_capacity must actually reserve room; Default::default() would
267        // give a zero-capacity vec.
268        let row = TokenRow::with_capacity(16);
269        assert!(row.is_empty());
270        assert!(row.data.capacity() >= 16);
271    }
272
273    #[test]
274    fn row_bitmap_is_null_checks_correct_bit() {
275        // Only bit 3 is set in the single bitmap byte. is_null must consult that
276        // exact bit; a `<<`->`>>` mutation would look at bit -3 (i.e. 0) and
277        // report the wrong columns.
278        let mut inline = [0; 32];
279        inline[0] = 0b0000_1000;
280        for bitmap in [
281            RowBitmap::Inline(inline),
282            RowBitmap::Heap(vec![0b0000_1000]),
283        ] {
284            assert!(bitmap.is_null(3));
285            assert!(!bitmap.is_null(0));
286            assert!(!bitmap.is_null(1));
287            assert!(!bitmap.is_null(2));
288            assert!(!bitmap.is_null(4));
289        }
290    }
291
292    #[tokio::test]
293    async fn row_bitmap_over_256_columns_is_read_whole() {
294        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
295
296        let mut bytes = [0u8; 38];
297        bytes[37] = 0b0000_1000; // column 299
298        let mut src = bytes::BytesMut::from(&bytes[..]).into_sql_read_bytes();
299        let bitmap = RowBitmap::decode(&mut src, 300).await.unwrap();
300
301        assert!(matches!(bitmap, RowBitmap::Heap(_)));
302        assert!(bitmap.is_null(299));
303        assert!(!bitmap.is_null(298));
304    }
305
306    #[test]
307    fn into_iter_yields_owned_values() {
308        let mut row = TokenRow::new();
309        row.push(ColumnData::I32(Some(1)));
310        row.push(ColumnData::I32(Some(2)));
311
312        let values: Vec<_> = row.into_iter().collect();
313        assert_eq!(
314            values,
315            vec![ColumnData::I32(Some(1)), ColumnData::I32(Some(2))]
316        );
317    }
318
319    #[tokio::test]
320    async fn encode_matching_columns_round_trip() {
321        let row = (true, 5i32).into_row();
322        let columns = vec![
323            MetaDataColumn {
324                base: BaseMetaDataColumn {
325                    flags: ColumnFlag::Nullable.into(),
326                    ty: TypeInfo::FixedLen(FixedLenType::Bit),
327                },
328                col_name: Default::default(),
329            },
330            MetaDataColumn {
331                base: BaseMetaDataColumn {
332                    flags: ColumnFlag::Nullable.into(),
333                    ty: TypeInfo::FixedLen(FixedLenType::Int4),
334                },
335                col_name: Default::default(),
336            },
337        ];
338        let mut buf = BytesMut::new();
339        let mut buf_with_columns = BytesMutWithDataColumns::new(&mut buf, &columns);
340
341        row.encode(&mut buf_with_columns).unwrap();
342        assert!(!buf.is_empty());
343    }
344
345    #[tokio::test]
346    async fn decode_reads_columns_from_cached_meta() {
347        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
348        use crate::tds::codec::TokenColMetaData;
349        use std::sync::Arc;
350
351        let col_meta = TokenColMetaData {
352            columns: vec![MetaDataColumn {
353                base: BaseMetaDataColumn {
354                    flags: ColumnFlag::Nullable.into(),
355                    ty: TypeInfo::FixedLen(FixedLenType::Int4),
356                },
357                col_name: Default::default(),
358            }],
359        };
360
361        let mut buf = BytesMut::new();
362        buf.put_i32_le(42);
363
364        let mut reader = buf.into_sql_read_bytes();
365        reader.context_mut().set_last_meta(Arc::new(col_meta));
366
367        let row = TokenRow::decode(&mut reader).await.unwrap();
368        assert_eq!(row.len(), 1);
369        assert_eq!(row.get(0), Some(&ColumnData::I32(Some(42))));
370    }
371
372    #[tokio::test]
373    async fn decode_nbc_before_colmetadata_is_protocol_error() {
374        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
375
376        let buf = BytesMut::new();
377        let err = TokenRow::decode_nbc(&mut buf.into_sql_read_bytes())
378            .await
379            .expect_err("NBCROW before COLMETADATA must error");
380        assert!(matches!(err, crate::Error::Protocol(_)));
381    }
382
383    #[tokio::test]
384    async fn decode_nbc_uses_bitmap_for_nulls() {
385        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
386        use crate::tds::codec::TokenColMetaData;
387        use std::sync::Arc;
388
389        // Two int columns: first null (bit 0 set), second present (value 7).
390        let col_meta = TokenColMetaData {
391            columns: vec![
392                MetaDataColumn {
393                    base: BaseMetaDataColumn {
394                        flags: ColumnFlag::Nullable.into(),
395                        ty: TypeInfo::FixedLen(FixedLenType::Int4),
396                    },
397                    col_name: Default::default(),
398                },
399                MetaDataColumn {
400                    base: BaseMetaDataColumn {
401                        flags: ColumnFlag::Nullable.into(),
402                        ty: TypeInfo::FixedLen(FixedLenType::Int4),
403                    },
404                    col_name: Default::default(),
405                },
406            ],
407        };
408
409        let mut buf = BytesMut::new();
410        buf.put_u8(0b0000_0001); // bitmap: column 0 is null
411        buf.put_i32_le(7); // column 1's value
412
413        let mut reader = buf.into_sql_read_bytes();
414        reader.context_mut().set_last_meta(Arc::new(col_meta));
415
416        let row = TokenRow::decode_nbc(&mut reader).await.unwrap();
417        assert_eq!(row.len(), 2);
418        assert_eq!(row.get(0), Some(&ColumnData::I32(None)));
419        assert_eq!(row.get(1), Some(&ColumnData::I32(Some(7))));
420    }
421}