opensrv_mysql/
params.rs

1// Copyright 2021 Datafuse Labs.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashMap;
16
17use crate::myc;
18use crate::{StatementData, Value};
19
20/// A `ParamParser` decodes query parameters included in a client's `EXECUTE` command given
21/// type information for the expected parameters.
22///
23/// Users should invoke [`iter`](struct.ParamParser.html#method.iter) method to iterate over the
24/// provided parameters.
25pub struct ParamParser<'a> {
26    pub(crate) params: u16,
27    pub(crate) bytes: &'a [u8],
28    pub(crate) long_data: &'a HashMap<u16, Vec<u8>>,
29    pub(crate) bound_types: &'a mut Vec<(myc::constants::ColumnType, bool)>,
30}
31
32impl<'a> ParamParser<'a> {
33    pub(crate) fn new(input: &'a [u8], stmt: &'a mut StatementData) -> Self {
34        ParamParser {
35            params: stmt.params,
36            bytes: input,
37            long_data: &stmt.long_data,
38            bound_types: &mut stmt.bound_types,
39        }
40    }
41}
42
43impl<'a> IntoIterator for ParamParser<'a> {
44    type IntoIter = Params<'a>;
45    type Item = ParamValue<'a>;
46    fn into_iter(self) -> Params<'a> {
47        Params {
48            params: self.params,
49            input: self.bytes,
50            nullmap: None,
51            col: 0,
52            long_data: self.long_data,
53            bound_types: self.bound_types,
54        }
55    }
56}
57
58/// An iterator over parameters provided by a client in an `EXECUTE` command.
59pub struct Params<'a> {
60    params: u16,
61    input: &'a [u8],
62    nullmap: Option<&'a [u8]>,
63    col: u16,
64    long_data: &'a HashMap<u16, Vec<u8>>,
65    bound_types: &'a mut Vec<(myc::constants::ColumnType, bool)>,
66}
67
68/// A single parameter value provided by a client when issuing an `EXECUTE` command.
69pub struct ParamValue<'a> {
70    /// The value provided for this parameter.
71    pub value: Value<'a>,
72    /// The column type assigned to this parameter.
73    pub coltype: myc::constants::ColumnType,
74}
75
76impl<'a> Iterator for Params<'a> {
77    type Item = ParamValue<'a>;
78    fn next(&mut self) -> Option<Self::Item> {
79        if self.nullmap.is_none() {
80            let nullmap_len = (self.params as usize + 7) / 8;
81            let (nullmap, rest) = self.input.split_at(nullmap_len);
82            self.nullmap = Some(nullmap);
83            self.input = rest;
84
85            if !rest.is_empty() && rest[0] != 0x00 {
86                let (typmap, rest) = rest[1..].split_at(2 * self.params as usize);
87                self.bound_types.clear();
88                for i in 0..self.params as usize {
89                    self.bound_types.push((
90                        myc::constants::ColumnType::try_from(typmap[2 * i]).unwrap_or_else(|e| {
91                            panic!("bad column type 0x{:x}: {}", typmap[2 * i], e)
92                        }),
93                        (typmap[2 * i + 1] & 128) != 0,
94                    ));
95                }
96                self.input = rest;
97            }
98        }
99
100        if self.col >= self.params {
101            return None;
102        }
103        let pt = &self.bound_types[self.col as usize];
104
105        // https://web.archive.org/web/20170404144156/https://dev.mysql.com/doc/internals/en/null-bitmap.html
106        // NULL-bitmap-byte = ((field-pos + offset) / 8)
107        // NULL-bitmap-bit  = ((field-pos + offset) % 8)
108        if let Some(nullmap) = self.nullmap {
109            let byte = self.col as usize / 8;
110            if byte >= nullmap.len() {
111                return None;
112            }
113            if (nullmap[byte] & 1u8 << (self.col % 8)) != 0 {
114                self.col += 1;
115                return Some(ParamValue {
116                    value: Value::null(),
117                    coltype: pt.0,
118                });
119            }
120        } else {
121            unreachable!();
122        }
123
124        let v = if let Some(data) = self.long_data.get(&self.col) {
125            Value::bytes(&data[..])
126        } else {
127            Value::parse_from(&mut self.input, pt.0, pt.1).unwrap()
128        };
129        self.col += 1;
130        Some(ParamValue {
131            value: v,
132            coltype: pt.0,
133        })
134    }
135}