zero_mysql/protocol/trait/
param.rs

1use auto_impl::auto_impl;
2
3use crate::constant::ColumnType;
4use crate::error::Result;
5use crate::protocol::primitive::*;
6
7/// Parameter indicator for COM_STMT_BULK_EXECUTE
8///
9/// See: https://mariadb.com/docs/server/reference/clientserver-protocol/3-binary-protocol-prepared-statements/com_stmt_bulk_execute
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[repr(u8)]
12pub enum ParamIndicator {
13    /// Value follows (0)
14    None = 0,
15    /// Value is null (1)
16    Null = 1,
17    /// For INSERT/UPDATE, value is default (2)
18    Default = 2,
19    /// Value is default for insert, ignored for update (3)
20    Ignore = 3,
21}
22
23pub trait Param {
24    fn is_null(&self) -> bool;
25    fn encode_type(&self, out: &mut Vec<u8>);
26    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()>;
27}
28
29pub trait TypedParam {
30    fn is_null(&self) -> bool {
31        false
32    }
33    fn encode_type(out: &mut Vec<u8>);
34    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()>;
35}
36
37impl TypedParam for i8 {
38    fn encode_type(out: &mut Vec<u8>) {
39        out.push(ColumnType::MYSQL_TYPE_TINY as u8);
40        out.push(0x00);
41    }
42
43    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
44        write_int_1(out, *self as u8);
45        Ok(())
46    }
47}
48
49impl TypedParam for i16 {
50    fn encode_type(out: &mut Vec<u8>) {
51        out.push(ColumnType::MYSQL_TYPE_SHORT as u8);
52        out.push(0x00);
53    }
54
55    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
56        write_int_2(out, *self as u16);
57        Ok(())
58    }
59}
60
61impl TypedParam for i32 {
62    fn encode_type(out: &mut Vec<u8>) {
63        out.push(ColumnType::MYSQL_TYPE_LONG as u8);
64        out.push(0x00);
65    }
66
67    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
68        write_int_4(out, *self as u32);
69        Ok(())
70    }
71}
72
73impl TypedParam for i64 {
74    fn encode_type(out: &mut Vec<u8>) {
75        out.push(ColumnType::MYSQL_TYPE_LONGLONG as u8);
76        out.push(0x00);
77    }
78
79    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
80        write_int_8(out, *self as u64);
81        Ok(())
82    }
83}
84
85impl TypedParam for u8 {
86    fn encode_type(out: &mut Vec<u8>) {
87        out.push(ColumnType::MYSQL_TYPE_TINY as u8);
88        out.push(0x80);
89    }
90
91    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
92        write_int_1(out, *self);
93        Ok(())
94    }
95}
96
97impl TypedParam for u16 {
98    fn encode_type(out: &mut Vec<u8>) {
99        out.push(ColumnType::MYSQL_TYPE_SHORT as u8);
100        out.push(0x80);
101    }
102
103    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
104        write_int_2(out, *self);
105        Ok(())
106    }
107}
108
109impl TypedParam for u32 {
110    fn encode_type(out: &mut Vec<u8>) {
111        out.push(ColumnType::MYSQL_TYPE_LONG as u8);
112        out.push(0x80);
113    }
114
115    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
116        write_int_4(out, *self);
117        Ok(())
118    }
119}
120
121impl TypedParam for u64 {
122    fn encode_type(out: &mut Vec<u8>) {
123        out.push(ColumnType::MYSQL_TYPE_LONGLONG as u8);
124        out.push(0x80);
125    }
126
127    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
128        write_int_8(out, *self);
129        Ok(())
130    }
131}
132
133impl TypedParam for f32 {
134    fn encode_type(out: &mut Vec<u8>) {
135        out.push(ColumnType::MYSQL_TYPE_FLOAT as u8);
136        out.push(0x00);
137    }
138
139    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
140        write_int_4(out, self.to_bits());
141        Ok(())
142    }
143}
144
145impl TypedParam for f64 {
146    fn encode_type(out: &mut Vec<u8>) {
147        out.push(ColumnType::MYSQL_TYPE_DOUBLE as u8);
148        out.push(0x00);
149    }
150
151    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
152        write_int_8(out, self.to_bits());
153        Ok(())
154    }
155}
156
157impl TypedParam for &str {
158    fn encode_type(out: &mut Vec<u8>) {
159        out.push(ColumnType::MYSQL_TYPE_VAR_STRING as u8);
160        out.push(0x00);
161    }
162
163    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
164        write_string_lenenc(out, self);
165        Ok(())
166    }
167}
168
169impl TypedParam for String {
170    fn encode_type(out: &mut Vec<u8>) {
171        out.push(ColumnType::MYSQL_TYPE_VAR_STRING as u8);
172        out.push(0x00);
173    }
174
175    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
176        write_string_lenenc(out, self);
177        Ok(())
178    }
179}
180
181impl TypedParam for &String {
182    fn encode_type(out: &mut Vec<u8>) {
183        out.push(ColumnType::MYSQL_TYPE_VAR_STRING as u8);
184        out.push(0x00);
185    }
186
187    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
188        write_string_lenenc(out, self);
189        Ok(())
190    }
191}
192
193impl TypedParam for &[u8] {
194    fn encode_type(out: &mut Vec<u8>) {
195        out.push(ColumnType::MYSQL_TYPE_BLOB as u8);
196        out.push(0x00);
197    }
198
199    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
200        write_bytes_lenenc(out, self);
201        Ok(())
202    }
203}
204
205impl TypedParam for Vec<u8> {
206    fn encode_type(out: &mut Vec<u8>) {
207        out.push(ColumnType::MYSQL_TYPE_BLOB as u8);
208        out.push(0x00);
209    }
210
211    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
212        write_bytes_lenenc(out, self);
213        Ok(())
214    }
215}
216
217impl TypedParam for &Vec<u8> {
218    fn encode_type(out: &mut Vec<u8>) {
219        out.push(ColumnType::MYSQL_TYPE_BLOB as u8);
220        out.push(0x00);
221    }
222
223    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
224        write_bytes_lenenc(out, self);
225        Ok(())
226    }
227}
228
229impl<T: TypedParam> TypedParam for Option<T> {
230    fn is_null(&self) -> bool {
231        self.is_none()
232    }
233
234    fn encode_type(out: &mut Vec<u8>) {
235        T::encode_type(out);
236    }
237
238    fn encode_value(&self, out: &mut Vec<u8>) -> Result<()> {
239        match self {
240            Some(value) => value.encode_value(out),
241            None => Ok(()),
242        }
243    }
244}
245
246// ============================================================================
247// Tests
248// ============================================================================
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253
254    #[test]
255    fn test_param_i32() {
256        let param: i32 = -42;
257        let mut types = Vec::new();
258        let mut values = Vec::new();
259
260        i32::encode_type(&mut types);
261        param.encode_value(&mut values).unwrap();
262
263        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_LONG as u8, 0x00]);
264        assert_eq!(values, (-42i32).to_le_bytes());
265        assert!(!param.is_null());
266    }
267
268    #[test]
269    fn test_param_u64() {
270        let param: u64 = 12345678901234;
271        let mut types = Vec::new();
272        let mut values = Vec::new();
273
274        u64::encode_type(&mut types);
275        param.encode_value(&mut values).unwrap();
276
277        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_LONGLONG as u8, 0x80]);
278        assert_eq!(values, 12345678901234u64.to_le_bytes());
279    }
280
281    #[test]
282    fn test_param_f64() {
283        let param: f64 = 3.14159;
284        let mut types = Vec::new();
285        let mut values = Vec::new();
286
287        f64::encode_type(&mut types);
288        param.encode_value(&mut values).unwrap();
289
290        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_DOUBLE as u8, 0x00]);
291        assert_eq!(values, 3.14159f64.to_bits().to_le_bytes());
292    }
293
294    #[test]
295    fn test_param_str() {
296        let param = "Hello, MySQL!";
297        let mut types = Vec::new();
298        let mut values = Vec::new();
299
300        <&str>::encode_type(&mut types);
301        param.encode_value(&mut values).unwrap();
302
303        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_VAR_STRING as u8, 0x00]);
304        assert_eq!(values[0], 13);
305        assert_eq!(&values[1..], b"Hello, MySQL!");
306    }
307
308    #[test]
309    fn test_param_string() {
310        let param = String::from("Rust");
311        let mut types = Vec::new();
312        let mut values = Vec::new();
313
314        String::encode_type(&mut types);
315        param.encode_value(&mut values).unwrap();
316
317        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_VAR_STRING as u8, 0x00]);
318        assert_eq!(values[0], 4);
319        assert_eq!(&values[1..], b"Rust");
320    }
321
322    #[test]
323    fn test_param_bytes() {
324        let param: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
325        let mut types = Vec::new();
326        let mut values = Vec::new();
327
328        <&[u8]>::encode_type(&mut types);
329        param.encode_value(&mut values).unwrap();
330
331        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_BLOB as u8, 0x00]);
332        assert_eq!(values[0], 4);
333        assert_eq!(&values[1..], &[0xDE, 0xAD, 0xBE, 0xEF]);
334    }
335
336    #[test]
337    fn test_param_vec_u8() {
338        let param = vec![1u8, 2, 3, 4, 5];
339        let mut types = Vec::new();
340        let mut values = Vec::new();
341
342        Vec::<u8>::encode_type(&mut types);
343        param.encode_value(&mut values).unwrap();
344
345        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_BLOB as u8, 0x00]);
346        assert_eq!(values[0], 5);
347        assert_eq!(&values[1..], &[1, 2, 3, 4, 5]);
348    }
349
350    #[test]
351    fn test_param_option_some() {
352        let param = Some(42i32);
353        let mut types = Vec::new();
354        let mut values = Vec::new();
355
356        assert!(!param.is_null());
357        Option::<i32>::encode_type(&mut types);
358        param.encode_value(&mut values).unwrap();
359
360        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_LONG as u8, 0x00]);
361        assert_eq!(values, 42i32.to_le_bytes());
362    }
363
364    #[test]
365    fn test_param_option_none() {
366        let param: Option<i32> = None;
367        let mut types = Vec::new();
368        let mut values = Vec::new();
369
370        assert!(param.is_null());
371        Option::<i32>::encode_type(&mut types);
372        param.encode_value(&mut values).unwrap();
373
374        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_LONG as u8, 0x00]);
375        assert_eq!(values, Vec::<u8>::new());
376    }
377
378    #[test]
379    fn test_param_option_string() {
380        let param = Some("test".to_string());
381        let mut types = Vec::new();
382        let mut values = Vec::new();
383
384        Option::<String>::encode_type(&mut types);
385        param.encode_value(&mut values).unwrap();
386
387        assert_eq!(types, vec![ColumnType::MYSQL_TYPE_VAR_STRING as u8, 0x00]);
388        assert_eq!(values[0], 4);
389        assert_eq!(&values[1..], b"test");
390    }
391}
392
393// ============================================================================
394// Params trait - for collections of parameters
395// ============================================================================
396
397/// Trait for parameter binding in prepared statements
398///
399/// This trait is implemented by external libraries to provide a custom parameter serialization.
400pub trait Params {
401    /// Number of parameters
402    fn len(&self) -> usize;
403
404    fn is_empty(&self) -> bool {
405        self.len() == 0
406    }
407
408    /// Write NULL bitmap
409    ///
410    /// The NULL bitmap is (num_params + 7) / 8 bytes long.
411    /// Bit is set to 1 if the parameter is NULL.
412    fn encode_null_bitmap(&self, out: &mut Vec<u8>);
413
414    /// Write parameter types
415    ///
416    /// Each parameter type is 2 bytes:
417    /// - 1 byte: MySQL type (MYSQL_TYPE_*)
418    /// - 1 byte: unsigned flag (0x80 if unsigned, 0x00 otherwise)
419    fn encode_types(&self, out: &mut Vec<u8>);
420
421    /// Write parameter values (binary encoded)
422    ///
423    /// Values are encoded according to MySQL binary protocol.
424    /// NULL parameters should be skipped (they're already in the NULL bitmap).
425    fn encode_values(&self, out: &mut Vec<u8>) -> Result<()>;
426
427    /// Write parameter values for bulk execution (COM_STMT_BULK_EXECUTE)
428    ///
429    /// Format:
430    /// - First: parameter indicators (1 byte per parameter)
431    /// - Then: values (only for parameters with indicator None)
432    ///
433    /// See: https://mariadb.com/docs/server/reference/clientserver-protocol/3-binary-protocol-prepared-statements/com_stmt_bulk_execute
434    fn encode_values_for_bulk(&self, out: &mut Vec<u8>) -> Result<()>;
435}
436
437#[auto_impl(&)]
438pub trait TypedParams {
439    fn len(&self) -> usize;
440    fn is_empty(&self) -> bool {
441        self.len() == 0
442    }
443    fn encode_null_bitmap(&self, out: &mut Vec<u8>);
444    fn encode_types(out: &mut Vec<u8>);
445    fn encode_values(&self, out: &mut Vec<u8>) -> Result<()>;
446    fn encode_values_for_bulk(&self, out: &mut Vec<u8>) -> Result<()>;
447}
448
449impl<T: TypedParams> Params for T {
450    fn len(&self) -> usize {
451        TypedParams::len(self)
452    }
453    fn encode_null_bitmap(&self, out: &mut Vec<u8>) {
454        TypedParams::encode_null_bitmap(self, out)
455    }
456    fn encode_types(&self, out: &mut Vec<u8>) {
457        T::encode_types(out)
458    }
459    fn encode_values(&self, out: &mut Vec<u8>) -> Result<()> {
460        TypedParams::encode_values(self, out)
461    }
462    fn encode_values_for_bulk(&self, out: &mut Vec<u8>) -> Result<()> {
463        TypedParams::encode_values_for_bulk(self, out)
464    }
465}
466
467impl TypedParams for () {
468    fn len(&self) -> usize {
469        0
470    }
471    fn encode_null_bitmap(&self, _out: &mut Vec<u8>) {}
472    fn encode_types(_out: &mut Vec<u8>) {}
473    fn encode_values(&self, _out: &mut Vec<u8>) -> Result<()> {
474        Ok(())
475    }
476    fn encode_values_for_bulk(&self, _out: &mut Vec<u8>) -> Result<()> {
477        Ok(())
478    }
479}
480
481// ============================================================================
482// Tuple implementations for common sizes
483// ============================================================================
484
485macro_rules! impl_params_for_tuple {
486    ($($T:ident : $idx:tt),+) => {
487        impl<$($T: TypedParam),+> TypedParams for ($($T,)+) {
488            fn len(&self) -> usize {
489                let mut count = 0;
490                $(
491                    let _ = &self.$idx;
492                    count += 1;
493                )+
494                count
495            }
496
497            fn encode_null_bitmap(&self, out: &mut Vec<u8>) {
498                let num_bytes = TypedParams::len(self).div_ceil(8);
499                let start_len = out.len();
500                out.resize(start_len + num_bytes, 0);
501
502                $(
503                    if self.$idx.is_null() {
504                        let byte_pos = start_len + ($idx >> 3);
505                        let bit_offset = $idx & 7;
506                        out[byte_pos] |= 1 << bit_offset;
507                    }
508                )+
509            }
510
511            fn encode_types(out: &mut Vec<u8>) {
512                $(
513                    $T::encode_type(out);
514                )+
515            }
516
517            fn encode_values(&self, out: &mut Vec<u8>) -> Result<()> {
518                $(
519                    if !self.$idx.is_null() {
520                        self.$idx.encode_value(out)?;
521                    }
522                )+
523                Ok(())
524            }
525
526            fn encode_values_for_bulk(&self, out: &mut Vec<u8>) -> Result<()> {
527                $(
528                    if self.$idx.is_null() {
529                        out.push(ParamIndicator::Null as u8);
530                    } else {
531                        out.push(ParamIndicator::None as u8);
532                        self.$idx.encode_value(out)?;
533                    }
534                )+
535                Ok(())
536            }
537        }
538    };
539}
540
541// Implement for tuples of size 1-12
542impl_params_for_tuple!(T0: 0);
543impl_params_for_tuple!(T0: 0, T1: 1);
544impl_params_for_tuple!(T0: 0, T1: 1, T2: 2);
545impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3);
546impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4);
547impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5);
548impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6);
549impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7);
550impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8);
551impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9);
552impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10);
553impl_params_for_tuple!(T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11);
554
555// ============================================================================
556// Tests for Params trait
557// ============================================================================
558
559#[cfg(test)]
560mod params_tests {
561    use super::*;
562
563    #[test]
564    fn test_params_tuple() {
565        let params = (42i32, "hello", 3.14f64);
566        assert_eq!(Params::len(&params), 3);
567
568        let mut null_bitmap = Vec::new();
569        Params::encode_null_bitmap(&params, &mut null_bitmap);
570        assert_eq!(null_bitmap, vec![0]);
571
572        let mut types = Vec::new();
573        Params::encode_types(&params, &mut types);
574        assert_eq!(types.len(), 6);
575
576        let mut values = Vec::new();
577        Params::encode_values(&params, &mut values).unwrap();
578        assert!(values.len() > 12);
579    }
580
581    #[test]
582    fn test_params_tuple_with_option() {
583        let params = (Some(42i32), None::<String>, Some("test"));
584        assert_eq!(Params::len(&params), 3);
585
586        let mut null_bitmap = Vec::new();
587        Params::encode_null_bitmap(&params, &mut null_bitmap);
588        assert_eq!(null_bitmap, vec![0b00000010]);
589
590        let mut values = Vec::new();
591        Params::encode_values(&params, &mut values).unwrap();
592        assert_eq!(values.len(), 9);
593    }
594
595    #[test]
596    fn test_params_mixed_types() {
597        let params = (
598            1i8, 2i16, 3i32, 4i64, 5u8, 6u16, 7u32, 8u64, 1.5f32, 2.5f64, "hello",
599        );
600        assert_eq!(Params::len(&params), 11);
601
602        let mut types = Vec::new();
603        Params::encode_types(&params, &mut types);
604        assert_eq!(types.len(), 22);
605
606        let mut values = Vec::new();
607        Params::encode_values(&params, &mut values).unwrap();
608        assert_eq!(values.len(), 48);
609    }
610
611    #[test]
612    fn test_params_string_variants() {
613        let s1 = "hello";
614        let s2 = String::from("world");
615        let s3 = &String::from("test");
616
617        let params = (s1, s2, s3);
618        assert_eq!(Params::len(&params), 3);
619
620        let mut values = Vec::new();
621        Params::encode_values(&params, &mut values).unwrap();
622        assert_eq!(values.len(), 17);
623    }
624
625    #[test]
626    fn test_params_byte_variants() {
627        let b1: &[u8] = &[1, 2, 3];
628        let b2 = vec![4, 5, 6];
629        let b3 = &vec![7, 8];
630
631        let params = (b1, b2, b3);
632        assert_eq!(Params::len(&params), 3);
633
634        let mut out = Vec::new();
635        Params::encode_values(&params, &mut out).unwrap();
636        assert_eq!(out.len(), 11);
637    }
638}