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