sqlx_mysql/protocol/text/
column.rs

1use std::str::from_utf8;
2
3use bitflags::bitflags;
4use bytes::{Buf, Bytes};
5
6use crate::error::Error;
7use crate::io::MySqlBufExt;
8use crate::io::ProtocolDecode;
9use crate::protocol::Capabilities;
10
11// https://dev.mysql.com/doc/dev/mysql-server/8.0.12/group__group__cs__column__definition__flags.html
12
13bitflags! {
14    #[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
15    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16    pub(crate) struct ColumnFlags: u16 {
17        /// Field can't be `NULL`.
18        const NOT_NULL = 1;
19
20        /// Field is part of a primary key.
21        const PRIMARY_KEY = 2;
22
23        /// Field is part of a unique key.
24        const UNIQUE_KEY = 4;
25
26        /// Field is part of a multi-part unique or primary key.
27        const MULTIPLE_KEY = 8;
28
29        /// Field is a blob.
30        const BLOB = 16;
31
32        /// Field is unsigned.
33        const UNSIGNED = 32;
34
35        /// Field is zero filled.
36        const ZEROFILL = 64;
37
38        /// Field is binary.
39        const BINARY = 128;
40
41        /// Field is an enumeration.
42        const ENUM = 256;
43
44        /// Field is an auto-incement field.
45        const AUTO_INCREMENT = 512;
46
47        /// Field is a timestamp.
48        const TIMESTAMP = 1024;
49
50        /// Field is a set.
51        const SET = 2048;
52
53        /// Field does not have a default value.
54        const NO_DEFAULT_VALUE = 4096;
55
56        /// Field is set to NOW on UPDATE.
57        const ON_UPDATE_NOW = 8192;
58
59        /// Field is a number.
60        const NUM = 32768;
61    }
62}
63
64// https://dev.mysql.com/doc/internals/en/com-query-response.html#column-type
65
66#[derive(Debug, Copy, Clone, PartialEq)]
67#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))]
68#[repr(u8)]
69pub enum ColumnType {
70    Decimal = 0x00,
71    Tiny = 0x01,
72    Short = 0x02,
73    Long = 0x03,
74    Float = 0x04,
75    Double = 0x05,
76    Null = 0x06,
77    Timestamp = 0x07,
78    LongLong = 0x08,
79    Int24 = 0x09,
80    Date = 0x0a,
81    Time = 0x0b,
82    Datetime = 0x0c,
83    Year = 0x0d,
84    VarChar = 0x0f,
85    Bit = 0x10,
86    Json = 0xf5,
87    NewDecimal = 0xf6,
88    Enum = 0xf7,
89    Set = 0xf8,
90    TinyBlob = 0xf9,
91    MediumBlob = 0xfa,
92    LongBlob = 0xfb,
93    Blob = 0xfc,
94    VarString = 0xfd,
95    String = 0xfe,
96    Geometry = 0xff,
97}
98
99// https://dev.mysql.com/doc/dev/mysql-server/8.0.12/page_protocol_com_query_response_text_resultset_column_definition.html
100// https://mariadb.com/kb/en/resultset/#column-definition-packet
101// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnDefinition41
102
103#[derive(Debug)]
104pub(crate) struct ColumnDefinition {
105    #[allow(unused)]
106    catalog: Bytes,
107    #[allow(unused)]
108    schema: Bytes,
109    #[allow(unused)]
110    table_alias: Bytes,
111    #[allow(unused)]
112    table: Bytes,
113    alias: Bytes,
114    name: Bytes,
115    #[allow(unused)]
116    pub(crate) collation: u16,
117    pub(crate) max_size: u32,
118    pub(crate) r#type: ColumnType,
119    pub(crate) flags: ColumnFlags,
120    #[allow(unused)]
121    decimals: u8,
122}
123
124impl ColumnDefinition {
125    // NOTE: strings in-protocol are transmitted according to the client character set
126    //       as this is UTF-8, all these strings should be UTF-8
127
128    pub(crate) fn name(&self) -> Result<&str, Error> {
129        from_utf8(&self.name).map_err(Error::protocol)
130    }
131
132    pub(crate) fn alias(&self) -> Result<&str, Error> {
133        from_utf8(&self.alias).map_err(Error::protocol)
134    }
135}
136
137impl ProtocolDecode<'_, Capabilities> for ColumnDefinition {
138    fn decode_with(mut buf: Bytes, _: Capabilities) -> Result<Self, Error> {
139        let catalog = buf.get_bytes_lenenc()?;
140        let schema = buf.get_bytes_lenenc()?;
141        let table_alias = buf.get_bytes_lenenc()?;
142        let table = buf.get_bytes_lenenc()?;
143        let alias = buf.get_bytes_lenenc()?;
144        let name = buf.get_bytes_lenenc()?;
145        let _next_len = buf.get_uint_lenenc(); // always 0x0c
146        let collation = buf.get_u16_le();
147        let max_size = buf.get_u32_le();
148        let type_id = buf.get_u8();
149        let flags = buf.get_u16_le();
150        let decimals = buf.get_u8();
151
152        Ok(Self {
153            catalog,
154            schema,
155            table_alias,
156            table,
157            alias,
158            name,
159            collation,
160            max_size,
161            r#type: ColumnType::try_from_u16(type_id)?,
162            flags: ColumnFlags::from_bits_truncate(flags),
163            decimals,
164        })
165    }
166}
167
168impl ColumnType {
169    pub(crate) fn name(self, flags: ColumnFlags, max_size: Option<u32>) -> &'static str {
170        let is_binary = flags.contains(ColumnFlags::BINARY);
171        let is_unsigned = flags.contains(ColumnFlags::UNSIGNED);
172        let is_enum = flags.contains(ColumnFlags::ENUM);
173
174        match self {
175            ColumnType::Tiny if max_size == Some(1) => "BOOLEAN",
176            ColumnType::Tiny if is_unsigned => "TINYINT UNSIGNED",
177            ColumnType::Short if is_unsigned => "SMALLINT UNSIGNED",
178            ColumnType::Long if is_unsigned => "INT UNSIGNED",
179            ColumnType::Int24 if is_unsigned => "MEDIUMINT UNSIGNED",
180            ColumnType::LongLong if is_unsigned => "BIGINT UNSIGNED",
181            ColumnType::Tiny => "TINYINT",
182            ColumnType::Short => "SMALLINT",
183            ColumnType::Long => "INT",
184            ColumnType::Int24 => "MEDIUMINT",
185            ColumnType::LongLong => "BIGINT",
186            ColumnType::Float => "FLOAT",
187            ColumnType::Double => "DOUBLE",
188            ColumnType::Null => "NULL",
189            ColumnType::Timestamp => "TIMESTAMP",
190            ColumnType::Date => "DATE",
191            ColumnType::Time => "TIME",
192            ColumnType::Datetime => "DATETIME",
193            ColumnType::Year => "YEAR",
194            ColumnType::Bit => "BIT",
195            ColumnType::Enum => "ENUM",
196            ColumnType::Set => "SET",
197            ColumnType::Decimal | ColumnType::NewDecimal => "DECIMAL",
198            ColumnType::Geometry => "GEOMETRY",
199            ColumnType::Json => "JSON",
200
201            ColumnType::String if is_binary => "BINARY",
202            ColumnType::String if is_enum => "ENUM",
203            ColumnType::VarChar | ColumnType::VarString if is_binary => "VARBINARY",
204
205            ColumnType::String => "CHAR",
206            ColumnType::VarChar | ColumnType::VarString => "VARCHAR",
207
208            ColumnType::TinyBlob if is_binary => "TINYBLOB",
209            ColumnType::TinyBlob => "TINYTEXT",
210
211            ColumnType::Blob if is_binary => "BLOB",
212            ColumnType::Blob => "TEXT",
213
214            ColumnType::MediumBlob if is_binary => "MEDIUMBLOB",
215            ColumnType::MediumBlob => "MEDIUMTEXT",
216
217            ColumnType::LongBlob if is_binary => "LONGBLOB",
218            ColumnType::LongBlob => "LONGTEXT",
219        }
220    }
221
222    pub(crate) fn try_from_u16(id: u8) -> Result<Self, Error> {
223        Ok(match id {
224            0x00 => ColumnType::Decimal,
225            0x01 => ColumnType::Tiny,
226            0x02 => ColumnType::Short,
227            0x03 => ColumnType::Long,
228            0x04 => ColumnType::Float,
229            0x05 => ColumnType::Double,
230            0x06 => ColumnType::Null,
231            0x07 => ColumnType::Timestamp,
232            0x08 => ColumnType::LongLong,
233            0x09 => ColumnType::Int24,
234            0x0a => ColumnType::Date,
235            0x0b => ColumnType::Time,
236            0x0c => ColumnType::Datetime,
237            0x0d => ColumnType::Year,
238            // [internal] 0x0e => ColumnType::NewDate,
239            0x0f => ColumnType::VarChar,
240            0x10 => ColumnType::Bit,
241            // [internal] 0x11 => ColumnType::Timestamp2,
242            // [internal] 0x12 => ColumnType::Datetime2,
243            // [internal] 0x13 => ColumnType::Time2,
244            0xf5 => ColumnType::Json,
245            0xf6 => ColumnType::NewDecimal,
246            0xf7 => ColumnType::Enum,
247            0xf8 => ColumnType::Set,
248            0xf9 => ColumnType::TinyBlob,
249            0xfa => ColumnType::MediumBlob,
250            0xfb => ColumnType::LongBlob,
251            0xfc => ColumnType::Blob,
252            0xfd => ColumnType::VarString,
253            0xfe => ColumnType::String,
254            0xff => ColumnType::Geometry,
255
256            _ => {
257                return Err(err_protocol!("unknown column type 0x{:02x}", id));
258            }
259        })
260    }
261}