zero_mysql/
raw.rs

1//! Flexible decoding API for MySQL binary protocol values.
2//!
3//! This module provides traits for decoding MySQL values directly into target types
4//! without intermediate `Value` allocation.
5
6use crate::constant::{ColumnFlags, ColumnType};
7use crate::error::{Error, Result, eyre};
8use crate::protocol::BinaryRowPayload;
9use crate::protocol::command::{ColumnDefinition, ColumnDefinitionTail};
10use crate::protocol::primitive::*;
11use crate::value::{Time8, Time12, Timestamp4, Timestamp7, Timestamp11, Value};
12use simdutf8::basic::from_utf8;
13use zerocopy::FromBytes;
14
15/// MySQL binary charset number - indicates binary/non-text data
16const BINARY_CHARSET: u16 = 63;
17
18/// Trait for types that can be decoded from MySQL binary protocol values.
19///
20/// Each method corresponds to a MySQL wire format. Implementations should
21/// return `Err` for unsupported conversions.
22pub trait FromRawValue<'buf>: Sized {
23    fn from_null() -> Result<Self> {
24        Err(Error::BadUsageError(format!(
25            "Cannot decode MySQL type NULL to {}",
26            std::any::type_name::<Self>()
27        )))
28    }
29
30    fn from_i8(_v: i8) -> Result<Self> {
31        Err(Error::BadUsageError(format!(
32            "Cannot decode MySQL type TINYINT (i8) to {}",
33            std::any::type_name::<Self>()
34        )))
35    }
36
37    fn from_i16(_v: i16) -> Result<Self> {
38        Err(Error::BadUsageError(format!(
39            "Cannot decode MySQL type SMALLINT (i16) to {}",
40            std::any::type_name::<Self>()
41        )))
42    }
43
44    fn from_i32(_v: i32) -> Result<Self> {
45        Err(Error::BadUsageError(format!(
46            "Cannot decode MySQL type INT (i32) to {}",
47            std::any::type_name::<Self>()
48        )))
49    }
50
51    fn from_i64(_v: i64) -> Result<Self> {
52        Err(Error::BadUsageError(format!(
53            "Cannot decode MySQL type BIGINT (i64) to {}",
54            std::any::type_name::<Self>()
55        )))
56    }
57
58    fn from_u8(_v: u8) -> Result<Self> {
59        Err(Error::BadUsageError(format!(
60            "Cannot decode MySQL type TINYINT UNSIGNED (u8) to {}",
61            std::any::type_name::<Self>()
62        )))
63    }
64
65    fn from_u16(_v: u16) -> Result<Self> {
66        Err(Error::BadUsageError(format!(
67            "Cannot decode MySQL type SMALLINT UNSIGNED (u16) to {}",
68            std::any::type_name::<Self>()
69        )))
70    }
71
72    fn from_u32(_v: u32) -> Result<Self> {
73        Err(Error::BadUsageError(format!(
74            "Cannot decode MySQL type INT UNSIGNED (u32) to {}",
75            std::any::type_name::<Self>()
76        )))
77    }
78
79    fn from_u64(_v: u64) -> Result<Self> {
80        Err(Error::BadUsageError(format!(
81            "Cannot decode MySQL type BIGINT UNSIGNED (u64) to {}",
82            std::any::type_name::<Self>()
83        )))
84    }
85
86    fn from_float(_v: f32) -> Result<Self> {
87        Err(Error::BadUsageError(format!(
88            "Cannot decode MySQL type FLOAT (f32) to {}",
89            std::any::type_name::<Self>()
90        )))
91    }
92
93    fn from_double(_v: f64) -> Result<Self> {
94        Err(Error::BadUsageError(format!(
95            "Cannot decode MySQL type DOUBLE (f64) to {}",
96            std::any::type_name::<Self>()
97        )))
98    }
99
100    fn from_bytes(_v: &'buf [u8]) -> Result<Self> {
101        Err(Error::BadUsageError(format!(
102            "Cannot decode MySQL type BYTES to {}",
103            std::any::type_name::<Self>()
104        )))
105    }
106
107    fn from_str(_v: &'buf [u8]) -> Result<Self> {
108        Err(Error::BadUsageError(format!(
109            "Cannot decode MySQL type STRING to {}",
110            std::any::type_name::<Self>()
111        )))
112    }
113
114    fn from_decimal(_v: &'buf [u8]) -> Result<Self> {
115        Err(Error::BadUsageError(format!(
116            "Cannot decode MySQL type DECIMAL to {}",
117            std::any::type_name::<Self>()
118        )))
119    }
120
121    fn from_date0() -> Result<Self> {
122        Err(Error::BadUsageError(format!(
123            "Cannot decode MySQL type DATE to {}",
124            std::any::type_name::<Self>()
125        )))
126    }
127
128    fn from_date4(_v: &'buf Timestamp4) -> Result<Self> {
129        Err(Error::BadUsageError(format!(
130            "Cannot decode MySQL type DATE to {}",
131            std::any::type_name::<Self>()
132        )))
133    }
134
135    fn from_datetime0() -> Result<Self> {
136        Err(Error::BadUsageError(format!(
137            "Cannot decode MySQL type DATETIME to {}",
138            std::any::type_name::<Self>()
139        )))
140    }
141
142    fn from_datetime4(_v: &'buf Timestamp4) -> Result<Self> {
143        Err(Error::BadUsageError(format!(
144            "Cannot decode MySQL type DATETIME to {}",
145            std::any::type_name::<Self>()
146        )))
147    }
148
149    fn from_datetime7(_v: &'buf Timestamp7) -> Result<Self> {
150        Err(Error::BadUsageError(format!(
151            "Cannot decode MySQL type DATETIME to {}",
152            std::any::type_name::<Self>()
153        )))
154    }
155
156    fn from_datetime11(_v: &'buf Timestamp11) -> Result<Self> {
157        Err(Error::BadUsageError(format!(
158            "Cannot decode MySQL type DATETIME to {}",
159            std::any::type_name::<Self>()
160        )))
161    }
162
163    fn from_time0() -> Result<Self> {
164        Err(Error::BadUsageError(format!(
165            "Cannot decode MySQL type TIME to {}",
166            std::any::type_name::<Self>()
167        )))
168    }
169
170    fn from_time8(_v: &'buf Time8) -> Result<Self> {
171        Err(Error::BadUsageError(format!(
172            "Cannot decode MySQL type TIME to {}",
173            std::any::type_name::<Self>()
174        )))
175    }
176
177    fn from_time12(_v: &'buf Time12) -> Result<Self> {
178        Err(Error::BadUsageError(format!(
179            "Cannot decode MySQL type TIME to {}",
180            std::any::type_name::<Self>()
181        )))
182    }
183}
184
185/// Parse a single value from binary data into target type `T`.
186///
187/// Returns the parsed value and remaining bytes.
188pub fn parse_value<'buf, T: FromRawValue<'buf>>(
189    col: &ColumnDefinitionTail,
190    is_null: bool,
191    data: &'buf [u8],
192) -> Result<(T, &'buf [u8])> {
193    if is_null {
194        return Ok((T::from_null()?, data));
195    }
196    let is_unsigned = col.flags()?.contains(ColumnFlags::UNSIGNED_FLAG);
197    let is_binary_charset = col.charset() == BINARY_CHARSET;
198
199    match col.column_type()? {
200        ColumnType::MYSQL_TYPE_NULL => Ok((T::from_null()?, data)),
201
202        // Integer types
203        ColumnType::MYSQL_TYPE_TINY => {
204            let (val, rest) = read_int_1(data)?;
205            let out = if is_unsigned {
206                T::from_u8(val)?
207            } else {
208                T::from_i8(val as i8)?
209            };
210            Ok((out, rest))
211        }
212
213        ColumnType::MYSQL_TYPE_SHORT | ColumnType::MYSQL_TYPE_YEAR => {
214            let (val, rest) = read_int_2(data)?;
215            let out = if is_unsigned {
216                T::from_u16(val)?
217            } else {
218                T::from_i16(val as i16)?
219            };
220            Ok((out, rest))
221        }
222
223        ColumnType::MYSQL_TYPE_INT24 | ColumnType::MYSQL_TYPE_LONG => {
224            let (val, rest) = read_int_4(data)?;
225            let out = if is_unsigned {
226                T::from_u32(val)?
227            } else {
228                T::from_i32(val as i32)?
229            };
230            Ok((out, rest))
231        }
232
233        ColumnType::MYSQL_TYPE_LONGLONG => {
234            let (val, rest) = read_int_8(data)?;
235            let out = if is_unsigned {
236                T::from_u64(val)?
237            } else {
238                T::from_i64(val as i64)?
239            };
240            Ok((out, rest))
241        }
242
243        // Floating point types
244        ColumnType::MYSQL_TYPE_FLOAT => {
245            let (val, rest) = read_int_4(data)?;
246            Ok((T::from_float(f32::from_bits(val))?, rest))
247        }
248
249        ColumnType::MYSQL_TYPE_DOUBLE => {
250            let (val, rest) = read_int_8(data)?;
251            Ok((T::from_double(f64::from_bits(val))?, rest))
252        }
253
254        // DATE types
255        ColumnType::MYSQL_TYPE_DATE | ColumnType::MYSQL_TYPE_NEWDATE => {
256            let (len, mut rest) = read_int_1(data)?;
257            match len {
258                0 => Ok((T::from_date0()?, rest)),
259                4 => {
260                    let ts = Timestamp4::ref_from_bytes(&rest[..4])?;
261                    rest = &rest[4..];
262                    Ok((T::from_date4(ts)?, rest))
263                }
264                _ => Err(Error::LibraryBug(eyre!("invalid date length: {}", len))),
265            }
266        }
267
268        // DATETIME/TIMESTAMP types
269        ColumnType::MYSQL_TYPE_DATETIME
270        | ColumnType::MYSQL_TYPE_TIMESTAMP
271        | ColumnType::MYSQL_TYPE_TIMESTAMP2
272        | ColumnType::MYSQL_TYPE_DATETIME2 => {
273            let (len, mut rest) = read_int_1(data)?;
274            match len {
275                0 => Ok((T::from_datetime0()?, rest)),
276                4 => {
277                    let ts = Timestamp4::ref_from_bytes(&rest[..4])?;
278                    rest = &rest[4..];
279                    Ok((T::from_datetime4(ts)?, rest))
280                }
281                7 => {
282                    let ts = Timestamp7::ref_from_bytes(&rest[..7])?;
283                    rest = &rest[7..];
284                    Ok((T::from_datetime7(ts)?, rest))
285                }
286                11 => {
287                    let ts = Timestamp11::ref_from_bytes(&rest[..11])?;
288                    rest = &rest[11..];
289                    Ok((T::from_datetime11(ts)?, rest))
290                }
291                _ => Err(Error::LibraryBug(eyre!("invalid datetime length: {}", len))),
292            }
293        }
294
295        // TIME types
296        ColumnType::MYSQL_TYPE_TIME | ColumnType::MYSQL_TYPE_TIME2 => {
297            let (len, mut rest) = read_int_1(data)?;
298            match len {
299                0 => Ok((T::from_time0()?, rest)),
300                8 => {
301                    let time = Time8::ref_from_bytes(&rest[..8])?;
302                    rest = &rest[8..];
303                    Ok((T::from_time8(time)?, rest))
304                }
305                12 => {
306                    let time = Time12::ref_from_bytes(&rest[..12])?;
307                    rest = &rest[12..];
308                    Ok((T::from_time12(time)?, rest))
309                }
310                _ => Err(Error::LibraryBug(eyre!("invalid time length: {}", len))),
311            }
312        }
313
314        // DECIMAL types
315        ColumnType::MYSQL_TYPE_DECIMAL | ColumnType::MYSQL_TYPE_NEWDECIMAL => {
316            let (bytes, rest) = read_string_lenenc(data)?;
317            Ok((T::from_decimal(bytes)?, rest))
318        }
319
320        // String and BLOB types
321        ColumnType::MYSQL_TYPE_VARCHAR
322        | ColumnType::MYSQL_TYPE_VAR_STRING
323        | ColumnType::MYSQL_TYPE_STRING
324        | ColumnType::MYSQL_TYPE_BLOB
325        | ColumnType::MYSQL_TYPE_TINY_BLOB
326        | ColumnType::MYSQL_TYPE_MEDIUM_BLOB
327        | ColumnType::MYSQL_TYPE_LONG_BLOB
328        | ColumnType::MYSQL_TYPE_GEOMETRY
329        | ColumnType::MYSQL_TYPE_JSON
330        | ColumnType::MYSQL_TYPE_ENUM
331        | ColumnType::MYSQL_TYPE_SET
332        | ColumnType::MYSQL_TYPE_BIT
333        | ColumnType::MYSQL_TYPE_TYPED_ARRAY => {
334            let (bytes, rest) = read_string_lenenc(data)?;
335            let out = if is_binary_charset {
336                T::from_bytes(bytes)?
337            } else {
338                T::from_str(bytes)?
339            };
340            Ok((out, rest))
341        }
342    }
343}
344
345/// Skip a single value in binary data without parsing it.
346///
347/// Returns the remaining bytes after the skipped value.
348pub fn skip_value<'buf>(
349    col: &ColumnDefinitionTail,
350    is_null: bool,
351    data: &'buf [u8],
352) -> Result<((), &'buf [u8])> {
353    if is_null {
354        return Ok(((), data));
355    }
356
357    match col.column_type()? {
358        ColumnType::MYSQL_TYPE_NULL => Ok(((), data)),
359
360        // Fixed-size integer types
361        ColumnType::MYSQL_TYPE_TINY => Ok(((), &data[1..])),
362        ColumnType::MYSQL_TYPE_SHORT | ColumnType::MYSQL_TYPE_YEAR => Ok(((), &data[2..])),
363        ColumnType::MYSQL_TYPE_INT24
364        | ColumnType::MYSQL_TYPE_LONG
365        | ColumnType::MYSQL_TYPE_FLOAT => Ok(((), &data[4..])),
366        ColumnType::MYSQL_TYPE_LONGLONG | ColumnType::MYSQL_TYPE_DOUBLE => Ok(((), &data[8..])),
367
368        // Variable-length date/time types
369        ColumnType::MYSQL_TYPE_DATE
370        | ColumnType::MYSQL_TYPE_NEWDATE
371        | ColumnType::MYSQL_TYPE_DATETIME
372        | ColumnType::MYSQL_TYPE_TIMESTAMP
373        | ColumnType::MYSQL_TYPE_TIMESTAMP2
374        | ColumnType::MYSQL_TYPE_DATETIME2
375        | ColumnType::MYSQL_TYPE_TIME
376        | ColumnType::MYSQL_TYPE_TIME2 => {
377            let (len, rest) = read_int_1(data)?;
378            Ok(((), &rest[len as usize..]))
379        }
380
381        // Length-encoded string types
382        ColumnType::MYSQL_TYPE_DECIMAL
383        | ColumnType::MYSQL_TYPE_NEWDECIMAL
384        | ColumnType::MYSQL_TYPE_VARCHAR
385        | ColumnType::MYSQL_TYPE_VAR_STRING
386        | ColumnType::MYSQL_TYPE_STRING
387        | ColumnType::MYSQL_TYPE_BLOB
388        | ColumnType::MYSQL_TYPE_TINY_BLOB
389        | ColumnType::MYSQL_TYPE_MEDIUM_BLOB
390        | ColumnType::MYSQL_TYPE_LONG_BLOB
391        | ColumnType::MYSQL_TYPE_GEOMETRY
392        | ColumnType::MYSQL_TYPE_JSON
393        | ColumnType::MYSQL_TYPE_ENUM
394        | ColumnType::MYSQL_TYPE_SET
395        | ColumnType::MYSQL_TYPE_BIT
396        | ColumnType::MYSQL_TYPE_TYPED_ARRAY => {
397            let (_, rest) = read_string_lenenc(data)?;
398            Ok(((), rest))
399        }
400    }
401}
402
403/// Trait for types that can be decoded from a MySQL row.
404pub trait FromRawRow<'buf>: Sized {
405    fn from_raw_row(cols: &[ColumnDefinition<'_>], row: BinaryRowPayload<'buf>) -> Result<Self>;
406}
407
408// ============================================================================
409// FromRawValue implementations for Value<'a>
410// ============================================================================
411
412impl<'buf, 'value> FromRawValue<'buf> for Value<'value>
413where
414    'buf: 'value,
415{
416    fn from_null() -> Result<Self> {
417        Ok(Value::Null)
418    }
419
420    fn from_i8(v: i8) -> Result<Self> {
421        Ok(Value::SignedInt(v as i64))
422    }
423
424    fn from_i16(v: i16) -> Result<Self> {
425        Ok(Value::SignedInt(v as i64))
426    }
427
428    fn from_i32(v: i32) -> Result<Self> {
429        Ok(Value::SignedInt(v as i64))
430    }
431
432    fn from_i64(v: i64) -> Result<Self> {
433        Ok(Value::SignedInt(v))
434    }
435
436    fn from_u8(v: u8) -> Result<Self> {
437        Ok(Value::UnsignedInt(v as u64))
438    }
439
440    fn from_u16(v: u16) -> Result<Self> {
441        Ok(Value::UnsignedInt(v as u64))
442    }
443
444    fn from_u32(v: u32) -> Result<Self> {
445        Ok(Value::UnsignedInt(v as u64))
446    }
447
448    fn from_u64(v: u64) -> Result<Self> {
449        Ok(Value::UnsignedInt(v))
450    }
451
452    fn from_float(v: f32) -> Result<Self> {
453        Ok(Value::Float(v))
454    }
455
456    fn from_double(v: f64) -> Result<Self> {
457        Ok(Value::Double(v))
458    }
459
460    fn from_bytes(v: &'buf [u8]) -> Result<Self> {
461        Ok(Value::Byte(v))
462    }
463
464    fn from_str(v: &'buf [u8]) -> Result<Self> {
465        Ok(Value::Byte(v))
466    }
467
468    fn from_decimal(v: &'buf [u8]) -> Result<Self> {
469        Ok(Value::Byte(v))
470    }
471
472    fn from_date0() -> Result<Self> {
473        Ok(Value::Date0)
474    }
475
476    fn from_date4(v: &'buf Timestamp4) -> Result<Self> {
477        Ok(Value::Date4(v))
478    }
479
480    fn from_datetime0() -> Result<Self> {
481        Ok(Value::Datetime0)
482    }
483
484    fn from_datetime4(v: &'buf Timestamp4) -> Result<Self> {
485        Ok(Value::Datetime4(v))
486    }
487
488    fn from_datetime7(v: &'buf Timestamp7) -> Result<Self> {
489        Ok(Value::Datetime7(v))
490    }
491
492    fn from_datetime11(v: &'buf Timestamp11) -> Result<Self> {
493        Ok(Value::Datetime11(v))
494    }
495
496    fn from_time0() -> Result<Self> {
497        Ok(Value::Time0)
498    }
499
500    fn from_time8(v: &'buf Time8) -> Result<Self> {
501        Ok(Value::Time8(v))
502    }
503
504    fn from_time12(v: &'buf Time12) -> Result<Self> {
505        Ok(Value::Time12(v))
506    }
507}
508
509// ============================================================================
510// FromRawValue implementations for primitive types
511// ============================================================================
512
513impl FromRawValue<'_> for i8 {
514    fn from_i8(v: i8) -> Result<Self> {
515        Ok(v)
516    }
517}
518
519impl FromRawValue<'_> for i16 {
520    fn from_i8(v: i8) -> Result<Self> {
521        Ok(v as i16)
522    }
523
524    fn from_i16(v: i16) -> Result<Self> {
525        Ok(v)
526    }
527}
528
529impl FromRawValue<'_> for i32 {
530    fn from_i8(v: i8) -> Result<Self> {
531        Ok(v as i32)
532    }
533
534    fn from_i16(v: i16) -> Result<Self> {
535        Ok(v as i32)
536    }
537
538    fn from_i32(v: i32) -> Result<Self> {
539        Ok(v)
540    }
541}
542
543impl FromRawValue<'_> for i64 {
544    fn from_i8(v: i8) -> Result<Self> {
545        Ok(v as i64)
546    }
547
548    fn from_i16(v: i16) -> Result<Self> {
549        Ok(v as i64)
550    }
551
552    fn from_i32(v: i32) -> Result<Self> {
553        Ok(v as i64)
554    }
555
556    fn from_i64(v: i64) -> Result<Self> {
557        Ok(v)
558    }
559}
560
561impl FromRawValue<'_> for bool {
562    fn from_i8(v: i8) -> Result<Self> {
563        Ok(v != 0)
564    }
565
566    fn from_u8(v: u8) -> Result<Self> {
567        Ok(v != 0)
568    }
569}
570
571impl FromRawValue<'_> for u8 {
572    fn from_u8(v: u8) -> Result<Self> {
573        Ok(v)
574    }
575}
576
577impl FromRawValue<'_> for u16 {
578    fn from_u8(v: u8) -> Result<Self> {
579        Ok(v as u16)
580    }
581
582    fn from_u16(v: u16) -> Result<Self> {
583        Ok(v)
584    }
585}
586
587impl FromRawValue<'_> for u32 {
588    fn from_u8(v: u8) -> Result<Self> {
589        Ok(v as u32)
590    }
591
592    fn from_u16(v: u16) -> Result<Self> {
593        Ok(v as u32)
594    }
595
596    fn from_u32(v: u32) -> Result<Self> {
597        Ok(v)
598    }
599}
600
601impl FromRawValue<'_> for u64 {
602    fn from_u8(v: u8) -> Result<Self> {
603        Ok(v as u64)
604    }
605
606    fn from_u16(v: u16) -> Result<Self> {
607        Ok(v as u64)
608    }
609
610    fn from_u32(v: u32) -> Result<Self> {
611        Ok(v as u64)
612    }
613
614    fn from_u64(v: u64) -> Result<Self> {
615        Ok(v)
616    }
617}
618
619impl FromRawValue<'_> for f32 {
620    fn from_float(v: f32) -> Result<Self> {
621        Ok(v)
622    }
623}
624
625impl FromRawValue<'_> for f64 {
626    fn from_double(v: f64) -> Result<Self> {
627        Ok(v)
628    }
629
630    fn from_float(v: f32) -> Result<Self> {
631        Ok(v as f64)
632    }
633}
634
635impl<'a> FromRawValue<'a> for &'a [u8] {
636    fn from_bytes(v: &'a [u8]) -> Result<Self> {
637        Ok(v)
638    }
639}
640
641impl FromRawValue<'_> for Vec<u8> {
642    fn from_bytes(v: &[u8]) -> Result<Self> {
643        Ok(v.to_vec())
644    }
645}
646
647impl<'a> FromRawValue<'a> for &'a str {
648    fn from_str(v: &'a [u8]) -> Result<Self> {
649        from_utf8(v).map_err(|e| {
650            Error::BadUsageError(format!("Cannot decode MySQL type STRING to &str: {}", e))
651        })
652    }
653}
654
655impl FromRawValue<'_> for String {
656    fn from_str(v: &[u8]) -> Result<Self> {
657        from_utf8(v).map(|s| s.to_owned()).map_err(|e| {
658            Error::BadUsageError(format!("Cannot decode MySQL type STRING to String: {}", e))
659        })
660    }
661}
662
663impl<'a, T: FromRawValue<'a>> FromRawValue<'a> for Option<T> {
664    fn from_null() -> Result<Self> {
665        Ok(None)
666    }
667
668    fn from_i8(v: i8) -> Result<Self> {
669        T::from_i8(v).map(Some)
670    }
671
672    fn from_i16(v: i16) -> Result<Self> {
673        T::from_i16(v).map(Some)
674    }
675
676    fn from_i32(v: i32) -> Result<Self> {
677        T::from_i32(v).map(Some)
678    }
679
680    fn from_i64(v: i64) -> Result<Self> {
681        T::from_i64(v).map(Some)
682    }
683
684    fn from_u8(v: u8) -> Result<Self> {
685        T::from_u8(v).map(Some)
686    }
687
688    fn from_u16(v: u16) -> Result<Self> {
689        T::from_u16(v).map(Some)
690    }
691
692    fn from_u32(v: u32) -> Result<Self> {
693        T::from_u32(v).map(Some)
694    }
695
696    fn from_u64(v: u64) -> Result<Self> {
697        T::from_u64(v).map(Some)
698    }
699
700    fn from_float(v: f32) -> Result<Self> {
701        T::from_float(v).map(Some)
702    }
703
704    fn from_double(v: f64) -> Result<Self> {
705        T::from_double(v).map(Some)
706    }
707
708    fn from_bytes(v: &'a [u8]) -> Result<Self> {
709        T::from_bytes(v).map(Some)
710    }
711
712    fn from_str(v: &'a [u8]) -> Result<Self> {
713        T::from_str(v).map(Some)
714    }
715
716    fn from_decimal(v: &'a [u8]) -> Result<Self> {
717        T::from_decimal(v).map(Some)
718    }
719
720    fn from_date0() -> Result<Self> {
721        T::from_date0().map(Some)
722    }
723
724    fn from_date4(v: &'a Timestamp4) -> Result<Self> {
725        T::from_date4(v).map(Some)
726    }
727
728    fn from_datetime0() -> Result<Self> {
729        T::from_datetime0().map(Some)
730    }
731
732    fn from_datetime4(v: &'a Timestamp4) -> Result<Self> {
733        T::from_datetime4(v).map(Some)
734    }
735
736    fn from_datetime7(v: &'a Timestamp7) -> Result<Self> {
737        T::from_datetime7(v).map(Some)
738    }
739
740    fn from_datetime11(v: &'a Timestamp11) -> Result<Self> {
741        T::from_datetime11(v).map(Some)
742    }
743
744    fn from_time0() -> Result<Self> {
745        T::from_time0().map(Some)
746    }
747
748    fn from_time8(v: &'a Time8) -> Result<Self> {
749        T::from_time8(v).map(Some)
750    }
751
752    fn from_time12(v: &'a Time12) -> Result<Self> {
753        T::from_time12(v).map(Some)
754    }
755}
756
757// ============================================================================
758// FromRawRow implementations for tuples
759// ============================================================================
760
761macro_rules! impl_from_raw_row_tuple {
762    ($($idx:tt: $T:ident),+) => {
763        impl<'buf, 'value, $($T: FromRawValue<'buf>),+> FromRawRow<'buf> for ($($T,)+) {
764            #[expect(non_snake_case)]
765            fn from_raw_row(cols: &[ColumnDefinition<'_>], row: BinaryRowPayload<'buf>) -> Result<Self> {
766                let mut data = row.values();
767                let null_bitmap = row.null_bitmap();
768                $(
769                    let ($T, rest) = parse_value::<$T>(&cols[$idx].tail, null_bitmap.is_null($idx), data)?;
770                    data = rest;
771                )+
772                let _ = data; // suppress unused warning for last element
773                Ok(($($T,)+))
774            }
775        }
776    };
777}
778
779impl_from_raw_row_tuple!(0: A);
780impl_from_raw_row_tuple!(0: A, 1: B);
781impl_from_raw_row_tuple!(0: A, 1: B, 2: C);
782impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D);
783impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E);
784impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
785impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
786impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
787impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I);
788impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J);
789impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K);
790impl_from_raw_row_tuple!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H, 8: I, 9: J, 10: K, 11: L);
791
792// ============================================================================
793// UUID support
794// ============================================================================
795
796#[cfg(feature = "with-uuid")]
797impl FromRawValue<'_> for uuid::Uuid {
798    fn from_str(v: &[u8]) -> Result<Self> {
799        let s = from_utf8(v).map_err(|e| {
800            Error::BadUsageError(format!("Cannot decode MySQL STRING to UUID: {}", e))
801        })?;
802        uuid::Uuid::parse_str(s)
803            .map_err(|e| Error::BadUsageError(format!("Cannot parse UUID from '{}': {}", s, e)))
804    }
805
806    fn from_bytes(v: &[u8]) -> Result<Self> {
807        uuid::Uuid::from_slice(v)
808            .map_err(|e| Error::BadUsageError(format!("Cannot decode MySQL BINARY to UUID: {}", e)))
809    }
810}
811
812// ============================================================================
813// chrono support
814// ============================================================================
815
816#[cfg(feature = "with-chrono")]
817impl FromRawValue<'_> for chrono::NaiveDate {
818    fn from_date0() -> Result<Self> {
819        Err(Error::BadUsageError(
820            "Cannot decode zero DATE to chrono::NaiveDate".to_string(),
821        ))
822    }
823
824    fn from_date4(v: &Timestamp4) -> Result<Self> {
825        chrono::NaiveDate::from_ymd_opt(v.year() as i32, v.month as u32, v.day as u32).ok_or_else(
826            || Error::BadUsageError(format!("Invalid date: {}-{}-{}", v.year(), v.month, v.day)),
827        )
828    }
829}
830
831#[cfg(feature = "with-chrono")]
832impl FromRawValue<'_> for chrono::NaiveTime {
833    fn from_time0() -> Result<Self> {
834        Ok(chrono::NaiveTime::from_hms_opt(0, 0, 0).expect("00:00:00 is valid"))
835    }
836
837    fn from_time8(v: &Time8) -> Result<Self> {
838        if v.is_negative() || v.days() > 0 {
839            return Err(Error::BadUsageError(
840                "Cannot decode TIME with days or negative to chrono::NaiveTime".to_string(),
841            ));
842        }
843        chrono::NaiveTime::from_hms_opt(v.hour as u32, v.minute as u32, v.second as u32).ok_or_else(
844            || {
845                Error::BadUsageError(format!(
846                    "Invalid time: {}:{}:{}",
847                    v.hour, v.minute, v.second
848                ))
849            },
850        )
851    }
852
853    fn from_time12(v: &Time12) -> Result<Self> {
854        if v.is_negative() || v.days() > 0 {
855            return Err(Error::BadUsageError(
856                "Cannot decode TIME with days or negative to chrono::NaiveTime".to_string(),
857            ));
858        }
859        chrono::NaiveTime::from_hms_micro_opt(
860            v.hour as u32,
861            v.minute as u32,
862            v.second as u32,
863            v.microsecond(),
864        )
865        .ok_or_else(|| {
866            Error::BadUsageError(format!(
867                "Invalid time: {}:{}:{}.{}",
868                v.hour,
869                v.minute,
870                v.second,
871                v.microsecond()
872            ))
873        })
874    }
875}
876
877#[cfg(feature = "with-chrono")]
878impl FromRawValue<'_> for chrono::NaiveDateTime {
879    fn from_datetime0() -> Result<Self> {
880        Err(Error::BadUsageError(
881            "Cannot decode zero DATETIME to chrono::NaiveDateTime".to_string(),
882        ))
883    }
884
885    fn from_datetime4(v: &Timestamp4) -> Result<Self> {
886        let date = chrono::NaiveDate::from_ymd_opt(v.year() as i32, v.month as u32, v.day as u32)
887            .ok_or_else(|| {
888            Error::BadUsageError(format!("Invalid date: {}-{}-{}", v.year(), v.month, v.day))
889        })?;
890        Ok(date.and_hms_opt(0, 0, 0).expect("00:00:00 is valid"))
891    }
892
893    fn from_datetime7(v: &Timestamp7) -> Result<Self> {
894        let date = chrono::NaiveDate::from_ymd_opt(v.year() as i32, v.month as u32, v.day as u32)
895            .ok_or_else(|| {
896            Error::BadUsageError(format!("Invalid date: {}-{}-{}", v.year(), v.month, v.day))
897        })?;
898        date.and_hms_opt(v.hour as u32, v.minute as u32, v.second as u32)
899            .ok_or_else(|| {
900                Error::BadUsageError(format!(
901                    "Invalid time: {}:{}:{}",
902                    v.hour, v.minute, v.second
903                ))
904            })
905    }
906
907    fn from_datetime11(v: &Timestamp11) -> Result<Self> {
908        let date = chrono::NaiveDate::from_ymd_opt(v.year() as i32, v.month as u32, v.day as u32)
909            .ok_or_else(|| {
910            Error::BadUsageError(format!("Invalid date: {}-{}-{}", v.year(), v.month, v.day))
911        })?;
912        date.and_hms_micro_opt(
913            v.hour as u32,
914            v.minute as u32,
915            v.second as u32,
916            v.microsecond(),
917        )
918        .ok_or_else(|| {
919            Error::BadUsageError(format!(
920                "Invalid datetime: {}-{}-{} {}:{}:{}.{}",
921                v.year(),
922                v.month,
923                v.day,
924                v.hour,
925                v.minute,
926                v.second,
927                v.microsecond()
928            ))
929        })
930    }
931}
932
933// ============================================================================
934// time crate support
935// ============================================================================
936
937#[cfg(feature = "with-time")]
938impl FromRawValue<'_> for time::Date {
939    fn from_date0() -> Result<Self> {
940        Err(Error::BadUsageError(
941            "Cannot decode zero DATE to time::Date".to_string(),
942        ))
943    }
944
945    fn from_date4(v: &Timestamp4) -> Result<Self> {
946        let month = time::Month::try_from(v.month)
947            .map_err(|e| Error::BadUsageError(format!("Invalid month {}: {}", v.month, e)))?;
948        time::Date::from_calendar_date(v.year() as i32, month, v.day).map_err(|e| {
949            Error::BadUsageError(format!(
950                "Invalid date {}-{}-{}: {}",
951                v.year(),
952                v.month,
953                v.day,
954                e
955            ))
956        })
957    }
958}
959
960#[cfg(feature = "with-time")]
961impl FromRawValue<'_> for time::Time {
962    fn from_time0() -> Result<Self> {
963        Ok(time::Time::MIDNIGHT)
964    }
965
966    fn from_time8(v: &Time8) -> Result<Self> {
967        if v.is_negative() || v.days() > 0 {
968            return Err(Error::BadUsageError(
969                "Cannot decode TIME with days or negative to time::Time".to_string(),
970            ));
971        }
972        time::Time::from_hms(v.hour, v.minute, v.second).map_err(|e| {
973            Error::BadUsageError(format!(
974                "Invalid time {}:{}:{}: {}",
975                v.hour, v.minute, v.second, e
976            ))
977        })
978    }
979
980    fn from_time12(v: &Time12) -> Result<Self> {
981        if v.is_negative() || v.days() > 0 {
982            return Err(Error::BadUsageError(
983                "Cannot decode TIME with days or negative to time::Time".to_string(),
984            ));
985        }
986        time::Time::from_hms_micro(v.hour, v.minute, v.second, v.microsecond()).map_err(|e| {
987            Error::BadUsageError(format!(
988                "Invalid time {}:{}:{}.{}: {}",
989                v.hour,
990                v.minute,
991                v.second,
992                v.microsecond(),
993                e
994            ))
995        })
996    }
997}
998
999#[cfg(feature = "with-time")]
1000impl FromRawValue<'_> for time::PrimitiveDateTime {
1001    fn from_datetime0() -> Result<Self> {
1002        Err(Error::BadUsageError(
1003            "Cannot decode zero DATETIME to time::PrimitiveDateTime".to_string(),
1004        ))
1005    }
1006
1007    fn from_datetime4(v: &Timestamp4) -> Result<Self> {
1008        let month = time::Month::try_from(v.month)
1009            .map_err(|e| Error::BadUsageError(format!("Invalid month {}: {}", v.month, e)))?;
1010        let date = time::Date::from_calendar_date(v.year() as i32, month, v.day).map_err(|e| {
1011            Error::BadUsageError(format!(
1012                "Invalid date {}-{}-{}: {}",
1013                v.year(),
1014                v.month,
1015                v.day,
1016                e
1017            ))
1018        })?;
1019        Ok(time::PrimitiveDateTime::new(date, time::Time::MIDNIGHT))
1020    }
1021
1022    fn from_datetime7(v: &Timestamp7) -> Result<Self> {
1023        let month = time::Month::try_from(v.month)
1024            .map_err(|e| Error::BadUsageError(format!("Invalid month {}: {}", v.month, e)))?;
1025        let date = time::Date::from_calendar_date(v.year() as i32, month, v.day).map_err(|e| {
1026            Error::BadUsageError(format!(
1027                "Invalid date {}-{}-{}: {}",
1028                v.year(),
1029                v.month,
1030                v.day,
1031                e
1032            ))
1033        })?;
1034        let time = time::Time::from_hms(v.hour, v.minute, v.second).map_err(|e| {
1035            Error::BadUsageError(format!(
1036                "Invalid time {}:{}:{}: {}",
1037                v.hour, v.minute, v.second, e
1038            ))
1039        })?;
1040        Ok(time::PrimitiveDateTime::new(date, time))
1041    }
1042
1043    fn from_datetime11(v: &Timestamp11) -> Result<Self> {
1044        let month = time::Month::try_from(v.month)
1045            .map_err(|e| Error::BadUsageError(format!("Invalid month {}: {}", v.month, e)))?;
1046        let date = time::Date::from_calendar_date(v.year() as i32, month, v.day).map_err(|e| {
1047            Error::BadUsageError(format!(
1048                "Invalid date {}-{}-{}: {}",
1049                v.year(),
1050                v.month,
1051                v.day,
1052                e
1053            ))
1054        })?;
1055        let time = time::Time::from_hms_micro(v.hour, v.minute, v.second, v.microsecond())
1056            .map_err(|e| {
1057                Error::BadUsageError(format!(
1058                    "Invalid time {}:{}:{}.{}: {}",
1059                    v.hour,
1060                    v.minute,
1061                    v.second,
1062                    v.microsecond(),
1063                    e
1064                ))
1065            })?;
1066        Ok(time::PrimitiveDateTime::new(date, time))
1067    }
1068}
1069
1070// ============================================================================
1071// rust_decimal support
1072// ============================================================================
1073
1074#[cfg(feature = "with-rust-decimal")]
1075impl FromRawValue<'_> for rust_decimal::Decimal {
1076    fn from_decimal(v: &[u8]) -> Result<Self> {
1077        let s = from_utf8(v).map_err(|e| {
1078            Error::BadUsageError(format!("Cannot decode MySQL DECIMAL to string: {}", e))
1079        })?;
1080        s.parse()
1081            .map_err(|e| Error::BadUsageError(format!("Cannot parse Decimal from '{}': {}", s, e)))
1082    }
1083
1084    fn from_str(v: &[u8]) -> Result<Self> {
1085        Self::from_decimal(v)
1086    }
1087}