1use asynchronous_codec::BytesMut;
2use bytes::BufMut;
3
4use crate::{tds::Collation, xml::XmlSchema, Error, SqlReadBytes};
5use std::{convert::TryFrom, sync::Arc};
6
7use super::Encode;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TypeLength {
12 Limited(u16),
14 Max,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum TypeInfo {
21 FixedLen(FixedLenType),
23 VarLenSized(VarLenContext),
25 VarLenSizedPrecision {
28 ty: VarLenType,
30 size: usize,
32 precision: u8,
34 scale: u8,
36 },
37 Xml {
39 schema: Option<Arc<XmlSchema>>,
41 size: usize,
43 },
44 Udt(UdtInfo),
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct UdtInfo {
58 pub max_byte_size: u16,
61 pub db_name: String,
63 pub schema_name: String,
65 pub type_name: String,
67 pub assembly_qualified_name: String,
69}
70
71#[derive(Clone, Debug, Copy, PartialEq, Eq)]
74pub struct VarLenContext {
75 r#type: VarLenType,
76 len: usize,
77 collation: Option<Collation>,
78}
79
80impl VarLenContext {
81 pub fn new(r#type: VarLenType, len: usize, collation: Option<Collation>) -> Self {
84 Self {
85 r#type,
86 len,
87 collation,
88 }
89 }
90
91 pub fn r#type(&self) -> VarLenType {
93 self.r#type
94 }
95
96 pub fn len(&self) -> usize {
98 self.len
99 }
100
101 pub fn is_empty(&self) -> bool {
103 self.len == 0
104 }
105
106 pub fn collation(&self) -> Option<Collation> {
108 self.collation
109 }
110}
111
112impl Encode<BytesMut> for VarLenContext {
113 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
114 dst.put_u8(self.r#type() as u8);
115
116 match self.r#type {
118 #[cfg(feature = "tds73")]
125 VarLenType::Daten => {}
126 #[cfg(feature = "tds73")]
127 VarLenType::Timen | VarLenType::DatetimeOffsetn | VarLenType::Datetime2 => {
128 dst.put_u8(self.len() as u8);
129 }
130 VarLenType::Bitn
131 | VarLenType::Intn
132 | VarLenType::Floatn
133 | VarLenType::Decimaln
134 | VarLenType::Numericn
135 | VarLenType::Guid
136 | VarLenType::Money
137 | VarLenType::Datetimen => {
138 dst.put_u8(self.len() as u8);
139 }
140 VarLenType::NChar
141 | VarLenType::BigChar
142 | VarLenType::NVarchar
143 | VarLenType::BigVarChar
144 | VarLenType::BigBinary
145 | VarLenType::BigVarBin => {
146 dst.put_u16_le(self.len() as u16);
147 }
148 VarLenType::Image | VarLenType::Text | VarLenType::NText | VarLenType::SSVariant => {
149 dst.put_u32_le(self.len() as u32);
150 }
151 VarLenType::Xml => (),
152 typ => {
153 return Err(Error::Protocol(
154 format!("encoding a {typ:?} var-len context is not supported").into(),
155 ))
156 }
157 }
158
159 if let Some(collation) = self.collation() {
160 dst.put_u32_le(collation.info());
161 dst.put_u8(collation.sort_id());
162 }
163
164 Ok(())
165 }
166}
167
168uint_enum! {
169 #[repr(u8)]
170 pub enum FixedLenType {
171 Null = 0x1F,
172 Int1 = 0x30,
173 Bit = 0x32,
174 Int2 = 0x34,
175 Int4 = 0x38,
176 Datetime4 = 0x3A,
177 Float4 = 0x3B,
178 Money = 0x3C,
179 Datetime = 0x3D,
180 Float8 = 0x3E,
181 Money4 = 0x7A,
182 Int8 = 0x7F,
183 }
184}
185
186#[cfg(not(feature = "tds73"))]
187uint_enum! {
188 #[repr(u8)]
190 pub enum VarLenType {
191 Guid = 0x24,
192 Intn = 0x26,
193 Bitn = 0x68,
194 Decimaln = 0x6A,
195 Numericn = 0x6C,
196 Floatn = 0x6D,
197 Money = 0x6E,
198 Datetimen = 0x6F,
199 BigVarBin = 0xA5,
200 BigVarChar = 0xA7,
201 BigBinary = 0xAD,
202 BigChar = 0xAF,
203 NVarchar = 0xE7,
204 NChar = 0xEF,
205 Xml = 0xF1,
206 Udt = 0xF0,
208 Text = 0x23,
209 Image = 0x22,
210 NText = 0x63,
211 SSVariant = 0x62, }
220}
221
222#[cfg(feature = "tds73")]
223uint_enum! {
224 #[repr(u8)]
226 pub enum VarLenType {
227 Guid = 0x24,
228 Intn = 0x26,
229 Bitn = 0x68,
230 Decimaln = 0x6A,
231 Numericn = 0x6C,
232 Floatn = 0x6D,
233 Money = 0x6E,
234 Datetimen = 0x6F,
235 Daten = 0x28,
236 Timen = 0x29,
237 Datetime2 = 0x2A,
238 DatetimeOffsetn = 0x2B,
239 BigVarBin = 0xA5,
240 BigVarChar = 0xA7,
241 BigBinary = 0xAD,
242 BigChar = 0xAF,
243 NVarchar = 0xE7,
244 NChar = 0xEF,
245 Xml = 0xF1,
246 Udt = 0xF0,
248 Text = 0x23,
249 Image = 0x22,
250 NText = 0x63,
251 SSVariant = 0x62, }
260}
261
262impl Encode<BytesMut> for TypeInfo {
263 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
264 match self {
265 TypeInfo::FixedLen(ty) => {
266 dst.put_u8(ty as u8);
267 }
268 TypeInfo::VarLenSized(ctx) => ctx.encode(dst)?,
269 TypeInfo::VarLenSizedPrecision {
270 ty,
271 size,
272 precision,
273 scale,
274 } => {
275 dst.put_u8(ty as u8);
276 dst.put_u8(size as u8);
277 dst.put_u8(precision);
278 dst.put_u8(scale);
279 }
280 TypeInfo::Xml { schema, .. } => {
281 dst.put_u8(VarLenType::Xml as u8);
282
283 if let Some(xs) = schema {
284 dst.put_u8(1);
285
286 let db_name_encoded: Vec<u16> = xs.db_name().encode_utf16().collect();
287 dst.put_u8(db_name_encoded.len() as u8);
288 for chr in db_name_encoded {
289 dst.put_u16_le(chr);
290 }
291
292 let owner_encoded: Vec<u16> = xs.owner().encode_utf16().collect();
293 dst.put_u8(owner_encoded.len() as u8);
294 for chr in owner_encoded {
295 dst.put_u16_le(chr);
296 }
297
298 let collection_encoded: Vec<u16> = xs.collection().encode_utf16().collect();
299 dst.put_u16_le(collection_encoded.len() as u16);
300 for chr in collection_encoded {
301 dst.put_u16_le(chr);
302 }
303 } else {
304 dst.put_u8(0);
305 }
306 }
307 TypeInfo::Udt(info) => {
308 dst.put_u8(VarLenType::Udt as u8);
309 dst.put_u16_le(info.max_byte_size);
310
311 let db_name: Vec<u16> = info.db_name.encode_utf16().collect();
312 dst.put_u8(db_name.len() as u8);
313 for chr in db_name {
314 dst.put_u16_le(chr);
315 }
316
317 let schema_name: Vec<u16> = info.schema_name.encode_utf16().collect();
318 dst.put_u8(schema_name.len() as u8);
319 for chr in schema_name {
320 dst.put_u16_le(chr);
321 }
322
323 let type_name: Vec<u16> = info.type_name.encode_utf16().collect();
324 dst.put_u8(type_name.len() as u8);
325 for chr in type_name {
326 dst.put_u16_le(chr);
327 }
328
329 let aqn: Vec<u16> = info.assembly_qualified_name.encode_utf16().collect();
330 dst.put_u16_le(aqn.len() as u16);
331 for chr in aqn {
332 dst.put_u16_le(chr);
333 }
334 }
335 }
336
337 Ok(())
338 }
339}
340
341impl TypeInfo {
342 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
343 where
344 R: SqlReadBytes + Unpin,
345 {
346 let ty = src.read_u8().await?;
347
348 if let Ok(ty) = FixedLenType::try_from(ty) {
349 return Ok(TypeInfo::FixedLen(ty));
350 }
351
352 match VarLenType::try_from(ty) {
353 Err(()) => Err(Error::Protocol(
354 format!("invalid or unsupported column type: {:?}", ty).into(),
355 )),
356 Ok(VarLenType::Xml) => {
357 let has_schema = src.read_u8().await?;
358
359 let schema = if has_schema == 1 {
360 let db_name = src.read_b_varchar().await?;
361 let owner = src.read_b_varchar().await?;
362 let collection = src.read_us_varchar().await?;
363
364 Some(Arc::new(XmlSchema::new(db_name, owner, collection)))
365 } else {
366 None
367 };
368
369 Ok(TypeInfo::Xml {
370 schema,
371 size: 0xfffffffffffffffe_usize,
372 })
373 }
374 Ok(VarLenType::Udt) => {
375 let max_byte_size = src.read_u16_le().await?;
377 let db_name = src.read_b_varchar().await?;
378 let schema_name = src.read_b_varchar().await?;
379 let type_name = src.read_b_varchar().await?;
380 let assembly_qualified_name = src.read_us_varchar().await?;
381
382 Ok(TypeInfo::Udt(UdtInfo {
383 max_byte_size,
384 db_name,
385 schema_name,
386 type_name,
387 assembly_qualified_name,
388 }))
389 }
390 Ok(ty) => {
391 let len = match ty {
392 #[cfg(feature = "tds73")]
393 VarLenType::Timen | VarLenType::DatetimeOffsetn | VarLenType::Datetime2 => {
394 src.read_u8().await? as usize
395 }
396 #[cfg(feature = "tds73")]
397 VarLenType::Daten => 3,
398 VarLenType::Bitn
399 | VarLenType::Intn
400 | VarLenType::Floatn
401 | VarLenType::Decimaln
402 | VarLenType::Numericn
403 | VarLenType::Guid
404 | VarLenType::Money
405 | VarLenType::Datetimen => src.read_u8().await? as usize,
406 VarLenType::NChar
407 | VarLenType::BigChar
408 | VarLenType::NVarchar
409 | VarLenType::BigVarChar
410 | VarLenType::BigBinary
411 | VarLenType::BigVarBin => src.read_u16_le().await? as usize,
412 VarLenType::Image
413 | VarLenType::Text
414 | VarLenType::NText
415 | VarLenType::SSVariant => src.read_u32_le().await? as usize,
416 _ => {
417 return Err(Error::Protocol(
418 format!("unsupported column type in COLMETADATA: {:?}", ty).into(),
419 ))
420 }
421 };
422
423 let collation = match ty {
424 VarLenType::NText
425 | VarLenType::Text
426 | VarLenType::BigChar
427 | VarLenType::NChar
428 | VarLenType::NVarchar
429 | VarLenType::BigVarChar => {
430 let info = src.read_u32_le().await?;
431 let sort_id = src.read_u8().await?;
432
433 Some(Collation::new(info, sort_id))
434 }
435 _ => None,
436 };
437
438 let vty = match ty {
439 VarLenType::Decimaln | VarLenType::Numericn => {
440 let precision = src.read_u8().await?;
441 let scale = src.read_u8().await?;
442
443 if precision > 38 || scale > precision {
447 return Err(Error::Protocol(
448 format!(
449 "decimal/numeric: invalid precision {precision} / scale {scale}"
450 )
451 .into(),
452 ));
453 }
454
455 TypeInfo::VarLenSizedPrecision {
456 size: len,
457 ty,
458 precision,
459 scale,
460 }
461 }
462 _ => {
463 let cx = VarLenContext::new(ty, len, collation);
464 TypeInfo::VarLenSized(cx)
465 }
466 };
467
468 Ok(vty)
469 }
470 }
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
478
479 #[tokio::test]
480 async fn round_trip() {
481 let types = vec![
482 TypeInfo::Xml {
483 schema: Some(
484 XmlSchema::new("fake-db-name", "fake-owner", "fake-collection").into(),
485 ),
486 size: 0xfffffffffffffffe_usize,
487 },
488 TypeInfo::Xml {
489 schema: None,
490 size: 0xfffffffffffffffe_usize,
491 },
492 TypeInfo::FixedLen(FixedLenType::Int4),
493 TypeInfo::VarLenSized(VarLenContext::new(
494 VarLenType::NChar,
495 40,
496 Some(Collation::new(13632521, 52)),
497 )),
498 TypeInfo::Udt(UdtInfo {
499 max_byte_size: 0xffff,
500 db_name: "fake-db".to_string(),
501 schema_name: "dbo".to_string(),
502 type_name: "geometry".to_string(),
503 assembly_qualified_name:
504 "Microsoft.SqlServer.Types.SqlGeometry, Microsoft.SqlServer.Types".to_string(),
505 }),
506 ];
507
508 for ti in types {
509 let mut buf = BytesMut::new();
510
511 ti.clone()
512 .encode(&mut buf)
513 .expect("encode should be successful");
514
515 let nti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
516 .await
517 .expect("decode must succeed");
518
519 assert_eq!(nti, ti)
520 }
521 }
522
523 #[cfg(feature = "tds73")]
524 #[tokio::test]
525 async fn date_typeinfo_round_trips_without_scale_byte() {
526 let ti = TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Daten, 3, None));
529 let mut buf = BytesMut::new();
530 ti.clone().encode(&mut buf).expect("encode must succeed");
531
532 assert_eq!(buf.as_ref(), &[VarLenType::Daten as u8]);
533
534 let nti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
535 .await
536 .expect("decode must succeed");
537 assert_eq!(nti, ti);
538 }
539
540 #[tokio::test]
541 async fn decode_rejects_out_of_range_precision_scale() {
542 let mut buf = BytesMut::new();
546 buf.put_u8(VarLenType::Decimaln as u8);
547 buf.put_u8(17); buf.put_u8(200); buf.put_u8(2); let err = TypeInfo::decode(&mut buf.into_sql_read_bytes())
552 .await
553 .expect_err("out-of-range precision must error");
554 assert!(matches!(err, Error::Protocol(_)));
555 }
556
557 #[test]
558 fn var_len_context_is_empty() {
559 assert!(VarLenContext::new(VarLenType::Intn, 0, None).is_empty());
560 assert!(!VarLenContext::new(VarLenType::Intn, 4, None).is_empty());
561 }
562
563 #[tokio::test]
564 async fn decode_intn_reads_one_byte_length() {
565 let mut buf = BytesMut::new();
567 buf.put_u8(VarLenType::Intn as u8);
568 buf.put_u8(4); let ti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
571 .await
572 .expect("decode must succeed");
573 assert_eq!(
574 ti,
575 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Intn, 4, None))
576 );
577 }
578
579 #[cfg(feature = "tds73")]
580 #[tokio::test]
581 async fn decode_timen_reads_one_byte_scale() {
582 let mut buf = BytesMut::new();
585 buf.put_u8(VarLenType::Timen as u8);
586 buf.put_u8(7); let ti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
589 .await
590 .expect("decode must succeed");
591 assert_eq!(
592 ti,
593 TypeInfo::VarLenSized(VarLenContext::new(VarLenType::Timen, 7, None))
594 );
595 }
596
597 #[tokio::test]
598 async fn decode_accepts_precision_38_and_scale_below_precision() {
599 let mut buf = BytesMut::new();
602 buf.put_u8(VarLenType::Decimaln as u8);
603 buf.put_u8(17); buf.put_u8(38); buf.put_u8(10); let ti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
608 .await
609 .expect("precision 38 must be accepted");
610 assert_eq!(
611 ti,
612 TypeInfo::VarLenSizedPrecision {
613 ty: VarLenType::Decimaln,
614 size: 17,
615 precision: 38,
616 scale: 10,
617 }
618 );
619 }
620
621 #[tokio::test]
622 async fn decode_accepts_scale_equal_to_precision() {
623 let mut buf = BytesMut::new();
625 buf.put_u8(VarLenType::Numericn as u8);
626 buf.put_u8(17); buf.put_u8(20); buf.put_u8(20); let ti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
631 .await
632 .expect("scale == precision must be accepted");
633 assert_eq!(
634 ti,
635 TypeInfo::VarLenSizedPrecision {
636 ty: VarLenType::Numericn,
637 size: 17,
638 precision: 20,
639 scale: 20,
640 }
641 );
642 }
643
644 #[test]
645 fn var_len_context_encode_xml_emits_only_type_byte() {
646 let mut buf = BytesMut::new();
649 VarLenContext::new(VarLenType::Xml, 0, None)
650 .encode(&mut buf)
651 .expect("encode must succeed");
652 assert_eq!(buf.as_ref(), &[VarLenType::Xml as u8]);
653 }
654
655 #[test]
656 fn var_len_context_encode_unsupported_type_errors() {
657 let mut buf = BytesMut::new();
660 let err = VarLenContext::new(VarLenType::Udt, 0, None)
661 .encode(&mut buf)
662 .expect_err("encoding a Udt var-len context must error");
663 assert!(matches!(err, Error::Protocol(_)));
664 }
665
666 #[tokio::test]
667 async fn decode_rejects_invalid_type_byte() {
668 let mut buf = BytesMut::new();
671 buf.put_u8(0x00);
672
673 let err = TypeInfo::decode(&mut buf.into_sql_read_bytes())
674 .await
675 .expect_err("invalid type byte must error");
676 assert!(matches!(err, Error::Protocol(_)));
677 }
678
679 #[tokio::test]
680 async fn decode_udt_info_round_trips() {
681 let ti = TypeInfo::Udt(UdtInfo {
684 max_byte_size: 0xffff,
685 db_name: "db".to_string(),
686 schema_name: "dbo".to_string(),
687 type_name: "geometry".to_string(),
688 assembly_qualified_name: "asm".to_string(),
689 });
690
691 let mut buf = BytesMut::new();
692 ti.clone().encode(&mut buf).expect("encode must succeed");
693
694 let decoded = TypeInfo::decode(&mut buf.into_sql_read_bytes())
695 .await
696 .expect("decode must succeed");
697 assert_eq!(decoded, ti);
698 }
699
700 #[tokio::test]
701 async fn decode_rejects_scale_greater_than_precision() {
702 let mut buf = BytesMut::new();
704 buf.put_u8(VarLenType::Decimaln as u8);
705 buf.put_u8(17); buf.put_u8(10); buf.put_u8(20); let err = TypeInfo::decode(&mut buf.into_sql_read_bytes())
710 .await
711 .expect_err("scale > precision must error");
712 assert!(matches!(err, Error::Protocol(_)));
713 }
714}