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);