zero_mysql/
value.rs

1/// MySQL Binary Protocol Value Types
2use zerocopy::byteorder::little_endian::{U16 as U16LE, U32 as U32LE};
3use zerocopy::{FromBytes, Immutable, KnownLayout};
4
5#[derive(Debug, Clone, Copy)]
6pub enum Value<'a> {
7    /// NULL value
8    Null,
9    /// Signed integer (TINYINT, SMALLINT, INT, BIGINT)
10    SignedInt(i64),
11    /// Unsigned integer (TINYINT UNSIGNED, SMALLINT UNSIGNED, INT UNSIGNED, BIGINT UNSIGNED)
12    UnsignedInt(u64),
13    /// FLOAT - 4-byte floating point
14    Float(f32),
15    /// DOUBLE - 8-byte floating point
16    Double(f64),
17    /// DATE - 0 bytes (0000-00-00)
18    Date0,
19    /// DATE - 4 bytes (ymd)
20    Date4(&'a Timestamp4),
21    /// DATETIME/TIMESTAMP - 0 bytes (0000-00-00 00:00:00)
22    Datetime0,
23    /// DATETIME/TIMESTAMP - 4 bytes (ymd)
24    Datetime4(&'a Timestamp4),
25    /// DATETIME/TIMESTAMP - 7 bytes (ymd + hms)
26    Datetime7(&'a Timestamp7),
27    /// DATETIME/TIMESTAMP - 11 bytes (ymd + hms + microseconds)
28    Datetime11(&'a Timestamp11),
29    /// TIME - 0 bytes (00:00:00)
30    Time0,
31    /// TIME - 8 bytes (without microseconds)
32    Time8(&'a Time8),
33    /// TIME - 12 bytes (with microseconds)
34    Time12(&'a Time12),
35    /// BLOB, GEOMETRY, STRING, VARCHAR, VAR_STRING, ..
36    Byte(&'a [u8]),
37}
38
39// ============================================================================
40// Temporal Types
41// ============================================================================
42
43/// TIMESTAMP - 4 bytes (DATE/DATETIME/TIMESTAMP with date only)
44#[repr(C, packed)]
45#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
46pub struct Timestamp4 {
47    pub year: U16LE,
48    pub month: u8,
49    pub day: u8,
50}
51
52impl Timestamp4 {
53    pub fn year(&self) -> u16 {
54        self.year.get()
55    }
56}
57
58/// TIMESTAMP - 7 bytes (DATE/DATETIME/TIMESTAMP without microseconds)
59#[repr(C, packed)]
60#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
61pub struct Timestamp7 {
62    pub year: U16LE,
63    pub month: u8,
64    pub day: u8,
65    pub hour: u8,
66    pub minute: u8,
67    pub second: u8,
68}
69
70impl Timestamp7 {
71    pub fn year(&self) -> u16 {
72        self.year.get()
73    }
74}
75
76/// TIMESTAMP - 11 bytes (DATE/DATETIME/TIMESTAMP with microseconds)
77#[repr(C, packed)]
78#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
79pub struct Timestamp11 {
80    pub year: U16LE,
81    pub month: u8,
82    pub day: u8,
83    pub hour: u8,
84    pub minute: u8,
85    pub second: u8,
86    pub microsecond: U32LE,
87}
88
89impl Timestamp11 {
90    pub fn year(&self) -> u16 {
91        self.year.get()
92    }
93
94    pub fn microsecond(&self) -> u32 {
95        self.microsecond.get()
96    }
97}
98
99/// TIME - 8 bytes
100#[repr(C, packed)]
101#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
102pub struct Time8 {
103    pub is_negative: u8,
104    pub days: U32LE,
105    pub hour: u8,
106    pub minute: u8,
107    pub second: u8,
108}
109
110impl Time8 {
111    pub fn is_negative(&self) -> bool {
112        self.is_negative != 0
113    }
114
115    pub fn days(&self) -> u32 {
116        self.days.get()
117    }
118}
119
120/// TIME - 12 bytesative (1), days (4 LE), hour (1), minute (1), second (1), microsecond (4 LE)
121#[repr(C, packed)]
122#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
123pub struct Time12 {
124    pub is_negative: u8,
125    pub days: U32LE,
126    pub hour: u8,
127    pub minute: u8,
128    pub second: u8,
129    pub microsecond: U32LE,
130}
131
132impl Time12 {
133    pub fn is_negative(&self) -> bool {
134        self.is_negative != 0
135    }
136
137    pub fn days(&self) -> u32 {
138        self.days.get()
139    }
140
141    pub fn microsecond(&self) -> u32 {
142        self.microsecond.get()
143    }
144}
145
146// ============================================================================
147// NULL Bitmap
148// ============================================================================
149
150/// NULL bitmap for binary protocol
151///
152/// In MySQL binary protocol, NULL values are indicated by a bitmap where each bit
153/// represents whether a column is NULL (1 = NULL, 0 = not NULL).
154///
155/// For result sets (COM_STMT_EXECUTE response), the bitmap has an offset of 2 bits.
156/// For prepared statement parameters, the offset is 0 bits.
157#[derive(Debug, Clone, Copy)]
158pub struct NullBitmap<'a> {
159    bitmap: &'a [u8],
160    offset: usize,
161}
162
163impl<'a> NullBitmap<'a> {
164    /// Create a NULL bitmap for result sets (offset = 2)
165    pub fn for_result_set(bitmap: &'a [u8]) -> Self {
166        Self { bitmap, offset: 2 }
167    }
168
169    /// Create a NULL bitmap for parameters (offset = 0)
170    pub fn for_parameters(bitmap: &'a [u8]) -> Self {
171        Self { bitmap, offset: 0 }
172    }
173
174    /// Check if the column at the given index is NULL
175    ///
176    /// # Arguments
177    /// * `idx` - Column index (0-based)
178    ///
179    /// # Returns
180    /// `true` if the column is NULL, `false` otherwise
181    pub fn is_null(&self, idx: usize) -> bool {
182        let bit_pos = idx + self.offset;
183        let byte_pos = bit_pos >> 3;
184        let bit_offset = bit_pos & 7;
185
186        if byte_pos >= self.bitmap.len() {
187            return false;
188        }
189
190        (self.bitmap[byte_pos] & (1 << bit_offset)) != 0
191    }
192
193    /// Get the raw bitmap bytes
194    pub fn as_bytes(&self) -> &'a [u8] {
195        self.bitmap
196    }
197}