tiberius/tds/codec/token/
token_row.rs1mod 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#[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 pub const fn new() -> Self {
53 Self { data: Vec::new() }
54 }
55
56 pub fn with_capacity(capacity: usize) -> Self {
58 Self {
59 data: Vec::with_capacity(capacity),
60 }
61 }
62
63 pub fn clear(&mut self) {
67 self.data.clear();
68 }
69
70 pub fn len(&self) -> usize {
72 self.data.len()
73 }
74
75 pub fn iter(&self) -> std::slice::Iter<'_, ColumnData<'a>> {
77 self.data.iter()
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.data.is_empty()
83 }
84
85 pub fn get(&self, index: usize) -> Option<&ColumnData<'a>> {
88 self.data.get(index)
89 }
90
91 pub fn push(&mut self, value: ColumnData<'a>) {
93 self.data.push(value);
94 }
95}
96
97impl TokenRow<'static> {
98 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 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
149struct RowBitmap {
166 data: Vec<u8>,
167}
168
169impl RowBitmap {
170 #[inline]
172 fn is_null(&self, i: usize) -> bool {
173 let index = i / 8;
174 let bit = i % 8;
175
176 self.data[index] & (1 << bit) > 0
177 }
178
179 async fn decode<R>(src: &mut R, columns: usize) -> crate::Result<Self>
182 where
183 R: SqlReadBytes + Unpin,
184 {
185 let size = columns.div_ceil(8);
186 let mut data = vec![0; size];
187 src.read_exact(&mut data[0..size]).await?;
188
189 Ok(Self { data })
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use crate::{BaseMetaDataColumn, ColumnFlag, FixedLenType, MetaDataColumn, TypeInfo};
197 use bytes::BytesMut;
198
199 #[tokio::test]
200 async fn wrong_number_of_columns_will_fail() {
201 let row = (true, 5).into_row();
202 let columns = vec![MetaDataColumn {
203 base: BaseMetaDataColumn {
204 flags: ColumnFlag::Nullable.into(),
205 ty: TypeInfo::FixedLen(FixedLenType::Bit),
206 },
207 col_name: Default::default(),
208 }];
209 let mut buf = BytesMut::new();
210 let mut buf_with_columns = BytesMutWithDataColumns::new(&mut buf, &columns);
211
212 row.encode(&mut buf_with_columns)
213 .expect_err("wrong number of columns");
214 }
215
216 #[tokio::test]
217 async fn row_before_colmetadata_is_protocol_error() {
218 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
219 let buf = BytesMut::new();
222 let err = TokenRow::decode(&mut buf.into_sql_read_bytes())
223 .await
224 .expect_err("ROW before COLMETADATA must error");
225 assert!(matches!(err, crate::Error::Protocol(_)));
226 }
227
228 #[test]
229 fn basic_container_operations() {
230 let mut row = TokenRow::new();
231 assert!(row.is_empty());
232 assert_eq!(row.len(), 0);
233 assert_eq!(row.get(0), None);
234
235 row.push(ColumnData::I32(Some(1)));
236 row.push(ColumnData::I32(Some(2)));
237 assert_eq!(row.len(), 2);
238 assert!(!row.is_empty());
239 assert_eq!(row.get(0), Some(&ColumnData::I32(Some(1))));
240 assert_eq!(row.get(5), None);
241
242 let collected: Vec<_> = row.iter().collect();
243 assert_eq!(collected.len(), 2);
244
245 row.clear();
246 assert!(row.is_empty());
247
248 let with_cap = TokenRow::with_capacity(4);
249 assert!(with_cap.is_empty());
250 }
251
252 #[test]
253 fn with_capacity_preallocates() {
254 let row = TokenRow::with_capacity(16);
257 assert!(row.is_empty());
258 assert!(row.data.capacity() >= 16);
259 }
260
261 #[test]
262 fn row_bitmap_is_null_checks_correct_bit() {
263 let bitmap = RowBitmap {
267 data: vec![0b0000_1000],
268 };
269
270 assert!(bitmap.is_null(3));
271 assert!(!bitmap.is_null(0));
272 assert!(!bitmap.is_null(1));
273 assert!(!bitmap.is_null(2));
274 assert!(!bitmap.is_null(4));
275 }
276
277 #[test]
278 fn into_iter_yields_owned_values() {
279 let mut row = TokenRow::new();
280 row.push(ColumnData::I32(Some(1)));
281 row.push(ColumnData::I32(Some(2)));
282
283 let values: Vec<_> = row.into_iter().collect();
284 assert_eq!(
285 values,
286 vec![ColumnData::I32(Some(1)), ColumnData::I32(Some(2))]
287 );
288 }
289
290 #[tokio::test]
291 async fn encode_matching_columns_round_trip() {
292 let row = (true, 5i32).into_row();
293 let columns = vec![
294 MetaDataColumn {
295 base: BaseMetaDataColumn {
296 flags: ColumnFlag::Nullable.into(),
297 ty: TypeInfo::FixedLen(FixedLenType::Bit),
298 },
299 col_name: Default::default(),
300 },
301 MetaDataColumn {
302 base: BaseMetaDataColumn {
303 flags: ColumnFlag::Nullable.into(),
304 ty: TypeInfo::FixedLen(FixedLenType::Int4),
305 },
306 col_name: Default::default(),
307 },
308 ];
309 let mut buf = BytesMut::new();
310 let mut buf_with_columns = BytesMutWithDataColumns::new(&mut buf, &columns);
311
312 row.encode(&mut buf_with_columns).unwrap();
313 assert!(!buf.is_empty());
314 }
315
316 #[tokio::test]
317 async fn decode_reads_columns_from_cached_meta() {
318 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
319 use crate::tds::codec::TokenColMetaData;
320 use std::sync::Arc;
321
322 let col_meta = TokenColMetaData {
323 columns: vec![MetaDataColumn {
324 base: BaseMetaDataColumn {
325 flags: ColumnFlag::Nullable.into(),
326 ty: TypeInfo::FixedLen(FixedLenType::Int4),
327 },
328 col_name: Default::default(),
329 }],
330 };
331
332 let mut buf = BytesMut::new();
333 buf.put_i32_le(42);
334
335 let mut reader = buf.into_sql_read_bytes();
336 reader.context_mut().set_last_meta(Arc::new(col_meta));
337
338 let row = TokenRow::decode(&mut reader).await.unwrap();
339 assert_eq!(row.len(), 1);
340 assert_eq!(row.get(0), Some(&ColumnData::I32(Some(42))));
341 }
342
343 #[tokio::test]
344 async fn decode_nbc_before_colmetadata_is_protocol_error() {
345 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
346
347 let buf = BytesMut::new();
348 let err = TokenRow::decode_nbc(&mut buf.into_sql_read_bytes())
349 .await
350 .expect_err("NBCROW before COLMETADATA must error");
351 assert!(matches!(err, crate::Error::Protocol(_)));
352 }
353
354 #[tokio::test]
355 async fn decode_nbc_uses_bitmap_for_nulls() {
356 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
357 use crate::tds::codec::TokenColMetaData;
358 use std::sync::Arc;
359
360 let col_meta = TokenColMetaData {
362 columns: vec![
363 MetaDataColumn {
364 base: BaseMetaDataColumn {
365 flags: ColumnFlag::Nullable.into(),
366 ty: TypeInfo::FixedLen(FixedLenType::Int4),
367 },
368 col_name: Default::default(),
369 },
370 MetaDataColumn {
371 base: BaseMetaDataColumn {
372 flags: ColumnFlag::Nullable.into(),
373 ty: TypeInfo::FixedLen(FixedLenType::Int4),
374 },
375 col_name: Default::default(),
376 },
377 ],
378 };
379
380 let mut buf = BytesMut::new();
381 buf.put_u8(0b0000_0001); buf.put_i32_le(7); let mut reader = buf.into_sql_read_bytes();
385 reader.context_mut().set_last_meta(Arc::new(col_meta));
386
387 let row = TokenRow::decode_nbc(&mut reader).await.unwrap();
388 assert_eq!(row.len(), 2);
389 assert_eq!(row.get(0), Some(&ColumnData::I32(None)));
390 assert_eq!(row.get(1), Some(&ColumnData::I32(Some(7))));
391 }
392}