1use std::{
2 borrow::{BorrowMut, Cow},
3 fmt::Display,
4};
5
6use crate::{
7 error::Error,
8 tds::codec::{Encode, FixedLenType, TokenType, TypeInfo, VarLenType},
9 Column, ColumnData, ColumnType, SqlReadBytes,
10};
11use asynchronous_codec::BytesMut;
12use bytes::BufMut;
13use enumflags2::{bitflags, BitFlags};
14
15#[derive(Debug, Clone)]
16pub struct TokenColMetaData<'a> {
17 pub columns: Vec<MetaDataColumn<'a>>,
18}
19
20#[derive(Debug, Clone)]
21pub struct MetaDataColumn<'a> {
22 pub base: BaseMetaDataColumn,
23 pub col_name: Cow<'a, str>,
24}
25
26impl<'a> Display for MetaDataColumn<'a> {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "{} ", self.col_name)?;
29
30 match &self.base.ty {
31 TypeInfo::FixedLen(fixed) => match fixed {
32 FixedLenType::Int1 => write!(f, "tinyint")?,
33 FixedLenType::Bit => write!(f, "bit")?,
34 FixedLenType::Int2 => write!(f, "smallint")?,
35 FixedLenType::Int4 => write!(f, "int")?,
36 FixedLenType::Datetime4 => write!(f, "smalldatetime")?,
37 FixedLenType::Float4 => write!(f, "real")?,
38 FixedLenType::Money => write!(f, "money")?,
39 FixedLenType::Datetime => write!(f, "datetime")?,
40 FixedLenType::Float8 => write!(f, "float")?,
41 FixedLenType::Money4 => write!(f, "smallmoney")?,
42 FixedLenType::Int8 => write!(f, "bigint")?,
43 FixedLenType::Null => unreachable!(),
44 },
45 TypeInfo::VarLenSized(ctx) => match ctx.r#type() {
46 VarLenType::Bitn => write!(f, "bit")?,
47 VarLenType::Guid => write!(f, "uniqueidentifier")?,
48 #[cfg(feature = "tds73")]
49 VarLenType::Daten => write!(f, "date")?,
50 #[cfg(feature = "tds73")]
51 VarLenType::Timen => write!(f, "time")?,
52 #[cfg(feature = "tds73")]
53 VarLenType::Datetime2 => write!(f, "datetime2({})", ctx.len())?,
54 VarLenType::Datetimen => write!(f, "datetime")?,
55 #[cfg(feature = "tds73")]
56 VarLenType::DatetimeOffsetn => write!(f, "datetimeoffset")?,
57 VarLenType::BigVarBin => {
58 if ctx.len() <= 8000 {
59 write!(f, "varbinary({})", ctx.len())?
60 } else {
61 write!(f, "varbinary(max)")?
62 }
63 }
64 VarLenType::BigVarChar => {
65 if ctx.len() <= 8000 {
66 write!(f, "varchar({})", ctx.len())?
67 } else {
68 write!(f, "varchar(max)")?
69 }
70 }
71 VarLenType::BigBinary => write!(f, "binary({})", ctx.len())?,
72 VarLenType::BigChar => write!(f, "char({})", ctx.len())?,
73 VarLenType::NVarchar => {
74 if ctx.len() <= 4000 {
75 write!(f, "nvarchar({})", ctx.len())?
76 } else {
77 write!(f, "nvarchar(max)")?
78 }
79 }
80 VarLenType::NChar => write!(f, "nchar({})", ctx.len())?,
81 VarLenType::Text => write!(f, "text")?,
82 VarLenType::Image => write!(f, "image")?,
83 VarLenType::NText => write!(f, "ntext")?,
84 VarLenType::Intn => match ctx.len() {
85 1 => write!(f, "tinyint")?,
86 2 => write!(f, "smallint")?,
87 4 => write!(f, "int")?,
88 8 => write!(f, "bigint")?,
89 _ => unreachable!(),
90 },
91 VarLenType::Floatn => match ctx.len() {
92 4 => write!(f, "real")?,
93 8 => write!(f, "float")?,
94 _ => unreachable!(),
95 },
96 _ => unreachable!(),
97 },
98 TypeInfo::VarLenSizedPrecision {
99 ty,
100 size: _,
101 precision,
102 scale,
103 } => match ty {
104 VarLenType::Decimaln => write!(f, "decimal({},{})", precision, scale)?,
105 VarLenType::Numericn => write!(f, "numeric({},{})", precision, scale)?,
106 _ => unreachable!(),
107 },
108 TypeInfo::Xml { .. } => write!(f, "xml")?,
109 }
110
111 Ok(())
112 }
113}
114
115#[derive(Debug, Clone)]
116pub struct BaseMetaDataColumn {
117 pub flags: BitFlags<ColumnFlag>,
118 pub ty: TypeInfo,
119}
120
121impl BaseMetaDataColumn {
122 pub(crate) fn null_value(&self) -> ColumnData<'static> {
123 match &self.ty {
124 TypeInfo::FixedLen(ty) => match ty {
125 FixedLenType::Null => ColumnData::I32(None),
126 FixedLenType::Int1 => ColumnData::U8(None),
127 FixedLenType::Bit => ColumnData::Bit(None),
128 FixedLenType::Int2 => ColumnData::I16(None),
129 FixedLenType::Int4 => ColumnData::I32(None),
130 FixedLenType::Datetime4 => ColumnData::SmallDateTime(None),
131 FixedLenType::Float4 => ColumnData::F32(None),
132 FixedLenType::Money => ColumnData::Numeric(None),
133 FixedLenType::Datetime => ColumnData::DateTime(None),
134 FixedLenType::Float8 => ColumnData::F64(None),
135 FixedLenType::Money4 => ColumnData::Numeric(None),
136 FixedLenType::Int8 => ColumnData::I64(None),
137 },
138 TypeInfo::VarLenSized(cx) => match cx.r#type() {
139 VarLenType::Guid => ColumnData::Guid(None),
140 VarLenType::Intn => match cx.len() {
141 1 => ColumnData::U8(None),
142 2 => ColumnData::I16(None),
143 4 => ColumnData::I32(None),
144 _ => ColumnData::I64(None),
145 },
146 VarLenType::Bitn => ColumnData::Bit(None),
147 VarLenType::Decimaln => ColumnData::Numeric(None),
148 VarLenType::Numericn => ColumnData::Numeric(None),
149 VarLenType::Floatn => match cx.len() {
150 4 => ColumnData::F32(None),
151 _ => ColumnData::F64(None),
152 },
153 VarLenType::Money => ColumnData::Numeric(None),
154 VarLenType::Datetimen => ColumnData::DateTime(None),
155 #[cfg(feature = "tds73")]
156 VarLenType::Daten => ColumnData::Date(None),
157 #[cfg(feature = "tds73")]
158 VarLenType::Timen => ColumnData::Time(None),
159 #[cfg(feature = "tds73")]
160 VarLenType::Datetime2 => ColumnData::DateTime2(None),
161 #[cfg(feature = "tds73")]
162 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
163 VarLenType::BigVarBin => ColumnData::Binary(None),
164 VarLenType::BigVarChar => ColumnData::String(None),
165 VarLenType::BigBinary => ColumnData::Binary(None),
166 VarLenType::BigChar => ColumnData::String(None),
167 VarLenType::NVarchar => ColumnData::String(None),
168 VarLenType::NChar => ColumnData::String(None),
169 VarLenType::Xml => ColumnData::Xml(None),
170 VarLenType::Udt => todo!("User-defined types not supported"),
171 VarLenType::Text => ColumnData::String(None),
172 VarLenType::Image => ColumnData::Binary(None),
173 VarLenType::NText => ColumnData::String(None),
174 VarLenType::SSVariant => todo!(),
175 },
176 TypeInfo::VarLenSizedPrecision { ty, .. } => match ty {
177 VarLenType::Guid => ColumnData::Guid(None),
178 VarLenType::Intn => ColumnData::I32(None),
179 VarLenType::Bitn => ColumnData::Bit(None),
180 VarLenType::Decimaln => ColumnData::Numeric(None),
181 VarLenType::Numericn => ColumnData::Numeric(None),
182 VarLenType::Floatn => ColumnData::F32(None),
183 VarLenType::Money => ColumnData::Numeric(None),
184 VarLenType::Datetimen => ColumnData::DateTime(None),
185 #[cfg(feature = "tds73")]
186 VarLenType::Daten => ColumnData::Date(None),
187 #[cfg(feature = "tds73")]
188 VarLenType::Timen => ColumnData::Time(None),
189 #[cfg(feature = "tds73")]
190 VarLenType::Datetime2 => ColumnData::DateTime2(None),
191 #[cfg(feature = "tds73")]
192 VarLenType::DatetimeOffsetn => ColumnData::DateTimeOffset(None),
193 VarLenType::BigVarBin => ColumnData::Binary(None),
194 VarLenType::BigVarChar => ColumnData::String(None),
195 VarLenType::BigBinary => ColumnData::Binary(None),
196 VarLenType::BigChar => ColumnData::String(None),
197 VarLenType::NVarchar => ColumnData::String(None),
198 VarLenType::NChar => ColumnData::String(None),
199 VarLenType::Xml => ColumnData::Xml(None),
200 VarLenType::Udt => todo!("User-defined types not supported"),
201 VarLenType::Text => ColumnData::String(None),
202 VarLenType::Image => ColumnData::Binary(None),
203 VarLenType::NText => ColumnData::String(None),
204 VarLenType::SSVariant => todo!(),
205 },
206 TypeInfo::Xml { .. } => ColumnData::Xml(None),
207 }
208 }
209}
210
211impl<'a> Encode<BytesMut> for TokenColMetaData<'a> {
212 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
213 dst.put_u8(TokenType::ColMetaData as u8);
214 dst.put_u16_le(self.columns.len() as u16);
215
216 for col in self.columns.into_iter() {
217 col.encode(dst)?;
218 }
219
220 Ok(())
221 }
222}
223
224impl<'a> Encode<BytesMut> for MetaDataColumn<'a> {
225 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
226 dst.put_u32_le(0);
227 self.base.encode(dst)?;
228
229 let len_pos = dst.len();
230 let mut length = 0u8;
231
232 dst.put_u8(length);
233
234 for chr in self.col_name.encode_utf16() {
235 length += 1;
236 dst.put_u16_le(chr);
237 }
238
239 let dst: &mut [u8] = dst.borrow_mut();
240 dst[len_pos] = length;
241
242 Ok(())
243 }
244}
245
246impl Encode<BytesMut> for BaseMetaDataColumn {
247 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
248 dst.put_u16_le(BitFlags::bits(self.flags));
249 self.ty.encode(dst)?;
250
251 Ok(())
252 }
253}
254
255#[bitflags]
257#[repr(u16)]
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub enum ColumnFlag {
260 Nullable = 1 << 0,
262 CaseSensitive = 1 << 1,
265 Updateable = 1 << 3,
267 UpdateableUnknown = 1 << 4,
269 Identity = 1 << 5,
271 Computed = 1 << 7,
273 FixedLenClrType = 1 << 10,
276 SparseColumnSet = 1 << 11,
278 Encrypted = 1 << 12,
282 Hidden = 1 << 13,
285 Key = 1 << 14,
288 NullableUnknown = 1 << 15,
290}
291
292impl TokenColMetaData<'static> {
293 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
294 where
295 R: SqlReadBytes + Unpin,
296 {
297 let column_count = src.read_u16_le().await?;
298 let mut columns = Vec::with_capacity(column_count as usize);
299
300 if column_count > 0 && column_count < 0xffff {
301 for _ in 0..column_count {
302 let base = BaseMetaDataColumn::decode(src).await?;
303 let col_name = Cow::from(src.read_b_varchar().await?);
304
305 columns.push(MetaDataColumn { base, col_name });
306 }
307 }
308
309 Ok(TokenColMetaData { columns })
310 }
311}
312
313impl<'a> TokenColMetaData<'a> {
314 pub(crate) fn columns(&self) -> impl Iterator<Item = Column> + '_ {
315 self.columns.iter().map(|x| Column {
316 name: x.col_name.to_string(),
317 column_type: ColumnType::from(&x.base.ty),
318 })
319 }
320}
321
322impl BaseMetaDataColumn {
323 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
324 where
325 R: SqlReadBytes + Unpin,
326 {
327 use VarLenType::*;
328
329 let _user_ty = src.read_u32_le().await?;
330
331 let flags = BitFlags::from_bits(src.read_u16_le().await?)
332 .map_err(|_| Error::Protocol("column metadata: invalid flags".into()))?;
333
334 let ty = TypeInfo::decode(src).await?;
335
336 if let TypeInfo::VarLenSized(cx) = ty {
337 if let Text | NText | Image = cx.r#type() {
338 let num_of_parts = src.read_u8().await?;
339
340 for _ in 0..num_of_parts {
342 src.read_us_varchar().await?;
343 }
344 };
345 };
346
347 Ok(BaseMetaDataColumn { flags, ty })
348 }
349}