1use bytes::BufMut;
10
11use std::{convert::TryFrom, fmt, io, marker::PhantomData, str::from_utf8};
12
13use crate::{
14 constants::{ColumnFlags, ColumnType},
15 io::{BufMutExt, ParseBuf},
16 misc::{
17 lenenc_str_len,
18 raw::bytes::{LenEnc, RawBytes},
19 unexpected_buf_eof,
20 },
21 proto::{MyDeserialize, MySerialize},
22 value::Value::*,
23};
24
25pub mod convert;
26pub mod json;
27
28pub trait SerializationSide {
30 const BIT_OFFSET: usize;
32}
33
34#[derive(Debug, Clone, Copy, Eq, PartialEq)]
36pub struct ServerSide;
37
38impl SerializationSide for ServerSide {
39 const BIT_OFFSET: usize = 2;
40}
41
42pub struct ClientSide;
44
45impl SerializationSide for ClientSide {
46 const BIT_OFFSET: usize = 0;
47}
48
49pub struct TextValue;
51
52pub struct BinValue;
54
55#[derive(Clone, PartialEq, PartialOrd)]
59pub enum Value {
60 NULL,
61 Bytes(Vec<u8>),
62 Int(i64),
63 UInt(u64),
64 Float(f32),
65 Double(f64),
66 Date(u16, u8, u8, u8, u8, u8, u32),
68 Time(bool, u32, u8, u8, u8, u32),
70}
71
72impl MySerialize for Value {
73 fn serialize(&self, buf: &mut Vec<u8>) {
74 match self {
75 Self::NULL => (),
76 Value::Bytes(x) => {
77 buf.put_lenenc_str(x);
78 }
79 Value::Int(x) => {
80 buf.put_i64_le(*x);
81 }
82 Value::UInt(x) => {
83 buf.put_u64_le(*x);
84 }
85 Value::Float(x) => {
86 buf.put_f32_le(*x);
87 }
88 Value::Double(x) => {
89 buf.put_f64_le(*x);
90 }
91 Value::Date(0u16, 0u8, 0u8, 0u8, 0u8, 0u8, 0u32) => {
92 buf.put_u8(0);
93 }
94 Value::Date(year, mon, day, 0u8, 0u8, 0u8, 0u32) => {
95 buf.put_u8(4);
96 buf.put_u16_le(*year);
97 buf.put_u8(*mon);
98 buf.put_u8(*day);
99 }
100 Value::Date(year, mon, day, hour, min, sec, 0u32) => {
101 buf.put_u8(7);
102 buf.put_u16_le(*year);
103 buf.put_u8(*mon);
104 buf.put_u8(*day);
105 buf.put_u8(*hour);
106 buf.put_u8(*min);
107 buf.put_u8(*sec);
108 }
109 Value::Date(year, mon, day, hour, min, sec, usec) => {
110 buf.put_u8(11);
111 buf.put_u16_le(*year);
112 buf.put_u8(*mon);
113 buf.put_u8(*day);
114 buf.put_u8(*hour);
115 buf.put_u8(*min);
116 buf.put_u8(*sec);
117 buf.put_u32_le(*usec);
118 }
119 Value::Time(_, 0u32, 0u8, 0u8, 0u8, 0u32) => {
120 buf.put_u8(0);
121 }
122 Value::Time(neg, d, h, m, s, 0u32) => {
123 buf.put_u8(8);
124 buf.put_u8(if *neg { 1 } else { 0 });
125 buf.put_u32_le(*d);
126 buf.put_u8(*h);
127 buf.put_u8(*m);
128 buf.put_u8(*s);
129 }
130 Value::Time(neg, days, hours, mins, secs, usecs) => {
131 buf.put_u8(12);
132 buf.put_u8(if *neg { 1 } else { 0 });
133 buf.put_u32_le(*days);
134 buf.put_u8(*hours);
135 buf.put_u8(*mins);
136 buf.put_u8(*secs);
137 buf.put_u32_le(*usecs);
138 }
139 }
140 }
141}
142
143#[derive(Debug, Clone, PartialEq)]
147pub struct ValueDeserializer<T>(pub Value, PhantomData<T>);
148
149impl<'de> MyDeserialize<'de> for ValueDeserializer<TextValue> {
150 const SIZE: Option<usize> = None;
151 type Ctx = ();
152
153 fn deserialize((): Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result<Self> {
154 let value = Value::deserialize_text(buf)?;
155 Ok(Self(value, PhantomData))
156 }
157}
158
159impl<'de> MyDeserialize<'de> for ValueDeserializer<BinValue> {
160 const SIZE: Option<usize> = None;
161 type Ctx = (ColumnType, ColumnFlags);
162
163 fn deserialize((col_type, col_flags): Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result<Self> {
164 let value = Value::deserialize_bin((col_type, col_flags), buf)?;
165 Ok(Self(value, PhantomData))
166 }
167}
168
169fn escaped(input: &str, no_backslash_escape: bool) -> String {
171 let mut output = String::with_capacity(input.len());
172 output.push('\'');
173 if no_backslash_escape {
174 for c in input.chars() {
175 if c == '\'' {
176 output.push('\'');
177 output.push('\'');
178 } else {
179 output.push(c);
180 }
181 }
182 } else {
183 for c in input.chars() {
184 if c == '\x00' {
185 output.push('\\');
186 output.push('0');
187 } else if c == '\n' {
188 output.push('\\');
189 output.push('n');
190 } else if c == '\r' {
191 output.push('\\');
192 output.push('r');
193 } else if c == '\\' || c == '\'' || c == '"' {
194 output.push('\\');
195 output.push(c);
196 } else if c == '\x1a' {
197 output.push('\\');
198 output.push('Z');
199 } else {
200 output.push(c);
201 }
202 }
203 }
204 output.push('\'');
205 output
206}
207
208macro_rules! de_num {
209 ($name:ident, $i:ident, $u:ident) => {
210 fn $name(unsigned: bool, buf: &mut ParseBuf<'_>) -> io::Result<Self> {
211 if unsigned {
212 buf.$u()
213 .ok_or_else(unexpected_buf_eof)
214 .map(|x| Int(x as i64))
215 } else {
216 buf.$i()
217 .ok_or_else(unexpected_buf_eof)
218 .map(|x| Int(x as i64))
219 }
220 }
221 };
222}
223
224impl Value {
225 pub fn bin_len(&self) -> u64 {
227 match self {
228 Value::NULL => 0,
229 Value::Bytes(x) => lenenc_str_len(x),
230 Value::Int(_) => 8,
231 Value::UInt(_) => 8,
232 Value::Float(_) => 4,
233 Value::Double(_) => 8,
234 Value::Date(0u16, 0u8, 0u8, 0u8, 0u8, 0u8, 0u32) => 1,
235 Value::Date(_, _, _, 0u8, 0u8, 0u8, 0u32) => 5,
236 Value::Date(_, _, _, _, _, _, 0u32) => 8,
237 Value::Date(_, _, _, _, _, _, _) => 12,
238 Value::Time(_, 0u32, 0u8, 0u8, 0u8, 0u32) => 1,
239 Value::Time(_, _, _, _, _, 0u32) => 9,
240 Value::Time(_, _, _, _, _, _) => 13,
241 }
242 }
243
244 pub fn as_sql(&self, no_backslash_escape: bool) -> String {
245 match *self {
246 Value::NULL => "NULL".into(),
247 Value::Int(x) => format!("{}", x),
248 Value::UInt(x) => format!("{}", x),
249 Value::Float(x) => format!("{}", x),
250 Value::Double(x) => format!("{}", x),
251 Value::Date(y, m, d, 0, 0, 0, 0) => format!("'{:04}-{:02}-{:02}'", y, m, d),
252 Value::Date(year, month, day, hour, minute, second, 0) => format!(
253 "'{:04}-{:02}-{:02} {:02}:{:02}:{:02}'",
254 year, month, day, hour, minute, second
255 ),
256 Value::Date(year, month, day, hour, minute, second, micros) => format!(
257 "'{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}'",
258 year, month, day, hour, minute, second, micros
259 ),
260 Value::Time(neg, d, h, i, s, 0) => {
261 if neg {
262 format!("'-{:03}:{:02}:{:02}'", d * 24 + u32::from(h), i, s)
263 } else {
264 format!("'{:03}:{:02}:{:02}'", d * 24 + u32::from(h), i, s)
265 }
266 }
267 Value::Time(neg, days, hours, minutes, seconds, micros) => {
268 if neg {
269 format!(
270 "'-{:03}:{:02}:{:02}.{:06}'",
271 days * 24 + u32::from(hours),
272 minutes,
273 seconds,
274 micros
275 )
276 } else {
277 format!(
278 "'{:03}:{:02}:{:02}.{:06}'",
279 days * 24 + u32::from(hours),
280 minutes,
281 seconds,
282 micros
283 )
284 }
285 }
286 Value::Bytes(ref bytes) => match from_utf8(bytes) {
287 Ok(string) => escaped(string, no_backslash_escape),
288 Err(_) => {
289 let mut s = String::from("0x");
290 for c in bytes.iter() {
291 s.extend(format!("{:02X}", *c).chars())
292 }
293 s
294 }
295 },
296 }
297 }
298
299 fn deserialize_text(buf: &mut ParseBuf<'_>) -> io::Result<Self> {
300 if buf.is_empty() {
301 return Err(unexpected_buf_eof());
302 }
303
304 match buf.0[0] {
305 0xfb => {
306 buf.skip(1);
307 Ok(Value::NULL)
308 }
309 _ => {
310 let bytes: RawBytes<'_, LenEnc> = buf.parse(())?;
311 Ok(Value::Bytes(bytes.0.into_owned()))
312 }
313 }
314 }
315
316 de_num!(deserialize_tiny, checked_eat_i8, checked_eat_u8);
317 de_num!(deserialize_short, checked_eat_i16_le, checked_eat_u16_le);
318 de_num!(deserialize_long, checked_eat_i32_le, checked_eat_u32_le);
319
320 fn deserialize_longlong(unsigned: bool, buf: &mut ParseBuf<'_>) -> io::Result<Self> {
321 if unsigned {
322 buf.checked_eat_u64_le()
323 .ok_or_else(unexpected_buf_eof)
324 .map(|x| i64::try_from(x).map(Int).unwrap_or_else(|_| UInt(x)))
325 } else {
326 buf.checked_eat_i64_le()
327 .ok_or_else(unexpected_buf_eof)
328 .map(Int)
329 }
330 }
331
332 fn deserialize_datetime(buf: &mut ParseBuf<'_>) -> io::Result<Self> {
333 let len = buf.checked_eat_u8().ok_or_else(unexpected_buf_eof)?;
334
335 let mut year = 0u16;
336 let mut month = 0u8;
337 let mut day = 0u8;
338 let mut hour = 0u8;
339 let mut minute = 0u8;
340 let mut second = 0u8;
341 let mut micro_second = 0u32;
342
343 let mut buf = buf
344 .checked_eat_buf(len as usize)
345 .ok_or_else(unexpected_buf_eof)?;
346
347 if len >= 4u8 {
348 year = buf.eat_u16_le();
349 month = buf.eat_u8();
350 day = buf.eat_u8();
351 }
352 if len >= 7u8 {
353 hour = buf.eat_u8();
354 minute = buf.eat_u8();
355 second = buf.eat_u8();
356 }
357 if len == 11u8 {
358 micro_second = buf.eat_u32_le();
359 }
360
361 Ok(Date(year, month, day, hour, minute, second, micro_second))
362 }
363
364 fn deserialize_time(buf: &mut ParseBuf<'_>) -> io::Result<Self> {
365 let len = buf.checked_eat_u8().ok_or_else(unexpected_buf_eof)?;
366
367 let mut is_negative = false;
368 let mut days = 0u32;
369 let mut hours = 0u8;
370 let mut minutes = 0u8;
371 let mut seconds = 0u8;
372 let mut micro_seconds = 0u32;
373
374 let mut buf = buf
375 .checked_eat_buf(len as usize)
376 .ok_or_else(unexpected_buf_eof)?;
377
378 if len >= 8u8 {
379 is_negative = buf.eat_u8() == 1u8;
380 days = buf.eat_u32_le();
381 hours = buf.eat_u8();
382 minutes = buf.eat_u8();
383 seconds = buf.eat_u8();
384 }
385 if len == 12u8 {
386 micro_seconds = buf.eat_u32_le();
387 }
388
389 Ok(Time(
390 is_negative,
391 days,
392 hours,
393 minutes,
394 seconds,
395 micro_seconds,
396 ))
397 }
398
399 pub(crate) fn deserialize_bin(
400 (column_type, column_flags): (ColumnType, ColumnFlags),
401 buf: &mut ParseBuf<'_>,
402 ) -> io::Result<Self> {
403 match column_type {
404 ColumnType::MYSQL_TYPE_STRING
405 | ColumnType::MYSQL_TYPE_VAR_STRING
406 | ColumnType::MYSQL_TYPE_BLOB
407 | ColumnType::MYSQL_TYPE_TINY_BLOB
408 | ColumnType::MYSQL_TYPE_MEDIUM_BLOB
409 | ColumnType::MYSQL_TYPE_LONG_BLOB
410 | ColumnType::MYSQL_TYPE_SET
411 | ColumnType::MYSQL_TYPE_ENUM
412 | ColumnType::MYSQL_TYPE_DECIMAL
413 | ColumnType::MYSQL_TYPE_VARCHAR
414 | ColumnType::MYSQL_TYPE_BIT
415 | ColumnType::MYSQL_TYPE_NEWDECIMAL
416 | ColumnType::MYSQL_TYPE_GEOMETRY
417 | ColumnType::MYSQL_TYPE_VECTOR
418 | ColumnType::MYSQL_TYPE_JSON => Ok(Bytes(
419 buf.checked_eat_lenenc_str()
420 .ok_or_else(unexpected_buf_eof)?
421 .to_vec(),
422 )),
423 ColumnType::MYSQL_TYPE_TINY => {
424 Self::deserialize_tiny(column_flags.contains(ColumnFlags::UNSIGNED_FLAG), buf)
425 }
426 ColumnType::MYSQL_TYPE_SHORT | ColumnType::MYSQL_TYPE_YEAR => {
427 Self::deserialize_short(column_flags.contains(ColumnFlags::UNSIGNED_FLAG), buf)
428 }
429 ColumnType::MYSQL_TYPE_LONG | ColumnType::MYSQL_TYPE_INT24 => {
430 Self::deserialize_long(column_flags.contains(ColumnFlags::UNSIGNED_FLAG), buf)
431 }
432 ColumnType::MYSQL_TYPE_LONGLONG => {
433 Self::deserialize_longlong(column_flags.contains(ColumnFlags::UNSIGNED_FLAG), buf)
434 }
435 ColumnType::MYSQL_TYPE_FLOAT => buf
436 .checked_eat_f32_le()
437 .ok_or_else(unexpected_buf_eof)
438 .map(Float),
439 ColumnType::MYSQL_TYPE_DOUBLE => buf
440 .checked_eat_f64_le()
441 .ok_or_else(unexpected_buf_eof)
442 .map(Double),
443 ColumnType::MYSQL_TYPE_TIMESTAMP
444 | ColumnType::MYSQL_TYPE_DATE
445 | ColumnType::MYSQL_TYPE_NEWDATE
446 | ColumnType::MYSQL_TYPE_DATETIME => Self::deserialize_datetime(buf),
447 ColumnType::MYSQL_TYPE_TIME => Self::deserialize_time(buf),
448 ColumnType::MYSQL_TYPE_NULL => Ok(NULL),
449 x => unimplemented!("Unsupported column type {:?}", x),
450 }
451 }
452}
453
454impl fmt::Debug for Value {
455 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
456 match *self {
457 Value::NULL => formatter.debug_tuple("Null").finish(),
458 Value::Bytes(ref bytes) => {
459 let mut debug = formatter.debug_tuple("Bytes");
460 if bytes.len() <= 8 {
461 debug
462 .field(&String::from_utf8_lossy(bytes).replace('\n', "\\n"))
463 .finish()
464 } else {
465 let bytes = String::from_utf8_lossy(&bytes[..8]).replace('\n', "\\n");
466 debug.field(&format!("{}..", bytes)).finish()
467 }
468 }
469 Value::Int(ref val) => formatter.debug_tuple("Int").field(val).finish(),
470 Value::UInt(ref val) => formatter.debug_tuple("UInt").field(val).finish(),
471 Value::Float(ref val) => formatter.debug_tuple("Float").field(val).finish(),
472 Value::Double(ref val) => formatter.debug_tuple("Double").field(val).finish(),
473 Value::Date(y, m, d, 0, 0, 0, 0) => {
474 let format = format!("'{:04}-{:02}-{:02}'", y, m, d);
475 formatter.debug_tuple("Date").field(&format).finish()
476 }
477 Value::Date(year, month, day, hour, minute, second, 0) => {
478 let format = format!(
479 "'{:04}-{:02}-{:02} {:02}:{:02}:{:02}'",
480 year, month, day, hour, minute, second
481 );
482 formatter.debug_tuple("Date").field(&format).finish()
483 }
484 Value::Date(year, month, day, hour, minute, second, micros) => {
485 let format = format!(
486 "'{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}'",
487 year, month, day, hour, minute, second, micros
488 );
489 formatter.debug_tuple("Date").field(&format).finish()
490 }
491 Value::Time(neg, d, h, i, s, 0) => {
492 let format = if neg {
493 format!("'-{:03}:{:02}:{:02}'", d * 24 + u32::from(h), i, s)
494 } else {
495 format!("'{:03}:{:02}:{:02}'", d * 24 + u32::from(h), i, s)
496 };
497 formatter.debug_tuple("Time").field(&format).finish()
498 }
499 Value::Time(neg, days, hours, minutes, seconds, micros) => {
500 let format = if neg {
501 format!(
502 "'-{:03}:{:02}:{:02}.{:06}'",
503 days * 24 + u32::from(hours),
504 minutes,
505 seconds,
506 micros
507 )
508 } else {
509 format!(
510 "'{:03}:{:02}:{:02}.{:06}'",
511 days * 24 + u32::from(hours),
512 minutes,
513 seconds,
514 micros
515 )
516 };
517 formatter.debug_tuple("Time").field(&format).finish()
518 }
519 }
520 }
521}
522
523#[cfg(test)]
524mod test {
525 use std::io;
526
527 use crate::{io::ParseBuf, value::Value};
528
529 #[test]
530 fn should_escape_string() {
531 assert_eq!(r"'?p??\\\\?p??'", Value::from("?p??\\\\?p??").as_sql(false));
532 assert_eq!(r#"'?p??\"?p??'"#, Value::from("?p??\"?p??").as_sql(false));
533 assert_eq!(r"'?p??\'?p??'", Value::from("?p??'?p??").as_sql(false));
534 assert_eq!(r"'?p??\n?p??'", Value::from("?p??\n?p??").as_sql(false));
535 assert_eq!(r"'?p??\r?p??'", Value::from("?p??\r?p??").as_sql(false));
536 assert_eq!(r"'?p??\0?p??'", Value::from("?p??\x00?p??").as_sql(false));
537 }
538
539 #[cfg(feature = "nightly")]
540 mod benches {
541 use std::convert::TryFrom;
542
543 use crate::{
544 constants::ColumnType,
545 io::WriteMysqlExt,
546 packets::{Column, ComStmtExecuteRequestBuilder, NullBitmap},
547 value::{ClientSide, Value},
548 };
549
550 #[bench]
551 fn bench_build_stmt_execute_request(bencher: &mut test::Bencher) {
552 let values = vec![
553 Value::Bytes(b"12.3456789".to_vec()),
554 Value::Int(0xF0),
555 Value::Int(0xF000),
556 Value::Int(0xF0000000),
557 Value::Float(std::f32::MAX),
558 Value::Double(std::f64::MAX),
559 Value::NULL,
560 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
561 Value::UInt(0xF000000000000000),
562 Value::Int(0xF00000),
563 Value::Date(2019, 11, 27, 0, 0, 0, 0),
564 Value::Time(true, 300, 8, 8, 8, 123456),
565 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
566 Value::Int(2019),
567 Value::Bytes(b"varchar".to_vec()),
568 Value::Bytes(b"1000000110000001".to_vec()),
569 Value::Bytes(br#"{"foo":"bar","baz":42345.6777}"#.to_vec()),
570 Value::Bytes(b"12.3456789".to_vec()),
571 Value::Bytes(b"Variant".to_vec()),
572 Value::Bytes(b"Element".to_vec()),
573 Value::Bytes(b"MYSQL_TYPE_TINY_BLOB".to_vec()),
574 Value::Bytes(b"MYSQL_TYPE_MEDIUM_BLOB".to_vec()),
575 Value::Bytes(b"MYSQL_TYPE_LONG_BLOB".to_vec()),
576 Value::Bytes(b"MYSQL_TYPE_BLOB".to_vec()),
577 Value::Bytes(b"MYSQL_TYPE_VAR_STRING".to_vec()),
578 Value::Bytes(b"MYSQL_TYPE_STRING".to_vec()),
579 Value::NULL,
580 Value::Bytes(b"MYSQL_TYPE_GEOMETRY".to_vec()),
581 Value::Bytes(b"MYSQL_TYPE_VECTOR".to_vec()),
582 ];
583
584 let (body, _) = ComStmtExecuteRequestBuilder::new(0).build(&*values);
585
586 bencher.bytes = body.len() as u64;
587 bencher.iter(|| ComStmtExecuteRequestBuilder::new(0).build(&*values));
588 }
589
590 #[cfg(feature = "nightly")]
591 #[bench]
592 fn bench_parse_bin_row(bencher: &mut test::Bencher) {
593 fn col(name: &str, ty: ColumnType) -> Column<'static> {
594 let mut payload = b"\x00def".to_vec();
595 for _ in 0..5 {
596 payload.write_lenenc_str(name.as_bytes()).unwrap();
597 }
598 payload.extend_from_slice(&b"_\x2d\x00\xff\xff\xff\xff"[..]);
599 payload.push(ty as u8);
600 payload.extend_from_slice(&b"\x00\x00\x00"[..]);
601 Column::read(&payload[..]).unwrap()
602 }
603
604 let values = vec![
605 Value::Bytes(b"12.3456789".to_vec()),
606 Value::Int(0xF0),
607 Value::Int(0xF000),
608 Value::Int(0xF0000000),
609 Value::Float(std::f32::MAX),
610 Value::Double(std::f64::MAX),
611 Value::NULL,
612 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
613 Value::UInt(0xF000000000000000),
614 Value::Int(0xF00000),
615 Value::Date(2019, 11, 27, 0, 0, 0, 0),
616 Value::Time(true, 300, 8, 8, 8, 123456),
617 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
618 Value::Int(2019),
619 Value::Bytes(b"varchar".to_vec()),
620 Value::Bytes(b"1000000110000001".to_vec()),
621 Value::Bytes(br#"{"foo":"bar","baz":42345.6777}"#.to_vec()),
622 Value::Bytes(b"12.3456789".to_vec()),
623 Value::Bytes(b"Variant".to_vec()),
624 Value::Bytes(b"Element".to_vec()),
625 Value::Bytes(b"MYSQL_TYPE_TINY_BLOB".to_vec()),
626 Value::Bytes(b"MYSQL_TYPE_MEDIUM_BLOB".to_vec()),
627 Value::Bytes(b"MYSQL_TYPE_LONG_BLOB".to_vec()),
628 Value::Bytes(b"MYSQL_TYPE_BLOB".to_vec()),
629 Value::Bytes(b"MYSQL_TYPE_VAR_STRING".to_vec()),
630 Value::Bytes(b"MYSQL_TYPE_STRING".to_vec()),
631 Value::NULL,
632 Value::Bytes(b"MYSQL_TYPE_GEOMETRY".to_vec()),
633 Value::Bytes(b"MYSQL_TYPE_VECTOR".to_vec()),
634 ];
635
636 let (body, _) = ComStmtExecuteRequestBuilder::new(0).build(&*values);
637
638 let bitmap_len = NullBitmap::<ClientSide>::bitmap_len(values.len());
639
640 let meta_offset = ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET + bitmap_len + 1;
641 let meta_len = values.len() * 2;
642 let columns = body[meta_offset..(meta_offset + meta_len)]
643 .chunks(2)
644 .map(|meta| col("foo", ColumnType::try_from(meta[0]).unwrap()))
645 .collect::<Vec<_>>();
646
647 let mut data = vec![0x00];
648 data.extend_from_slice(
649 &body[ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET
650 ..(ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET + bitmap_len)],
651 );
652 data.extend_from_slice(
653 &body[(ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET
654 + bitmap_len
655 + 1
656 + 2 * values.len())..],
657 );
658
659 bencher.bytes = data.len() as u64;
660 bencher.iter(|| Value::read_bin_many::<ClientSide>(&*data, &*columns).unwrap());
661 }
662 }
663
664 #[test]
665 fn mysql_simple_issue_284() -> io::Result<()> {
666 use Value::*;
667
668 let mut buf = ParseBuf(&[1, 49, 1, 50, 1, 51, 251, 1, 52, 1, 53, 251, 1, 55][..]);
669 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"1".to_vec()));
670 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"2".to_vec()));
671 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"3".to_vec()));
672 assert_eq!(Value::deserialize_text(&mut buf)?, NULL);
673 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"4".to_vec()));
674 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"5".to_vec()));
675 assert_eq!(Value::deserialize_text(&mut buf)?, NULL);
676 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"7".to_vec()));
677
678 Ok(())
679 }
680}