zero_mysql/protocol/command/
column_definition.rs

1use crate::constant::{ColumnFlags, ColumnType};
2use crate::error::{Error, Result, eyre};
3use crate::protocol::primitive::*;
4use zerocopy::byteorder::little_endian::{U16 as U16LE, U32 as U32LE};
5use zerocopy::{FromBytes, Immutable, KnownLayout};
6
7/// Represents a payload part of a column definition packet
8#[derive(Debug, Clone, Copy)]
9pub struct ColumnDefinitionBytes<'a>(pub &'a [u8]);
10
11impl<'a> ColumnDefinitionBytes<'a> {
12    /// Get a reference to the fixed-size tail of the column definition
13    ///
14    /// The tail is always the last 12 bytes of the column definition packet
15    pub fn tail(&self) -> Result<&'a ColumnDefinitionTail> {
16        if self.0.len() < 12 {
17            return Err(Error::LibraryBug(eyre!(
18                "column definition too short: {} < 12",
19                self.0.len()
20            )));
21        }
22        let tail_bytes = &self.0[self.0.len() - 12..];
23        Ok(ColumnDefinitionTail::ref_from_bytes(tail_bytes)?)
24    }
25}
26
27/// The column definition parsed from `ColumnDefinitionBytes`
28#[derive(Debug, Clone)]
29pub struct ColumnDefinition<'a> {
30    pub schema: &'a [u8],
31    pub table_alias: &'a [u8],
32    pub table_original: &'a [u8],
33    pub name_alias: &'a [u8],
34    pub name_original: &'a [u8],
35    pub tail: &'a ColumnDefinitionTail,
36}
37
38impl<'a> TryFrom<ColumnDefinitionBytes<'a>> for ColumnDefinition<'a> {
39    type Error = Error;
40
41    fn try_from(bytes: ColumnDefinitionBytes<'a>) -> Result<Self> {
42        let data = bytes.0;
43
44        // ─── Variable Length String Fields ───────────────────────────
45        let (_catalog, data) = read_string_lenenc(data)?;
46        let (schema, data) = read_string_lenenc(data)?;
47        let (table_alias, data) = read_string_lenenc(data)?;
48        let (table_original, data) = read_string_lenenc(data)?;
49        let (name_alias, data) = read_string_lenenc(data)?;
50        let (name_original, data) = read_string_lenenc(data)?;
51
52        // ─── Columndefinitiontail ────────────────────────────────────
53        // length is always 0x0c
54        let (_length, data) = read_int_lenenc(data)?;
55        let tail = ColumnDefinitionTail::ref_from_bytes(data)?;
56        Ok(Self {
57            // catalog,
58            schema,
59            table_alias,
60            table_original,
61            name_alias,
62            name_original,
63            tail,
64        })
65    }
66}
67
68/// Fixed-size tail of Column Definition packet (12 bytes)
69#[repr(C, packed)]
70#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
71pub struct ColumnDefinitionTail {
72    charset: U16LE,
73    column_length: U32LE,
74    column_type: u8,
75    flags: U16LE,
76    decimals: u8,
77    reserved: U16LE,
78}
79
80impl ColumnDefinitionTail {
81    pub fn charset(&self) -> u16 {
82        self.charset.get()
83    }
84
85    pub fn column_length(&self) -> u32 {
86        self.column_length.get()
87    }
88
89    pub fn column_type(&self) -> Result<ColumnType> {
90        ColumnType::from_u8(self.column_type).ok_or_else(|| {
91            Error::LibraryBug(eyre!("unknown column type: 0x{:02X}", self.column_type))
92        })
93    }
94
95    pub fn flags(&self) -> Result<ColumnFlags> {
96        ColumnFlags::from_bits(self.flags.get()).ok_or_else(|| {
97            Error::LibraryBug(eyre!("invalid column flags: 0x{:04X}", self.flags.get()))
98        })
99    }
100}
101
102pub struct ColumnDefinitions {
103    _packets: Vec<u8>, // concatenation of packets (length(usize, native endian) + payload)
104    definitions: Vec<ColumnDefinition<'static>>,
105}
106
107impl ColumnDefinitions {
108    pub fn new(num_columns: usize, packets: Vec<u8>) -> Result<Self> {
109        let definitions = {
110            let mut buf = packets.as_slice();
111            let mut definitions = Vec::with_capacity(num_columns);
112            for _ in 0..num_columns {
113                let len = u32::from_ne_bytes(buf[..4].try_into().unwrap()) as usize;
114                definitions.push(ColumnDefinition::try_from(ColumnDefinitionBytes(
115                    &buf[4..4 + len],
116                ))?);
117                buf = &buf[4 + len..]; // Advance past the length prefix and payload
118            }
119
120            // Safety: borrowed data is valid for 'static because Self holds packets
121            unsafe {
122                std::mem::transmute::<Vec<ColumnDefinition<'_>>, Vec<ColumnDefinition<'static>>>(
123                    definitions,
124                )
125            }
126        };
127
128        Ok(Self {
129            _packets: packets,
130            definitions,
131        })
132    }
133
134    pub fn definitions<'a>(&'a self) -> &'a [ColumnDefinition<'a>] {
135        self.definitions.as_slice()
136    }
137}