zero_mysql/ref_row.rs
1//! Zero-copy row decoding for fixed-size types.
2//!
3//! This module provides traits and types for zero-copy decoding of database rows
4//! where all fields have fixed wire sizes. This is useful for high-performance
5//! scenarios where avoiding allocations is critical.
6//!
7//! # Requirements
8//!
9//! - All struct fields must implement `FixedWireSize`
10//! - All columns must be `NOT NULL` (no `Option<T>` support)
11//! - Struct must use `#[repr(C, packed)]` for predictable layout
12//! - Fields must use endian-aware types (e.g., `I64LE` instead of `i64`)
13//!
14//! # Example
15//!
16//! ```ignore
17//! use zerocopy::little_endian::{I64 as I64LE, I32 as I32LE};
18//! use zero_mysql::ref_row::RefFromRow;
19//!
20//! #[derive(RefFromRow)]
21//! #[repr(C, packed)]
22//! struct UserStats {
23//! user_id: I64LE,
24//! login_count: I32LE,
25//! }
26//! ```
27
28use crate::error::Result;
29use crate::protocol::BinaryRowPayload;
30use crate::protocol::command::ColumnDefinition;
31
32/// Marker trait for types with a fixed wire size in MySQL binary protocol.
33///
34/// This trait is only implemented for types that have a guaranteed fixed size
35/// on the wire. Native integer types like `i64` are NOT implemented because
36/// MySQL uses little-endian encoding, which differs from native byte order on
37/// big-endian platforms.
38///
39/// Use zerocopy's little-endian types instead:
40/// - `zerocopy::little_endian::I16` instead of `i16`
41/// - `zerocopy::little_endian::I32` instead of `i32`
42/// - `zerocopy::little_endian::I64` instead of `i64`
43/// - etc.
44pub trait FixedWireSize {
45 /// The fixed size in bytes on the wire.
46 const WIRE_SIZE: usize;
47}
48
49// Single-byte types are endian-agnostic
50impl FixedWireSize for i8 {
51 const WIRE_SIZE: usize = 1;
52}
53impl FixedWireSize for u8 {
54 const WIRE_SIZE: usize = 1;
55}
56
57// Little-endian integer types (MySQL wire format)
58impl FixedWireSize for zerocopy::little_endian::I16 {
59 const WIRE_SIZE: usize = 2;
60}
61impl FixedWireSize for zerocopy::little_endian::U16 {
62 const WIRE_SIZE: usize = 2;
63}
64impl FixedWireSize for zerocopy::little_endian::I32 {
65 const WIRE_SIZE: usize = 4;
66}
67impl FixedWireSize for zerocopy::little_endian::U32 {
68 const WIRE_SIZE: usize = 4;
69}
70impl FixedWireSize for zerocopy::little_endian::I64 {
71 const WIRE_SIZE: usize = 8;
72}
73impl FixedWireSize for zerocopy::little_endian::U64 {
74 const WIRE_SIZE: usize = 8;
75}
76
77// Re-export little-endian types for convenience
78pub use zerocopy::little_endian::{
79 I16 as I16LE, I32 as I32LE, I64 as I64LE, U16 as U16LE, U32 as U32LE, U64 as U64LE,
80};
81
82/// Trait for zero-copy decoding of a row into a fixed-size struct.
83///
84/// Unlike `FromRow`, this trait returns a reference directly into the buffer
85/// without any copying or allocation. This requires:
86///
87/// 1. All fields have fixed wire sizes (implement `FixedWireSize`)
88/// 2. No NULL values (columns must be `NOT NULL`)
89/// 3. Struct has `#[repr(C, packed)]` layout
90///
91/// The derive macro generates zerocopy trait implementations automatically.
92pub trait RefFromRow<'buf>: Sized {
93 /// Decode a row as a zero-copy reference.
94 ///
95 /// # Errors
96 ///
97 /// Returns an error if:
98 /// - The row data size doesn't match the struct size
99 /// - Any column is NULL (RefFromRow doesn't support NULL)
100 fn ref_from_row(
101 cols: &[ColumnDefinition<'_>],
102 row: BinaryRowPayload<'buf>,
103 ) -> Result<&'buf Self>;
104}