1use crate::mysql::types;
16use crate::value::Value;
17use rustlavel_core::{Error, Result};
18
19pub const MAX_PAYLOAD: usize = 0xFF_FF_FF;
25
26pub const MAX_PACKET_SIZE: u32 = 1 << 24;
28
29pub const CHARSET_UTF8MB4: u8 = 45;
32
33pub const CHARSET_BINARY: u16 = 63;
35
36pub const CLIENT_LONG_PASSWORD: u32 = 0x0000_0001;
43pub const CLIENT_FOUND_ROWS: u32 = 0x0000_0002;
44pub const CLIENT_LONG_FLAG: u32 = 0x0000_0004;
45pub const CLIENT_CONNECT_WITH_DB: u32 = 0x0000_0008;
46pub const CLIENT_LOCAL_FILES: u32 = 0x0000_0080;
47pub const CLIENT_PROTOCOL_41: u32 = 0x0000_0200;
48pub const CLIENT_SSL: u32 = 0x0000_0800;
49pub const CLIENT_TRANSACTIONS: u32 = 0x0000_2000;
50pub const CLIENT_SECURE_CONNECTION: u32 = 0x0000_8000;
51pub const CLIENT_MULTI_STATEMENTS: u32 = 0x0001_0000;
52pub const CLIENT_MULTI_RESULTS: u32 = 0x0002_0000;
53pub const CLIENT_PS_MULTI_RESULTS: u32 = 0x0004_0000;
54pub const CLIENT_PLUGIN_AUTH: u32 = 0x0008_0000;
55pub const CLIENT_CONNECT_ATTRS: u32 = 0x0010_0000;
56pub const CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA: u32 = 0x0020_0000;
57pub const CLIENT_SESSION_TRACK: u32 = 0x0080_0000;
58pub const CLIENT_DEPRECATE_EOF: u32 = 0x0100_0000;
59
60pub const CLIENT_CAPABILITIES: u32 = CLIENT_LONG_PASSWORD
71 | CLIENT_LONG_FLAG
72 | CLIENT_PROTOCOL_41
73 | CLIENT_TRANSACTIONS
74 | CLIENT_SECURE_CONNECTION
75 | CLIENT_MULTI_RESULTS
76 | CLIENT_PS_MULTI_RESULTS
77 | CLIENT_PLUGIN_AUTH
78 | CLIENT_CONNECT_ATTRS
79 | CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
80 | CLIENT_SESSION_TRACK;
81
82pub const SERVER_STATUS_IN_TRANS: u16 = 0x0001;
88pub const SERVER_STATUS_AUTOCOMMIT: u16 = 0x0002;
89pub const SERVER_MORE_RESULTS_EXISTS: u16 = 0x0008;
90
91pub const NOT_NULL_FLAG: u16 = 0x0001;
94pub const UNSIGNED_FLAG: u16 = 0x0020;
95pub const BINARY_FLAG: u16 = 0x0080;
96
97pub const COM_QUIT: u8 = 0x01;
100pub const COM_QUERY: u8 = 0x03;
101pub const COM_PING: u8 = 0x0E;
102pub const COM_STMT_PREPARE: u8 = 0x16;
103pub const COM_STMT_EXECUTE: u8 = 0x17;
104pub const COM_STMT_CLOSE: u8 = 0x19;
105
106pub const CURSOR_TYPE_NO_CURSOR: u8 = 0x00;
109
110#[derive(Default)]
115pub struct Buffer {
116 bytes: Vec<u8>,
117}
118
119impl Buffer {
120 pub fn new() -> Self {
121 Buffer::default()
122 }
123
124 pub fn into_bytes(self) -> Vec<u8> {
125 self.bytes
126 }
127
128 pub fn len(&self) -> usize {
129 self.bytes.len()
130 }
131
132 pub fn is_empty(&self) -> bool {
133 self.bytes.is_empty()
134 }
135
136 pub fn u8(&mut self, value: u8) -> &mut Self {
137 self.bytes.push(value);
138 self
139 }
140
141 pub fn u16(&mut self, value: u16) -> &mut Self {
142 self.bytes.extend_from_slice(&value.to_le_bytes());
143 self
144 }
145
146 pub fn u32(&mut self, value: u32) -> &mut Self {
147 self.bytes.extend_from_slice(&value.to_le_bytes());
148 self
149 }
150
151 pub fn u64(&mut self, value: u64) -> &mut Self {
152 self.bytes.extend_from_slice(&value.to_le_bytes());
153 self
154 }
155
156 pub fn raw(&mut self, value: &[u8]) -> &mut Self {
157 self.bytes.extend_from_slice(value);
158 self
159 }
160
161 pub fn cstr(&mut self, value: &str) -> &mut Self {
163 self.bytes.extend(value.bytes().filter(|byte| *byte != 0));
166 self.bytes.push(0);
167 self
168 }
169
170 pub fn lenenc_int(&mut self, value: u64) -> &mut Self {
173 match value {
174 0..=0xFA => self.bytes.push(value as u8),
177 0xFB..=0xFFFF => {
178 self.bytes.push(0xFC);
179 self.bytes.extend_from_slice(&(value as u16).to_le_bytes());
180 }
181 0x1_0000..=0xFF_FFFF => {
182 self.bytes.push(0xFD);
183 self.bytes.extend_from_slice(&(value as u32).to_le_bytes()[..3]);
184 }
185 _ => {
186 self.bytes.push(0xFE);
187 self.bytes.extend_from_slice(&value.to_le_bytes());
188 }
189 }
190 self
191 }
192
193 pub fn lenenc_bytes(&mut self, value: &[u8]) -> &mut Self {
195 self.lenenc_int(value.len() as u64);
196 self.bytes.extend_from_slice(value);
197 self
198 }
199
200 pub fn ssl_request(&mut self, capabilities: u32) -> &mut Self {
209 self.u32(capabilities | CLIENT_SSL);
210 self.u32(MAX_PACKET_SIZE);
211 self.u8(CHARSET_UTF8MB4);
212 self.raw(&[0u8; 23]);
213 self
214 }
215
216 pub fn handshake_response(
218 &mut self,
219 capabilities: u32,
220 user: &str,
221 auth_response: &[u8],
222 database: Option<&str>,
223 plugin: &str,
224 attributes: &[(&str, &str)],
225 ) -> &mut Self {
226 self.u32(capabilities);
227 self.u32(MAX_PACKET_SIZE);
228 self.u8(CHARSET_UTF8MB4);
229 self.raw(&[0u8; 23]);
230 self.cstr(user);
231
232 if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA != 0 {
235 self.lenenc_bytes(auth_response);
236 } else {
237 self.u8(auth_response.len() as u8);
238 self.raw(auth_response);
239 }
240
241 if capabilities & CLIENT_CONNECT_WITH_DB != 0 {
242 self.cstr(database.unwrap_or(""));
243 }
244 if capabilities & CLIENT_PLUGIN_AUTH != 0 {
245 self.cstr(plugin);
246 }
247 if capabilities & CLIENT_CONNECT_ATTRS != 0 {
248 let mut pairs = Buffer::new();
249 for (key, value) in attributes {
250 pairs.lenenc_bytes(key.as_bytes());
251 pairs.lenenc_bytes(value.as_bytes());
252 }
253 let pairs = pairs.into_bytes();
254 self.lenenc_int(pairs.len() as u64);
255 self.raw(&pairs);
256 }
257 self
258 }
259
260 pub fn auth_response(&mut self, data: &[u8]) -> &mut Self {
265 self.raw(data)
266 }
267
268 pub fn com_query(&mut self, sql: &str) -> &mut Self {
271 self.u8(COM_QUERY);
272 self.raw(sql.as_bytes())
273 }
274
275 pub fn com_ping(&mut self) -> &mut Self {
278 self.u8(COM_PING)
279 }
280
281 pub fn com_quit(&mut self) -> &mut Self {
282 self.u8(COM_QUIT)
283 }
284
285 pub fn com_stmt_prepare(&mut self, sql: &str) -> &mut Self {
287 self.u8(COM_STMT_PREPARE);
288 self.raw(sql.as_bytes())
289 }
290
291 pub fn com_stmt_execute(&mut self, statement_id: u32, params: &[Value]) -> &mut Self {
297 self.u8(COM_STMT_EXECUTE);
298 self.u32(statement_id);
299 self.u8(CURSOR_TYPE_NO_CURSOR);
300 self.u32(1);
303
304 if params.is_empty() {
305 return self;
306 }
307
308 let mut null_bitmap = vec![0u8; params.len().div_ceil(8)];
312 for (index, param) in params.iter().enumerate() {
313 if param.is_null() {
314 null_bitmap[index / 8] |= 1 << (index % 8);
315 }
316 }
317 self.raw(&null_bitmap);
318
319 self.u8(1);
323 for param in params {
324 let (column_type, unsigned) = types::bind_type(param);
325 self.u8(column_type);
326 self.u8(if unsigned { 0x80 } else { 0x00 });
327 }
328 for param in params {
329 types::encode_bind(param, &mut self.bytes);
330 }
331 self
332 }
333
334 pub fn com_stmt_close(&mut self, statement_id: u32) -> &mut Self {
339 self.u8(COM_STMT_CLOSE);
340 self.u32(statement_id)
341 }
342}
343
344pub fn frame(payload: &[u8], sequence: u8) -> (Vec<u8>, u8) {
350 let mut out = Vec::with_capacity(payload.len() + 4);
351 let mut sequence = sequence;
352 let mut rest = payload;
353
354 loop {
355 let take = rest.len().min(MAX_PAYLOAD);
356 out.extend_from_slice(&(take as u32).to_le_bytes()[..3]);
357 out.push(sequence);
358 out.extend_from_slice(&rest[..take]);
359 sequence = sequence.wrapping_add(1);
360 rest = &rest[take..];
361
362 if take < MAX_PAYLOAD {
363 break;
364 }
365 }
366
367 (out, sequence)
368}
369
370#[derive(Debug, Clone, Default)]
372pub struct Column {
373 pub name: String,
376 pub original_name: String,
377 pub table: String,
378 pub charset: u16,
379 pub length: u32,
380 pub column_type: u8,
381 pub flags: u16,
382 pub decimals: u8,
383}
384
385impl Column {
386 pub fn is_binary(&self) -> bool {
392 self.charset == CHARSET_BINARY
393 }
394
395 pub fn is_unsigned(&self) -> bool {
396 self.flags & UNSIGNED_FLAG != 0
397 }
398}
399
400#[derive(Debug, Clone, Default)]
402pub struct Handshake {
403 pub server_version: String,
404 pub connection_id: u32,
405 pub scramble: Vec<u8>,
407 pub capabilities: u32,
408 pub charset: u8,
409 pub status: u16,
410 pub auth_plugin: String,
411}
412
413#[derive(Debug, Clone, Default)]
415pub struct OkPacket {
416 pub affected_rows: u64,
417 pub last_insert_id: u64,
422 pub status: u16,
423 pub warnings: u16,
424 pub info: String,
425}
426
427#[derive(Debug, Clone, Default)]
429pub struct EofPacket {
430 pub warnings: u16,
431 pub status: u16,
432}
433
434#[derive(Debug, Clone, Default)]
436pub struct ServerError {
437 pub code: u16,
438 pub sql_state: String,
441 pub message: String,
442}
443
444impl ServerError {
445 pub fn into_error(self, sql: Option<&str>) -> Error {
450 let mut text = if self.sql_state.is_empty() {
451 format!("MySQL error {}: {}", self.code, self.message)
452 } else {
453 format!("MySQL error {} ({}): {}", self.code, self.sql_state, self.message)
454 };
455
456 if let Some(sql) = sql {
457 text.push_str(&format!("\n SQL: {sql}"));
458 }
459 Error::msg(text)
460 }
461}
462
463#[derive(Debug, Clone, Default)]
465pub struct PrepareOk {
466 pub statement_id: u32,
467 pub columns: u16,
468 pub params: u16,
469 pub warnings: u16,
470}
471
472#[derive(Debug)]
478pub enum Packet {
479 Ok(OkPacket),
480 Err(ServerError),
481 Eof(EofPacket),
482 AuthSwitch { plugin: String, data: Vec<u8> },
485 AuthMoreData(Vec<u8>),
487 Other(Vec<u8>),
489}
490
491impl Packet {
492 pub fn parse(payload: &[u8]) -> Result<Packet> {
494 match payload.first() {
495 None => Err(Error::Protocol("the server sent an empty packet".into())),
496 Some(0x00) => Ok(Packet::Ok(parse_ok(payload)?)),
497 Some(0xFF) => Ok(Packet::Err(parse_err(payload)?)),
498 Some(0x01) => Ok(Packet::AuthMoreData(payload[1..].to_vec())),
499 Some(0xFE) if payload.len() < 9 => Ok(Packet::Eof(parse_eof(payload)?)),
502 Some(0xFE) => {
503 let mut reader = Reader::new(&payload[1..]);
504 Ok(Packet::AuthSwitch {
505 plugin: reader.cstr()?,
506 data: trim_trailing_nul(reader.rest()).to_vec(),
507 })
508 }
509 Some(_) => Ok(Packet::Other(payload.to_vec())),
510 }
511 }
512}
513
514pub fn is_err(payload: &[u8]) -> bool {
517 payload.first() == Some(&0xFF)
518}
519
520pub fn is_eof(payload: &[u8]) -> bool {
522 payload.first() == Some(&0xFE) && payload.len() < 9
523}
524
525pub fn parse_handshake(payload: &[u8]) -> Result<Handshake> {
527 let mut reader = Reader::new(payload);
528
529 let version = reader.u8()?;
530 if version != 10 {
531 return Err(Error::Protocol(format!(
532 "the server speaks handshake protocol {version}; this driver implements version 10. \
533 MySQL 4.0 and older are not supported."
534 )));
535 }
536
537 let mut handshake = Handshake {
538 server_version: reader.cstr()?,
539 connection_id: reader.u32()?,
540 ..Handshake::default()
541 };
542
543 handshake.scramble.extend_from_slice(reader.take(8)?);
546 reader.skip(1)?; let lower = reader.u16()? as u32;
549 handshake.capabilities = lower;
550
551 if reader.is_empty() {
553 return Ok(handshake);
554 }
555
556 handshake.charset = reader.u8()?;
557 handshake.status = reader.u16()?;
558 handshake.capabilities |= (reader.u16()? as u32) << 16;
559
560 let scramble_length = reader.u8()?;
561 reader.skip(10)?; if handshake.capabilities & CLIENT_SECURE_CONNECTION != 0 {
564 let rest = (scramble_length as usize).saturating_sub(8).max(13);
567 let part = reader.take(rest.min(reader.remaining()))?;
568 handshake.scramble.extend_from_slice(trim_trailing_nul(part));
569 }
570
571 if handshake.capabilities & CLIENT_PLUGIN_AUTH != 0 && !reader.is_empty() {
572 handshake.auth_plugin = reader.cstr().unwrap_or_default();
573 }
574
575 Ok(handshake)
576}
577
578pub fn parse_ok(payload: &[u8]) -> Result<OkPacket> {
580 let mut reader = Reader::new(payload);
581 reader.skip(1)?; Ok(OkPacket {
584 affected_rows: reader.lenenc_int()?,
585 last_insert_id: reader.lenenc_int()?,
586 status: reader.u16().unwrap_or(0),
587 warnings: reader.u16().unwrap_or(0),
588 info: String::from_utf8_lossy(reader.rest()).into_owned(),
589 })
590}
591
592pub fn parse_err(payload: &[u8]) -> Result<ServerError> {
594 let mut reader = Reader::new(payload);
595 reader.skip(1)?; let code = reader.u16()?;
598
599 let sql_state = if reader.peek() == Some(b'#') {
602 reader.skip(1)?;
603 String::from_utf8_lossy(reader.take(5)?).into_owned()
604 } else {
605 String::new()
606 };
607
608 Ok(ServerError {
609 code,
610 sql_state,
611 message: String::from_utf8_lossy(reader.rest()).into_owned(),
612 })
613}
614
615pub fn parse_eof(payload: &[u8]) -> Result<EofPacket> {
617 let mut reader = Reader::new(payload);
618 reader.skip(1)?; Ok(EofPacket {
621 warnings: reader.u16().unwrap_or(0),
622 status: reader.u16().unwrap_or(0),
623 })
624}
625
626pub fn parse_column(payload: &[u8]) -> Result<Column> {
628 let mut reader = Reader::new(payload);
629
630 reader.lenenc_bytes()?; reader.lenenc_bytes()?; let table = String::from_utf8_lossy(reader.lenenc_bytes()?).into_owned();
633 reader.lenenc_bytes()?; let name = String::from_utf8_lossy(reader.lenenc_bytes()?).into_owned();
635 let original_name = String::from_utf8_lossy(reader.lenenc_bytes()?).into_owned();
636
637 reader.lenenc_int()?; Ok(Column {
640 name,
641 original_name,
642 table,
643 charset: reader.u16()?,
644 length: reader.u32()?,
645 column_type: reader.u8()?,
646 flags: reader.u16()?,
647 decimals: reader.u8()?,
648 })
649}
650
651pub fn parse_prepare_ok(payload: &[u8]) -> Result<PrepareOk> {
653 let mut reader = Reader::new(payload);
654 reader.skip(1)?; let statement_id = reader.u32()?;
657 let columns = reader.u16()?;
658 let params = reader.u16()?;
659 reader.skip(1).ok(); Ok(PrepareOk {
662 statement_id,
663 columns,
664 params,
665 warnings: reader.u16().unwrap_or(0),
666 })
667}
668
669pub fn parse_text_row(payload: &[u8], columns: usize) -> Result<Vec<Option<Vec<u8>>>> {
675 let mut reader = Reader::new(payload);
676 let mut values = Vec::with_capacity(columns);
677
678 for _ in 0..columns {
679 values.push(reader.lenenc_bytes_or_null()?.map(<[u8]>::to_vec));
680 }
681
682 Ok(values)
683}
684
685fn trim_trailing_nul(bytes: &[u8]) -> &[u8] {
688 match bytes.last() {
689 Some(0) => &bytes[..bytes.len() - 1],
690 _ => bytes,
691 }
692}
693
694pub struct Reader<'a> {
696 bytes: &'a [u8],
697 position: usize,
698}
699
700impl<'a> Reader<'a> {
701 pub fn new(bytes: &'a [u8]) -> Self {
702 Reader { bytes, position: 0 }
703 }
704
705 pub fn is_empty(&self) -> bool {
706 self.position >= self.bytes.len()
707 }
708
709 pub fn remaining(&self) -> usize {
710 self.bytes.len().saturating_sub(self.position)
711 }
712
713 pub fn peek(&self) -> Option<u8> {
714 self.bytes.get(self.position).copied()
715 }
716
717 pub fn take(&mut self, count: usize) -> Result<&'a [u8]> {
718 let end = self.position.checked_add(count).ok_or_else(truncated)?;
719 if end > self.bytes.len() {
720 return Err(truncated());
721 }
722 let slice = &self.bytes[self.position..end];
723 self.position = end;
724 Ok(slice)
725 }
726
727 pub fn skip(&mut self, count: usize) -> Result<()> {
728 self.take(count).map(|_| ())
729 }
730
731 pub fn u8(&mut self) -> Result<u8> {
732 Ok(self.take(1)?[0])
733 }
734
735 pub fn u16(&mut self) -> Result<u16> {
736 Ok(u16::from_le_bytes(self.take(2)?.try_into().expect("2 bytes")))
737 }
738
739 pub fn u32(&mut self) -> Result<u32> {
740 Ok(u32::from_le_bytes(self.take(4)?.try_into().expect("4 bytes")))
741 }
742
743 pub fn u64(&mut self) -> Result<u64> {
744 Ok(u64::from_le_bytes(self.take(8)?.try_into().expect("8 bytes")))
745 }
746
747 pub fn lenenc_int(&mut self) -> Result<u64> {
750 match self.u8()? {
751 marker @ 0..=0xFA => Ok(marker as u64),
752 0xFB => Err(Error::Protocol("a NULL where a length was expected".into())),
753 0xFC => Ok(self.u16()? as u64),
754 0xFD => {
755 let bytes = self.take(3)?;
756 Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], 0]) as u64)
757 }
758 _ => self.u64(),
759 }
760 }
761
762 pub fn lenenc_bytes(&mut self) -> Result<&'a [u8]> {
763 let length = self.lenenc_int()? as usize;
764 self.take(length)
765 }
766
767 pub fn lenenc_bytes_or_null(&mut self) -> Result<Option<&'a [u8]>> {
768 if self.peek() == Some(0xFB) {
769 self.position += 1;
770 return Ok(None);
771 }
772 self.lenenc_bytes().map(Some)
773 }
774
775 pub fn cstr(&mut self) -> Result<String> {
776 let start = self.position;
777 while self.position < self.bytes.len() && self.bytes[self.position] != 0 {
778 self.position += 1;
779 }
780 if self.position >= self.bytes.len() {
781 return Err(Error::Protocol("unterminated string from the server".into()));
782 }
783 let text = String::from_utf8_lossy(&self.bytes[start..self.position]).into_owned();
784 self.position += 1;
785 Ok(text)
786 }
787
788 pub fn rest(&mut self) -> &'a [u8] {
789 let slice = &self.bytes[self.position.min(self.bytes.len())..];
790 self.position = self.bytes.len();
791 slice
792 }
793}
794
795fn truncated() -> Error {
796 Error::Protocol("truncated packet from the server".into())
797}
798
799#[cfg(test)]
800mod tests {
801 use super::*;
802
803 #[test]
804 fn a_frame_carries_a_little_endian_length_and_a_sequence_id() {
805 let (bytes, next) = frame(b"select 1", 0);
806
807 assert_eq!(&bytes[..3], &[8, 0, 0]);
808 assert_eq!(bytes[3], 0);
809 assert_eq!(&bytes[4..], b"select 1");
810 assert_eq!(next, 1);
811 }
812
813 #[test]
814 fn a_payload_longer_than_one_frame_is_split_and_terminated() {
815 let payload = vec![0x41u8; MAX_PAYLOAD + 5];
816 let (bytes, next) = frame(&payload, 3);
817
818 assert_eq!(&bytes[..3], &[0xFF, 0xFF, 0xFF]);
820 assert_eq!(bytes[3], 3);
821 let second = &bytes[4 + MAX_PAYLOAD..];
822 assert_eq!(&second[..3], &[5, 0, 0]);
823 assert_eq!(second[3], 4);
824 assert_eq!(next, 5);
825 }
826
827 #[test]
828 fn a_length_encoded_integer_uses_each_of_its_four_widths() {
829 assert_eq!(Buffer::new().lenenc_int(250).clone_bytes(), vec![250]);
831 assert_eq!(Buffer::new().lenenc_int(251).clone_bytes(), vec![0xFC, 251, 0]);
833 assert_eq!(Buffer::new().lenenc_int(65_535).clone_bytes(), vec![0xFC, 0xFF, 0xFF]);
834 assert_eq!(Buffer::new().lenenc_int(65_536).clone_bytes(), vec![0xFD, 0, 0, 1]);
836 assert_eq!(
837 Buffer::new().lenenc_int(16_777_215).clone_bytes(),
838 vec![0xFD, 0xFF, 0xFF, 0xFF]
839 );
840 assert_eq!(
842 Buffer::new().lenenc_int(16_777_216).clone_bytes(),
843 vec![0xFE, 0, 0, 0, 1, 0, 0, 0, 0]
844 );
845 }
846
847 #[test]
848 fn a_length_encoded_integer_survives_a_round_trip_at_every_width() {
849 for value in [0u64, 1, 250, 251, 65_535, 65_536, 16_777_215, 16_777_216, u64::MAX] {
850 let bytes = Buffer::new().lenenc_int(value).clone_bytes();
851 let decoded = Reader::new(&bytes).lenenc_int().unwrap();
852 assert_eq!(decoded, value, "for {value}");
853 }
854 }
855
856 #[test]
857 fn a_length_encoded_string_carries_its_own_length() {
858 let bytes = Buffer::new().lenenc_bytes(b"ada").clone_bytes();
859 assert_eq!(bytes, b"\x03ada");
860
861 let mut reader = Reader::new(&bytes);
862 assert_eq!(reader.lenenc_bytes().unwrap(), b"ada");
863 }
864
865 #[test]
866 fn parses_a_handshake_v10() {
867 let mut payload = vec![10u8];
868 payload.extend_from_slice(b"8.0.36\0");
869 payload.extend_from_slice(&7u32.to_le_bytes());
870 payload.extend_from_slice(b"12345678"); payload.push(0); payload.extend_from_slice(&((CLIENT_CAPABILITIES & 0xFFFF) as u16).to_le_bytes());
873 payload.push(CHARSET_UTF8MB4);
874 payload.extend_from_slice(&SERVER_STATUS_AUTOCOMMIT.to_le_bytes());
875 payload.extend_from_slice(&((CLIENT_CAPABILITIES >> 16) as u16).to_le_bytes());
876 payload.push(21); payload.extend_from_slice(&[0u8; 10]);
878 payload.extend_from_slice(b"abcdefghijkl\0"); payload.extend_from_slice(b"caching_sha2_password\0");
880
881 let handshake = parse_handshake(&payload).unwrap();
882
883 assert_eq!(handshake.server_version, "8.0.36");
884 assert_eq!(handshake.connection_id, 7);
885 assert_eq!(handshake.scramble, b"12345678abcdefghijkl");
886 assert_eq!(handshake.auth_plugin, "caching_sha2_password");
887 assert!(handshake.capabilities & CLIENT_PLUGIN_AUTH != 0);
888 }
889
890 #[test]
891 fn refuses_a_handshake_from_a_server_too_old_to_talk_to() {
892 let error = parse_handshake(&[9, 0]).unwrap_err().to_string();
893 assert!(error.contains("version 10"), "{error}");
894 }
895
896 #[test]
897 fn parses_an_ok_packet_including_the_generated_key() {
898 let payload = [0x00, 0x01, 0x2A, 0x02, 0x00, 0x00, 0x00];
900
901 let ok = parse_ok(&payload).unwrap();
902 assert_eq!(ok.affected_rows, 1);
903 assert_eq!(ok.last_insert_id, 42);
904 assert_eq!(ok.status, SERVER_STATUS_AUTOCOMMIT);
905 }
906
907 #[test]
908 fn an_error_packet_becomes_an_error_that_names_the_sql_state() {
909 let mut payload = vec![0xFF];
910 payload.extend_from_slice(&1146u16.to_le_bytes());
911 payload.push(b'#');
912 payload.extend_from_slice(b"42S02");
913 payload.extend_from_slice(b"Table 'blog.nope' doesn't exist");
914
915 let error = parse_err(&payload).unwrap();
916 assert_eq!(error.code, 1146);
917 assert_eq!(error.sql_state, "42S02");
918
919 let rendered = error.into_error(Some("select * from nope")).to_string();
920 assert!(rendered.contains("1146"), "{rendered}");
921 assert!(rendered.contains("42S02"), "{rendered}");
922 assert!(rendered.contains("SQL: select * from nope"), "{rendered}");
923 }
924
925 #[test]
926 fn an_error_without_a_sql_state_still_reports_its_code() {
927 let mut payload = vec![0xFF];
928 payload.extend_from_slice(&1045u16.to_le_bytes());
929 payload.extend_from_slice(b"Access denied");
930
931 let error = parse_err(&payload).unwrap();
932 assert_eq!(error.code, 1045);
933 assert!(error.sql_state.is_empty());
934 assert!(error.into_error(None).to_string().contains("1045"));
935 }
936
937 #[test]
938 fn an_eof_packet_is_told_from_a_row_by_its_length() {
939 let eof = [0xFE, 0x00, 0x00, 0x02, 0x00];
940 assert!(is_eof(&eof));
941 assert_eq!(parse_eof(&eof).unwrap().status, SERVER_STATUS_AUTOCOMMIT);
942
943 let row = [0xFE, 1, 2, 3, 4, 5, 6, 7, 8, 9];
945 assert!(!is_eof(&row));
946 }
947
948 #[test]
949 fn parses_a_column_definition() {
950 let mut payload = Buffer::new();
951 payload.lenenc_bytes(b"def");
952 payload.lenenc_bytes(b"blog");
953 payload.lenenc_bytes(b"users");
954 payload.lenenc_bytes(b"users");
955 payload.lenenc_bytes(b"total");
956 payload.lenenc_bytes(b"id");
957 payload.lenenc_int(0x0C);
958 payload.u16(CHARSET_BINARY);
959 payload.u32(20);
960 payload.u8(types::LONGLONG);
961 payload.u16(UNSIGNED_FLAG | NOT_NULL_FLAG);
962 payload.u8(0);
963 payload.u16(0);
964
965 let column = parse_column(&payload.clone_bytes()).unwrap();
966
967 assert_eq!(column.name, "total");
970 assert_eq!(column.original_name, "id");
971 assert_eq!(column.table, "users");
972 assert_eq!(column.column_type, types::LONGLONG);
973 assert!(column.is_unsigned());
974 assert!(column.is_binary());
975 }
976
977 #[test]
978 fn parses_a_prepare_response() {
979 let mut payload = vec![0x00];
980 payload.extend_from_slice(&9u32.to_le_bytes());
981 payload.extend_from_slice(&3u16.to_le_bytes());
982 payload.extend_from_slice(&2u16.to_le_bytes());
983 payload.push(0);
984 payload.extend_from_slice(&0u16.to_le_bytes());
985
986 let prepared = parse_prepare_ok(&payload).unwrap();
987 assert_eq!(prepared.statement_id, 9);
988 assert_eq!(prepared.columns, 3);
989 assert_eq!(prepared.params, 2);
990 }
991
992 #[test]
993 fn a_text_row_distinguishes_null_from_the_empty_string() {
994 let payload = [0x03, b'a', b'd', b'a', 0xFB, 0x00];
995
996 let values = parse_text_row(&payload, 3).unwrap();
997 assert_eq!(values[0].as_deref(), Some(&b"ada"[..]));
998 assert_eq!(values[1], None);
999 assert_eq!(values[2].as_deref(), Some(&b""[..]));
1000 }
1001
1002 #[test]
1003 fn an_ssl_request_is_exactly_thirty_two_bytes() {
1004 let mut buffer = Buffer::new();
1008 buffer.ssl_request(CLIENT_PROTOCOL_41);
1009 let bytes = buffer.into_bytes();
1010
1011 assert_eq!(bytes.len(), 32);
1012 assert_eq!(&bytes[9..32], &[0u8; 23], "the 23-byte filler must be zeroed");
1013 }
1014
1015 #[test]
1016 fn an_ssl_request_sets_the_ssl_flag_whatever_it_was_given() {
1017 let mut buffer = Buffer::new();
1018 buffer.ssl_request(CLIENT_PROTOCOL_41);
1019 let flags = u32::from_le_bytes(buffer.into_bytes()[0..4].try_into().unwrap());
1020
1021 assert!(flags & CLIENT_SSL != 0, "without this the server never starts a handshake");
1022 assert!(flags & CLIENT_PROTOCOL_41 != 0, "and it must not drop what it was given");
1023 }
1024
1025 #[test]
1026 fn an_ssl_request_is_the_prefix_of_the_handshake_response() {
1027 let mut request = Buffer::new();
1031 request.ssl_request(CLIENT_PROTOCOL_41 | CLIENT_SSL);
1032
1033 let mut full = Buffer::new();
1034 full.handshake_response(
1035 CLIENT_PROTOCOL_41 | CLIENT_SSL,
1036 "someone",
1037 b"digest",
1038 None,
1039 "caching_sha2_password",
1040 &[],
1041 );
1042
1043 assert_eq!(request.into_bytes(), full.into_bytes()[..32]);
1044 }
1045
1046 #[test]
1047 fn a_handshake_response_names_the_plugin_and_the_database() {
1048 let bytes = Buffer::new()
1049 .handshake_response(
1050 CLIENT_CAPABILITIES | CLIENT_CONNECT_WITH_DB,
1051 "ada",
1052 &[0xAA; 20],
1053 Some("blog"),
1054 "mysql_native_password",
1055 &[("program_name", "rustlavel")],
1056 )
1057 .clone_bytes();
1058
1059 let capabilities = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
1060 assert!(capabilities & CLIENT_CONNECT_WITH_DB != 0);
1061 assert_eq!(u32::from_le_bytes(bytes[4..8].try_into().unwrap()), MAX_PACKET_SIZE);
1062 assert_eq!(bytes[8], CHARSET_UTF8MB4);
1063 assert_eq!(&bytes[9..32], &[0u8; 23]);
1064 assert_eq!(&bytes[32..36], b"ada\0");
1065 assert_eq!(bytes[36], 20);
1067 assert!(bytes.windows(5).any(|w| w == b"blog\0"));
1068 assert!(bytes.windows(22).any(|w| w == b"mysql_native_password\0"));
1069 assert!(bytes.windows(12).any(|w| w == b"program_name"));
1070 }
1071
1072 #[test]
1073 fn a_nul_in_a_username_cannot_truncate_the_field() {
1074 let bytes = Buffer::new().cstr("ada\0admin").clone_bytes();
1075 assert_eq!(bytes, b"adaadmin\0");
1076 }
1077
1078 #[test]
1079 fn execute_marks_null_parameters_in_a_bitmap_and_sends_no_value_for_them() {
1080 let bytes = Buffer::new()
1081 .com_stmt_execute(7, &[Value::Int(1), Value::Null, Value::Int(2)])
1082 .clone_bytes();
1083
1084 assert_eq!(bytes[0], COM_STMT_EXECUTE);
1085 assert_eq!(u32::from_le_bytes(bytes[1..5].try_into().unwrap()), 7);
1086 assert_eq!(bytes[5], CURSOR_TYPE_NO_CURSOR);
1087 assert_eq!(u32::from_le_bytes(bytes[6..10].try_into().unwrap()), 1);
1088
1089 assert_eq!(bytes[10], 0b0000_0010);
1091 assert_eq!(bytes[11], 1, "new parameters are bound");
1092
1093 assert_eq!(&bytes[12..18], &[types::LONGLONG, 0, types::NULL, 0, types::LONGLONG, 0]);
1095 assert_eq!(bytes.len(), 18 + 16);
1096 }
1097
1098 #[test]
1099 fn execute_without_parameters_stops_before_the_bitmap() {
1100 let bytes = Buffer::new().com_stmt_execute(7, &[]).clone_bytes();
1101 assert_eq!(bytes.len(), 10);
1102 }
1103
1104 #[test]
1105 fn builds_the_small_commands() {
1106 assert_eq!(Buffer::new().com_query("select 1").clone_bytes(), b"\x03select 1");
1107 assert_eq!(Buffer::new().com_ping().clone_bytes(), vec![COM_PING]);
1108 assert_eq!(Buffer::new().com_quit().clone_bytes(), vec![COM_QUIT]);
1109 assert_eq!(
1110 Buffer::new().com_stmt_prepare("select ?").clone_bytes(),
1111 b"\x16select ?"
1112 );
1113 assert_eq!(
1114 Buffer::new().com_stmt_close(5).clone_bytes(),
1115 vec![COM_STMT_CLOSE, 5, 0, 0, 0]
1116 );
1117 }
1118
1119 #[test]
1120 fn a_truncated_packet_is_a_protocol_error_rather_than_a_panic() {
1121 assert!(parse_column(&[0x03, b'd']).is_err());
1122 assert!(parse_handshake(&[10]).is_err());
1123 assert!(Packet::parse(&[]).is_err());
1124 assert!(Reader::new(&[0xFC, 1]).lenenc_int().is_err());
1125 }
1126
1127 #[test]
1128 fn classifies_the_packets_a_command_can_be_answered_with() {
1129 assert!(matches!(Packet::parse(&[0x00, 0, 0, 2, 0, 0, 0]).unwrap(), Packet::Ok(_)));
1130 assert!(matches!(Packet::parse(&[0xFF, 0x15, 0x04]).unwrap(), Packet::Err(_)));
1131 assert!(matches!(Packet::parse(&[0xFE, 0, 0, 2, 0]).unwrap(), Packet::Eof(_)));
1132 assert!(matches!(Packet::parse(&[0x01, 3]).unwrap(), Packet::AuthMoreData(data) if data == [3]));
1133
1134 let mut switch = vec![0xFE];
1135 switch.extend_from_slice(b"mysql_native_password\0");
1136 switch.extend_from_slice(b"0123456789abcdefghij\0");
1137 match Packet::parse(&switch).unwrap() {
1138 Packet::AuthSwitch { plugin, data } => {
1139 assert_eq!(plugin, "mysql_native_password");
1140 assert_eq!(data, b"0123456789abcdefghij");
1141 }
1142 other => panic!("expected an auth switch, got {other:?}"),
1143 }
1144 }
1145
1146 #[test]
1147 fn the_negotiated_capabilities_leave_out_the_dangerous_ones() {
1148 assert_eq!(CLIENT_CAPABILITIES & CLIENT_MULTI_STATEMENTS, 0);
1151 assert_eq!(CLIENT_CAPABILITIES & CLIENT_LOCAL_FILES, 0);
1152 assert_ne!(CLIENT_CAPABILITIES & CLIENT_PROTOCOL_41, 0);
1154 assert_ne!(CLIENT_CAPABILITIES & CLIENT_PLUGIN_AUTH, 0);
1155 assert_ne!(CLIENT_CAPABILITIES & CLIENT_TRANSACTIONS, 0);
1156 assert_eq!(CLIENT_CAPABILITIES & CLIENT_DEPRECATE_EOF, 0);
1158 assert_eq!(CLIENT_CAPABILITIES & CLIENT_SSL, 0);
1159 }
1160
1161 impl Buffer {
1162 fn clone_bytes(&self) -> Vec<u8> {
1163 self.bytes.clone()
1164 }
1165 }
1166}