tiberius/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)]
12pub struct TokenRow<'a> {
13    data: Vec<ColumnData<'a>>,
14}
15
16impl<'a> IntoIterator for TokenRow<'a> {
17    type Item = ColumnData<'a>;
18    type IntoIter = std::vec::IntoIter<Self::Item>;
19
20    fn into_iter(self) -> Self::IntoIter {
21        self.data.into_iter()
22    }
23}
24
25impl<'a> Encode<BytesMutWithDataColumns<'a>> for TokenRow<'a> {
26    fn encode(self, dst: &mut BytesMutWithDataColumns<'a>) -> crate::Result<()> {
27        dst.put_u8(TokenType::Row as u8);
28
29        if self.data.len() != dst.data_columns().len() {
30            return Err(crate::Error::BulkInput(
31                format!(
32                    "Expecting {} columns but {} were given",
33                    dst.data_columns().len(),
34                    self.data.len()
35                )
36                .into(),
37            ));
38        }
39
40        for (value, column) in self.data.into_iter().zip(dst.data_columns()) {
41            let mut dst_ti = BytesMutWithTypeInfo::new(dst).with_type_info(&column.base.ty);
42            value.encode(&mut dst_ti)?
43        }
44
45        Ok(())
46    }
47}
48
49impl<'a> TokenRow<'a> {
50    /// Creates a new empty row.
51    pub const fn new() -> Self {
52        Self { data: Vec::new() }
53    }
54
55    /// Creates a new empty row with allocated capacity.
56    pub fn with_capacity(capacity: usize) -> Self {
57        Self {
58            data: Vec::with_capacity(capacity),
59        }
60    }
61
62    /// Clears the row, removing all column values.
63    ///
64    /// Note that this method has no effect on the allocated capacity of the row.
65    pub fn clear(&mut self) {
66        self.data.clear();
67    }
68
69    /// The number of columns.
70    pub fn len(&self) -> usize {
71        self.data.len()
72    }
73
74    /// Returns an iterator over column values.
75    pub fn iter(&self) -> std::slice::Iter<'_, ColumnData<'a>> {
76        self.data.iter()
77    }
78
79    /// True if row has no columns.
80    pub fn is_empty(&self) -> bool {
81        self.data.is_empty()
82    }
83
84    /// Gets the columnar data with the given index. `None` if index out of
85    /// bounds.
86    pub fn get(&self, index: usize) -> Option<&ColumnData<'a>> {
87        self.data.get(index)
88    }
89
90    /// Adds a new value to the row.
91    pub fn push(&mut self, value: ColumnData<'a>) {
92        self.data.push(value);
93    }
94}
95
96impl TokenRow<'static> {
97    /// Normal row. We'll read the metadata what we've cached and parse columns
98    /// based on that.
99    pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
100    where
101        R: SqlReadBytes + Unpin,
102    {
103        let col_meta = src.context().last_meta().unwrap();
104
105        let mut row = Self {
106            data: Vec::with_capacity(col_meta.columns.len()),
107        };
108
109        for column in col_meta.columns.iter() {
110            let data = ColumnData::decode(src, &column.base.ty).await?;
111            row.data.push(data);
112        }
113
114        Ok(row)
115    }
116
117    /// SQL Server has packed nulls on this row type. We'll read what columns
118    /// are null from the bitmap.
119    pub(crate) async fn decode_nbc<R>(src: &mut R) -> crate::Result<Self>
120    where
121        R: SqlReadBytes + Unpin,
122    {
123        let col_meta = src.context().last_meta().unwrap();
124        let row_bitmap = RowBitmap::decode(src, col_meta.columns.len()).await?;
125
126        let mut row = Self {
127            data: Vec::with_capacity(col_meta.columns.len()),
128        };
129
130        for (i, column) in col_meta.columns.iter().enumerate() {
131            let data = if row_bitmap.is_null(i) {
132                column.base.null_value()
133            } else {
134                ColumnData::decode(src, &column.base.ty).await?
135            };
136
137            row.data.push(data);
138        }
139
140        Ok(row)
141    }
142}
143
144/// A bitmap of null values in the row. Sometimes SQL Server decides to pack the
145/// null values in the row, calling it the NBCROW. In this kind of tokens the row
146/// itself skips the null columns completely, but they can be found from the bitmap
147/// stored in the beginning of the token.
148///
149/// One byte can store eight bits of information. Bits with value of one being null.
150///
151/// If our row has eight columns, and our byte in bits is:
152///
153/// ```ignore
154/// 1 0 0 1 0 1 0 0
155/// ```
156///
157/// This would mean columns 0, 3 and 5 are null and should not be parsed at all.
158/// For more than eight columns, more bits need to be reserved for the bitmap
159/// (see the size calculation).
160struct RowBitmap {
161    data: Vec<u8>,
162}
163
164impl RowBitmap {
165    /// Is the given column index null or not.
166    #[inline]
167    fn is_null(&self, i: usize) -> bool {
168        let index = i / 8;
169        let bit = i % 8;
170
171        self.data[index] & (1 << bit) > 0
172    }
173
174    /// Decode the bitmap data from the beginning of the row. Only doable if the
175    /// type is `NbcRowToken`.
176    async fn decode<R>(src: &mut R, columns: usize) -> crate::Result<Self>
177    where
178        R: SqlReadBytes + Unpin,
179    {
180        let size = (columns + 8 - 1) / 8;
181        let mut data = vec![0; size];
182        src.read_exact(&mut data[0..size]).await?;
183
184        Ok(Self { data })
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use crate::{BaseMetaDataColumn, ColumnFlag, FixedLenType, MetaDataColumn, TypeInfo};
192    use bytes::BytesMut;
193
194    #[tokio::test]
195    async fn wrong_number_of_columns_will_fail() {
196        let row = (true, 5).into_row();
197        let columns = vec![MetaDataColumn {
198            base: BaseMetaDataColumn {
199                flags: ColumnFlag::Nullable.into(),
200                ty: TypeInfo::FixedLen(FixedLenType::Bit),
201            },
202            col_name: Default::default(),
203        }];
204        let mut buf = BytesMut::new();
205        let mut buf_with_columns = BytesMutWithDataColumns::new(&mut buf, &columns);
206
207        row.encode(&mut buf_with_columns)
208            .expect_err("wrong number of columns");
209    }
210}