tiberius/tds/codec/token/
token_col_metadata.rs

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/// A setting a column can hold.
256#[bitflags]
257#[repr(u16)]
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub enum ColumnFlag {
260    /// The column can be null.
261    Nullable = 1 << 0,
262    /// Set for string columns with binary collation and always for the XML data
263    /// type.
264    CaseSensitive = 1 << 1,
265    /// If column is writeable.
266    Updateable = 1 << 3,
267    /// Column modification status unknown.
268    UpdateableUnknown = 1 << 4,
269    /// Column is an identity.
270    Identity = 1 << 5,
271    /// Coulumn is computed.
272    Computed = 1 << 7,
273    /// Column is a fixed-length common language runtime user-defined type (CLR
274    /// UDT).
275    FixedLenClrType = 1 << 10,
276    /// Column is the special XML column for the sparse column set.
277    SparseColumnSet = 1 << 11,
278    /// Column is encrypted transparently and has to be decrypted to view the
279    /// plaintext value. This flag is valid when the column encryption feature
280    /// is negotiated between client and server and is turned on.
281    Encrypted = 1 << 12,
282    /// Column is part of a hidden primary key created to support a T-SQL SELECT
283    /// statement containing FOR BROWSE.
284    Hidden = 1 << 13,
285    /// Column is part of a primary key for the row and the T-SQL SELECT
286    /// statement contains FOR BROWSE.
287    Key = 1 << 14,
288    /// It is unknown whether the column might be nullable.
289    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                // table name
341                for _ in 0..num_of_parts {
342                    src.read_us_varchar().await?;
343                }
344            };
345        };
346
347        Ok(BaseMetaDataColumn { flags, ty })
348    }
349}