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_DATETIME => Self::deserialize_datetime(buf),
446 ColumnType::MYSQL_TYPE_TIME => Self::deserialize_time(buf),
447 ColumnType::MYSQL_TYPE_NULL => Ok(NULL),
448 x => unimplemented!("Unsupported column type {:?}", x),
449 }
450 }
451}
452
453impl fmt::Debug for Value {
454 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
455 match *self {
456 Value::NULL => formatter.debug_tuple("Null").finish(),
457 Value::Bytes(ref bytes) => {
458 let mut debug = formatter.debug_tuple("Bytes");
459 if bytes.len() <= 8 {
460 debug
461 .field(&String::from_utf8_lossy(bytes).replace('\n', "\\n"))
462 .finish()
463 } else {
464 let bytes = String::from_utf8_lossy(&bytes[..8]).replace('\n', "\\n");
465 debug.field(&format!("{}..", bytes)).finish()
466 }
467 }
468 Value::Int(ref val) => formatter.debug_tuple("Int").field(val).finish(),
469 Value::UInt(ref val) => formatter.debug_tuple("UInt").field(val).finish(),
470 Value::Float(ref val) => formatter.debug_tuple("Float").field(val).finish(),
471 Value::Double(ref val) => formatter.debug_tuple("Double").field(val).finish(),
472 Value::Date(y, m, d, 0, 0, 0, 0) => {
473 let format = format!("'{:04}-{:02}-{:02}'", y, m, d);
474 formatter.debug_tuple("Date").field(&format).finish()
475 }
476 Value::Date(year, month, day, hour, minute, second, 0) => {
477 let format = format!(
478 "'{:04}-{:02}-{:02} {:02}:{:02}:{:02}'",
479 year, month, day, hour, minute, second
480 );
481 formatter.debug_tuple("Date").field(&format).finish()
482 }
483 Value::Date(year, month, day, hour, minute, second, micros) => {
484 let format = format!(
485 "'{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:06}'",
486 year, month, day, hour, minute, second, micros
487 );
488 formatter.debug_tuple("Date").field(&format).finish()
489 }
490 Value::Time(neg, d, h, i, s, 0) => {
491 let format = if neg {
492 format!("'-{:03}:{:02}:{:02}'", d * 24 + u32::from(h), i, s)
493 } else {
494 format!("'{:03}:{:02}:{:02}'", d * 24 + u32::from(h), i, s)
495 };
496 formatter.debug_tuple("Time").field(&format).finish()
497 }
498 Value::Time(neg, days, hours, minutes, seconds, micros) => {
499 let format = if neg {
500 format!(
501 "'-{:03}:{:02}:{:02}.{:06}'",
502 days * 24 + u32::from(hours),
503 minutes,
504 seconds,
505 micros
506 )
507 } else {
508 format!(
509 "'{:03}:{:02}:{:02}.{:06}'",
510 days * 24 + u32::from(hours),
511 minutes,
512 seconds,
513 micros
514 )
515 };
516 formatter.debug_tuple("Time").field(&format).finish()
517 }
518 }
519 }
520}
521
522#[cfg(test)]
523mod test {
524 use std::io;
525
526 use crate::{io::ParseBuf, value::Value};
527
528 #[test]
529 fn should_escape_string() {
530 assert_eq!(r"'?p??\\\\?p??'", Value::from("?p??\\\\?p??").as_sql(false));
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??\n?p??'", Value::from("?p??\n?p??").as_sql(false));
534 assert_eq!(r"'?p??\r?p??'", Value::from("?p??\r?p??").as_sql(false));
535 assert_eq!(r"'?p??\0?p??'", Value::from("?p??\x00?p??").as_sql(false));
536 }
537
538 #[cfg(feature = "nightly")]
539 mod benches {
540 use std::convert::TryFrom;
541
542 use crate::{
543 constants::ColumnType,
544 io::WriteMysqlExt,
545 packets::{Column, ComStmtExecuteRequestBuilder, NullBitmap},
546 value::{ClientSide, Value},
547 };
548
549 #[bench]
550 fn bench_build_stmt_execute_request(bencher: &mut test::Bencher) {
551 let values = vec![
552 Value::Bytes(b"12.3456789".to_vec()),
553 Value::Int(0xF0),
554 Value::Int(0xF000),
555 Value::Int(0xF0000000),
556 Value::Float(std::f32::MAX),
557 Value::Double(std::f64::MAX),
558 Value::NULL,
559 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
560 Value::UInt(0xF000000000000000),
561 Value::Int(0xF00000),
562 Value::Date(2019, 11, 27, 0, 0, 0, 0),
563 Value::Time(true, 300, 8, 8, 8, 123456),
564 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
565 Value::Int(2019),
566 Value::Bytes(b"varchar".to_vec()),
567 Value::Bytes(b"1000000110000001".to_vec()),
568 Value::Bytes(br#"{"foo":"bar","baz":42345.6777}"#.to_vec()),
569 Value::Bytes(b"12.3456789".to_vec()),
570 Value::Bytes(b"Variant".to_vec()),
571 Value::Bytes(b"Element".to_vec()),
572 Value::Bytes(b"MYSQL_TYPE_TINY_BLOB".to_vec()),
573 Value::Bytes(b"MYSQL_TYPE_MEDIUM_BLOB".to_vec()),
574 Value::Bytes(b"MYSQL_TYPE_LONG_BLOB".to_vec()),
575 Value::Bytes(b"MYSQL_TYPE_BLOB".to_vec()),
576 Value::Bytes(b"MYSQL_TYPE_VAR_STRING".to_vec()),
577 Value::Bytes(b"MYSQL_TYPE_STRING".to_vec()),
578 Value::NULL,
579 Value::Bytes(b"MYSQL_TYPE_GEOMETRY".to_vec()),
580 Value::Bytes(b"MYSQL_TYPE_VECTOR".to_vec()),
581 ];
582
583 let (body, _) = ComStmtExecuteRequestBuilder::new(0).build(&*values);
584
585 bencher.bytes = body.len() as u64;
586 bencher.iter(|| ComStmtExecuteRequestBuilder::new(0).build(&*values));
587 }
588
589 #[cfg(feature = "nightly")]
590 #[bench]
591 fn bench_parse_bin_row(bencher: &mut test::Bencher) {
592 fn col(name: &str, ty: ColumnType) -> Column<'static> {
593 let mut payload = b"\x00def".to_vec();
594 for _ in 0..5 {
595 payload.write_lenenc_str(name.as_bytes()).unwrap();
596 }
597 payload.extend_from_slice(&b"_\x2d\x00\xff\xff\xff\xff"[..]);
598 payload.push(ty as u8);
599 payload.extend_from_slice(&b"\x00\x00\x00"[..]);
600 Column::read(&payload[..]).unwrap()
601 }
602
603 let values = vec![
604 Value::Bytes(b"12.3456789".to_vec()),
605 Value::Int(0xF0),
606 Value::Int(0xF000),
607 Value::Int(0xF0000000),
608 Value::Float(std::f32::MAX),
609 Value::Double(std::f64::MAX),
610 Value::NULL,
611 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
612 Value::UInt(0xF000000000000000),
613 Value::Int(0xF00000),
614 Value::Date(2019, 11, 27, 0, 0, 0, 0),
615 Value::Time(true, 300, 8, 8, 8, 123456),
616 Value::Date(2019, 11, 27, 12, 30, 0, 123456),
617 Value::Int(2019),
618 Value::Bytes(b"varchar".to_vec()),
619 Value::Bytes(b"1000000110000001".to_vec()),
620 Value::Bytes(br#"{"foo":"bar","baz":42345.6777}"#.to_vec()),
621 Value::Bytes(b"12.3456789".to_vec()),
622 Value::Bytes(b"Variant".to_vec()),
623 Value::Bytes(b"Element".to_vec()),
624 Value::Bytes(b"MYSQL_TYPE_TINY_BLOB".to_vec()),
625 Value::Bytes(b"MYSQL_TYPE_MEDIUM_BLOB".to_vec()),
626 Value::Bytes(b"MYSQL_TYPE_LONG_BLOB".to_vec()),
627 Value::Bytes(b"MYSQL_TYPE_BLOB".to_vec()),
628 Value::Bytes(b"MYSQL_TYPE_VAR_STRING".to_vec()),
629 Value::Bytes(b"MYSQL_TYPE_STRING".to_vec()),
630 Value::NULL,
631 Value::Bytes(b"MYSQL_TYPE_GEOMETRY".to_vec()),
632 Value::Bytes(b"MYSQL_TYPE_VECTOR".to_vec()),
633 ];
634
635 let (body, _) = ComStmtExecuteRequestBuilder::new(0).build(&*values);
636
637 let bitmap_len = NullBitmap::<ClientSide>::bitmap_len(values.len());
638
639 let meta_offset = ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET + bitmap_len + 1;
640 let meta_len = values.len() * 2;
641 let columns = body[meta_offset..(meta_offset + meta_len)]
642 .chunks(2)
643 .map(|meta| col("foo", ColumnType::try_from(meta[0]).unwrap()))
644 .collect::<Vec<_>>();
645
646 let mut data = vec![0x00];
647 data.extend_from_slice(
648 &body[ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET
649 ..(ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET + bitmap_len)],
650 );
651 data.extend_from_slice(
652 &body[(ComStmtExecuteRequestBuilder::NULL_BITMAP_OFFSET
653 + bitmap_len
654 + 1
655 + 2 * values.len())..],
656 );
657
658 bencher.bytes = data.len() as u64;
659 bencher.iter(|| Value::read_bin_many::<ClientSide>(&*data, &*columns).unwrap());
660 }
661 }
662
663 #[test]
664 fn mysql_simple_issue_284() -> io::Result<()> {
665 use Value::*;
666
667 let mut buf = ParseBuf(&[1, 49, 1, 50, 1, 51, 251, 1, 52, 1, 53, 251, 1, 55][..]);
668 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"1".to_vec()));
669 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"2".to_vec()));
670 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"3".to_vec()));
671 assert_eq!(Value::deserialize_text(&mut buf)?, NULL);
672 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"4".to_vec()));
673 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"5".to_vec()));
674 assert_eq!(Value::deserialize_text(&mut buf)?, NULL);
675 assert_eq!(Value::deserialize_text(&mut buf)?, Bytes(b"7".to_vec()));
676
677 Ok(())
678 }
679}