1use asynchronous_codec::BytesMut;
2use bytes::BufMut;
3
4use crate::{tds::Collation, xml::XmlSchema, Error, SqlReadBytes};
5use std::{convert::TryFrom, sync::Arc, usize};
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),
22 VarLenSized(VarLenContext),
23 VarLenSizedPrecision {
24 ty: VarLenType,
25 size: usize,
26 precision: u8,
27 scale: u8,
28 },
29 Xml {
30 schema: Option<Arc<XmlSchema>>,
31 size: usize,
32 },
33}
34
35#[derive(Clone, Debug, Copy, PartialEq, Eq)]
36pub struct VarLenContext {
37 r#type: VarLenType,
38 len: usize,
39 collation: Option<Collation>,
40}
41
42impl VarLenContext {
43 pub fn new(r#type: VarLenType, len: usize, collation: Option<Collation>) -> Self {
44 Self {
45 r#type,
46 len,
47 collation,
48 }
49 }
50
51 pub fn r#type(&self) -> VarLenType {
53 self.r#type
54 }
55
56 pub fn len(&self) -> usize {
58 self.len
59 }
60
61 pub fn collation(&self) -> Option<Collation> {
63 self.collation
64 }
65}
66
67impl Encode<BytesMut> for VarLenContext {
68 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
69 dst.put_u8(self.r#type() as u8);
70
71 match self.r#type {
73 #[cfg(feature = "tds73")]
74 VarLenType::Daten
75 | VarLenType::Timen
76 | VarLenType::DatetimeOffsetn
77 | VarLenType::Datetime2 => {
78 dst.put_u8(self.len() as u8);
79 }
80 VarLenType::Bitn
81 | VarLenType::Intn
82 | VarLenType::Floatn
83 | VarLenType::Decimaln
84 | VarLenType::Numericn
85 | VarLenType::Guid
86 | VarLenType::Money
87 | VarLenType::Datetimen => {
88 dst.put_u8(self.len() as u8);
89 }
90 VarLenType::NChar
91 | VarLenType::BigChar
92 | VarLenType::NVarchar
93 | VarLenType::BigVarChar
94 | VarLenType::BigBinary
95 | VarLenType::BigVarBin => {
96 dst.put_u16_le(self.len() as u16);
97 }
98 VarLenType::Image | VarLenType::Text | VarLenType::NText => {
99 dst.put_u32_le(self.len() as u32);
100 }
101 VarLenType::Xml => (),
102 typ => todo!("encoding {:?} is not supported yet", typ),
103 }
104
105 if let Some(collation) = self.collation() {
106 dst.put_u32_le(collation.info());
107 dst.put_u8(collation.sort_id());
108 }
109
110 Ok(())
111 }
112}
113
114uint_enum! {
115 #[repr(u8)]
116 pub enum FixedLenType {
117 Null = 0x1F,
118 Int1 = 0x30,
119 Bit = 0x32,
120 Int2 = 0x34,
121 Int4 = 0x38,
122 Datetime4 = 0x3A,
123 Float4 = 0x3B,
124 Money = 0x3C,
125 Datetime = 0x3D,
126 Float8 = 0x3E,
127 Money4 = 0x7A,
128 Int8 = 0x7F,
129 }
130}
131
132#[cfg(not(feature = "tds73"))]
133uint_enum! {
134 #[repr(u8)]
136 pub enum VarLenType {
137 Guid = 0x24,
138 Intn = 0x26,
139 Bitn = 0x68,
140 Decimaln = 0x6A,
141 Numericn = 0x6C,
142 Floatn = 0x6D,
143 Money = 0x6E,
144 Datetimen = 0x6F,
145 BigVarBin = 0xA5,
146 BigVarChar = 0xA7,
147 BigBinary = 0xAD,
148 BigChar = 0xAF,
149 NVarchar = 0xE7,
150 NChar = 0xEF,
151 Xml = 0xF1,
152 Udt = 0xF0,
154 Text = 0x23,
155 Image = 0x22,
156 NText = 0x63,
157 SSVariant = 0x62, }
166}
167
168#[cfg(feature = "tds73")]
169uint_enum! {
170 #[repr(u8)]
172 pub enum VarLenType {
173 Guid = 0x24,
174 Intn = 0x26,
175 Bitn = 0x68,
176 Decimaln = 0x6A,
177 Numericn = 0x6C,
178 Floatn = 0x6D,
179 Money = 0x6E,
180 Datetimen = 0x6F,
181 Daten = 0x28,
182 Timen = 0x29,
183 Datetime2 = 0x2A,
184 DatetimeOffsetn = 0x2B,
185 BigVarBin = 0xA5,
186 BigVarChar = 0xA7,
187 BigBinary = 0xAD,
188 BigChar = 0xAF,
189 NVarchar = 0xE7,
190 NChar = 0xEF,
191 Xml = 0xF1,
192 Udt = 0xF0,
194 Text = 0x23,
195 Image = 0x22,
196 NText = 0x63,
197 SSVariant = 0x62, }
206}
207
208impl Encode<BytesMut> for TypeInfo {
209 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
210 match self {
211 TypeInfo::FixedLen(ty) => {
212 dst.put_u8(ty as u8);
213 }
214 TypeInfo::VarLenSized(ctx) => ctx.encode(dst)?,
215 TypeInfo::VarLenSizedPrecision {
216 ty,
217 size,
218 precision,
219 scale,
220 } => {
221 dst.put_u8(ty as u8);
222 dst.put_u8(size as u8);
223 dst.put_u8(precision);
224 dst.put_u8(scale);
225 }
226 TypeInfo::Xml { schema, .. } => {
227 dst.put_u8(VarLenType::Xml as u8);
228
229 if let Some(xs) = schema {
230 dst.put_u8(1);
231
232 let db_name_encoded: Vec<u16> = xs.db_name().encode_utf16().collect();
233 dst.put_u8(db_name_encoded.len() as u8);
234 for chr in db_name_encoded {
235 dst.put_u16_le(chr);
236 }
237
238 let owner_encoded: Vec<u16> = xs.owner().encode_utf16().collect();
239 dst.put_u8(owner_encoded.len() as u8);
240 for chr in owner_encoded {
241 dst.put_u16_le(chr);
242 }
243
244 let collection_encoded: Vec<u16> = xs.collection().encode_utf16().collect();
245 dst.put_u16_le(collection_encoded.len() as u16);
246 for chr in collection_encoded {
247 dst.put_u16_le(chr);
248 }
249 } else {
250 dst.put_u8(0);
251 }
252 }
253 }
254
255 Ok(())
256 }
257}
258
259impl TypeInfo {
260 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
261 where
262 R: SqlReadBytes + Unpin,
263 {
264 let ty = src.read_u8().await?;
265
266 if let Ok(ty) = FixedLenType::try_from(ty) {
267 return Ok(TypeInfo::FixedLen(ty));
268 }
269
270 match VarLenType::try_from(ty) {
271 Err(()) => Err(Error::Protocol(
272 format!("invalid or unsupported column type: {:?}", ty).into(),
273 )),
274 Ok(VarLenType::Xml) => {
275 let has_schema = src.read_u8().await?;
276
277 let schema = if has_schema == 1 {
278 let db_name = src.read_b_varchar().await?;
279 let owner = src.read_b_varchar().await?;
280 let collection = src.read_us_varchar().await?;
281
282 Some(Arc::new(XmlSchema::new(db_name, owner, collection)))
283 } else {
284 None
285 };
286
287 Ok(TypeInfo::Xml {
288 schema,
289 size: 0xfffffffffffffffe_usize,
290 })
291 }
292 Ok(ty) => {
293 let len = match ty {
294 #[cfg(feature = "tds73")]
295 VarLenType::Timen | VarLenType::DatetimeOffsetn | VarLenType::Datetime2 => {
296 src.read_u8().await? as usize
297 }
298 #[cfg(feature = "tds73")]
299 VarLenType::Daten => 3,
300 VarLenType::Bitn
301 | VarLenType::Intn
302 | VarLenType::Floatn
303 | VarLenType::Decimaln
304 | VarLenType::Numericn
305 | VarLenType::Guid
306 | VarLenType::Money
307 | VarLenType::Datetimen => src.read_u8().await? as usize,
308 VarLenType::NChar
309 | VarLenType::BigChar
310 | VarLenType::NVarchar
311 | VarLenType::BigVarChar
312 | VarLenType::BigBinary
313 | VarLenType::BigVarBin => src.read_u16_le().await? as usize,
314 VarLenType::Image | VarLenType::Text | VarLenType::NText => {
315 src.read_u32_le().await? as usize
316 }
317 _ => todo!("not yet implemented for {:?}", ty),
318 };
319
320 let collation = match ty {
321 VarLenType::NText
322 | VarLenType::Text
323 | VarLenType::BigChar
324 | VarLenType::NChar
325 | VarLenType::NVarchar
326 | VarLenType::BigVarChar => {
327 let info = src.read_u32_le().await?;
328 let sort_id = src.read_u8().await?;
329
330 Some(Collation::new(info, sort_id))
331 }
332 _ => None,
333 };
334
335 let vty = match ty {
336 VarLenType::Decimaln | VarLenType::Numericn => {
337 let precision = src.read_u8().await?;
338 let scale = src.read_u8().await?;
339
340 TypeInfo::VarLenSizedPrecision {
341 size: len,
342 ty,
343 precision,
344 scale,
345 }
346 }
347 _ => {
348 let cx = VarLenContext::new(ty, len, collation);
349 TypeInfo::VarLenSized(cx)
350 }
351 };
352
353 Ok(vty)
354 }
355 }
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
363
364 #[tokio::test]
365 async fn round_trip() {
366 let types = vec![
367 TypeInfo::Xml {
368 schema: Some(
369 XmlSchema::new("fake-db-name", "fake-owner", "fake-collection").into(),
370 ),
371 size: 0xfffffffffffffffe_usize,
372 },
373 TypeInfo::Xml {
374 schema: None,
375 size: 0xfffffffffffffffe_usize,
376 },
377 TypeInfo::FixedLen(FixedLenType::Int4),
378 TypeInfo::VarLenSized(VarLenContext::new(
379 VarLenType::NChar,
380 40,
381 Some(Collation::new(13632521, 52)),
382 )),
383 ];
384
385 for ti in types {
386 let mut buf = BytesMut::new();
387
388 ti.clone()
389 .encode(&mut buf)
390 .expect("encode should be successful");
391
392 let nti = TypeInfo::decode(&mut buf.into_sql_read_bytes())
393 .await
394 .expect("decode must succeed");
395
396 assert_eq!(nti, ti)
397 }
398 }
399}