rbdc_mysql/protocol/text/
column.rs

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