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}