msql_srv/
params.rs

1use crate::myc;
2use crate::{StatementData, Value};
3use std::collections::HashMap;
4use std::convert::TryFrom;
5
6/// A `ParamParser` decodes query parameters included in a client's `EXECUTE` command given
7/// type information for the expected parameters.
8///
9/// Users should invoke [`iter`](struct.ParamParser.html#method.iter) method to iterate over the
10/// provided parameters.
11pub struct ParamParser<'a> {
12    pub(crate) params: u16,
13    pub(crate) bytes: &'a [u8],
14    pub(crate) long_data: &'a HashMap<u16, Vec<u8>>,
15    pub(crate) bound_types: &'a mut Vec<(myc::constants::ColumnType, bool)>,
16}
17
18impl<'a> ParamParser<'a> {
19    pub(crate) fn new(input: &'a [u8], stmt: &'a mut StatementData) -> Self {
20        ParamParser {
21            params: stmt.params,
22            bytes: input,
23            long_data: &stmt.long_data,
24            bound_types: &mut stmt.bound_types,
25        }
26    }
27}
28
29impl<'a> IntoIterator for ParamParser<'a> {
30    type IntoIter = Params<'a>;
31    type Item = ParamValue<'a>;
32    fn into_iter(self) -> Params<'a> {
33        Params {
34            params: self.params,
35            input: self.bytes,
36            nullmap: None,
37            col: 0,
38            long_data: self.long_data,
39            bound_types: self.bound_types,
40        }
41    }
42}
43
44/// An iterator over parameters provided by a client in an `EXECUTE` command.
45pub struct Params<'a> {
46    params: u16,
47    input: &'a [u8],
48    nullmap: Option<&'a [u8]>,
49    col: u16,
50    long_data: &'a HashMap<u16, Vec<u8>>,
51    bound_types: &'a mut Vec<(myc::constants::ColumnType, bool)>,
52}
53
54/// A single parameter value provided by a client when issuing an `EXECUTE` command.
55pub struct ParamValue<'a> {
56    /// The value provided for this parameter.
57    pub value: Value<'a>,
58    /// The column type assigned to this parameter.
59    pub coltype: myc::constants::ColumnType,
60}
61
62impl<'a> Iterator for Params<'a> {
63    type Item = ParamValue<'a>;
64    fn next(&mut self) -> Option<Self::Item> {
65        if self.nullmap.is_none() {
66            let nullmap_len = (self.params as usize + 7) / 8;
67            let (nullmap, rest) = self.input.split_at(nullmap_len);
68            self.nullmap = Some(nullmap);
69            self.input = rest;
70
71            if !rest.is_empty() && rest[0] != 0x00 {
72                let (typmap, rest) = rest[1..].split_at(2 * self.params as usize);
73                self.bound_types.clear();
74                for i in 0..self.params as usize {
75                    self.bound_types.push((
76                        myc::constants::ColumnType::try_from(typmap[2 * i]).unwrap_or_else(|e| {
77                            panic!("bad column type 0x{:x}: {}", typmap[2 * i], e)
78                        }),
79                        (typmap[2 * i + 1] & 128) != 0,
80                    ));
81                }
82                self.input = rest;
83            }
84        }
85
86        if self.col >= self.params {
87            return None;
88        }
89        let pt = &self.bound_types[self.col as usize];
90
91        // https://web.archive.org/web/20170404144156/https://dev.mysql.com/doc/internals/en/null-bitmap.html
92        // NULL-bitmap-byte = ((field-pos + offset) / 8)
93        // NULL-bitmap-bit  = ((field-pos + offset) % 8)
94        if let Some(nullmap) = self.nullmap {
95            let byte = self.col as usize / 8;
96            if byte >= nullmap.len() {
97                return None;
98            }
99            if (nullmap[byte] & 1u8 << (self.col % 8)) != 0 {
100                self.col += 1;
101                return Some(ParamValue {
102                    value: Value::null(),
103                    coltype: pt.0,
104                });
105            }
106        } else {
107            unreachable!();
108        }
109
110        let v = if let Some(data) = self.long_data.get(&self.col) {
111            Value::bytes(&data[..])
112        } else {
113            Value::parse_from(&mut self.input, pt.0, pt.1).unwrap()
114        };
115        self.col += 1;
116        Some(ParamValue {
117            value: v,
118            coltype: pt.0,
119        })
120    }
121}