pgdog_plugin/
parameters.rs

1//! Prepared statement parameters.
2//!
3//! # Example
4//!
5//! ```
6//! use pgdog_plugin::prelude::*;
7//!
8//! let params = Parameters::default();
9//! assert_eq!(ParameterFormat::Text, params.parameter_format(0));
10//!
11//! if let Some(param) = params.get(0) {
12//!     let value = param.decode(params.parameter_format(0));
13//! }
14//! ```
15use std::{ops::Deref, os::raw::c_void, ptr::null, str::from_utf8};
16
17use crate::PdParameters;
18
19/// Parameter format code. 0 is text encoding (usually UTF-8), 1 is binary encoding,
20/// specific to the parameter data type.
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[repr(C)]
23pub enum ParameterFormat {
24    /// Text encoding.
25    Text = 0,
26
27    /// Binary encoding.
28    Binary = 1,
29}
30
31/// Wrapper around a decoded parameter.
32///
33/// # Example
34///
35/// ```
36/// # use pgdog_plugin::prelude::*;
37/// let parameter = ParameterValue::Text("test");
38/// match parameter {
39///     ParameterValue::Text(text) => assert_eq!(text, "test"),
40///     ParameterValue::Binary(binary) => println!("{:?}", binary),
41/// }
42/// ```
43#[derive(Debug, PartialEq, Eq)]
44pub enum ParameterValue<'a> {
45    /// Parameter is encoded using text (UTF-8).
46    Text(&'a str),
47    /// Parameter is encoded using binary encoding.
48    Binary(&'a [u8]),
49}
50
51/// Prepared statement bound parameter.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Parameter {
54    /// Parameter data length.
55    pub len: i32,
56    /// Parameter data.
57    ///
58    /// Use [`Self::decode`] to read this value.
59    pub data: Vec<u8>,
60}
61
62impl Parameter {
63    /// Decode parameter given the provided format. If the parameter is encoded using text encoding (default),
64    /// a UTF-8 string is returned. If encoded using binary encoding, a slice of bytes.
65    ///
66    /// If the parameter is `NULL` or the encoding doesn't match the data, `None` is returned.
67    ///
68    /// # Example
69    ///
70    /// ```
71    /// use pgdog_plugin::prelude::*;
72    ///
73    /// let parameter = Parameter {
74    ///     len: -1,
75    ///     data: vec![],
76    /// };
77    /// assert!(parameter.decode(ParameterFormat::Text).is_none());
78    ///
79    /// let parameter = Parameter {
80    ///     len: 5,
81    ///     data: "hello".as_bytes().to_vec(),
82    /// };
83    /// assert_eq!(parameter.decode(ParameterFormat::Text), Some(ParameterValue::Text("hello")));
84    /// ```
85    ///
86    pub fn decode(&self, format: ParameterFormat) -> Option<ParameterValue<'_>> {
87        if self.len == -1 {
88            return None;
89        }
90        match format {
91            ParameterFormat::Binary => Some(ParameterValue::Binary(&self.data[..])),
92            ParameterFormat::Text => from_utf8(&self.data).ok().map(ParameterValue::Text),
93        }
94    }
95
96    /// Returns true if the parameter is `NULL`.
97    pub fn null(&self) -> bool {
98        self.len == -1
99    }
100}
101
102/// Prepared statement parameters.
103#[derive(Debug, PartialEq, Eq)]
104pub struct Parameters {
105    params: Option<Vec<Parameter>>,
106    format_codes: Option<Vec<ParameterFormat>>,
107    borrowed: bool,
108}
109
110impl Default for Parameters {
111    fn default() -> Self {
112        Self {
113            params: Some(vec![]),
114            format_codes: Some(vec![]),
115            borrowed: false,
116        }
117    }
118}
119
120impl Clone for Parameters {
121    fn clone(&self) -> Self {
122        Self {
123            params: self.params.clone(),
124            format_codes: self.format_codes.clone(),
125            borrowed: false,
126        }
127    }
128}
129
130impl Parameters {
131    /// Returns a list of parameter format codes.
132    pub fn format_codes(&self) -> &[ParameterFormat] {
133        self.format_codes.as_ref().unwrap()
134    }
135
136    /// Get a parameter format code indicating the encoding used.
137    pub fn parameter_format(&self, param: usize) -> ParameterFormat {
138        match self.format_codes().len() {
139            0 => ParameterFormat::Text,
140            1 => self.format_codes()[0].clone(),
141            _ => self
142                .format_codes()
143                .get(param)
144                .unwrap_or(&ParameterFormat::Text)
145                .clone(),
146        }
147    }
148}
149
150impl Deref for Parameters {
151    type Target = Vec<Parameter>;
152
153    fn deref(&self) -> &Self::Target {
154        self.params.as_ref().unwrap()
155    }
156}
157
158impl From<PdParameters> for Parameters {
159    fn from(value: PdParameters) -> Self {
160        if value.format_codes.is_null() || value.params.is_null() {
161            return Self {
162                params: Some(vec![]),
163                format_codes: Some(vec![]),
164                borrowed: false,
165            };
166        }
167
168        // SAFETY: The parameters have the same data type, size and alignment because it's created
169        // by `Vec::new`.
170        //
171        // This is enforced by the Rust compiler and plugins are required to use the same version
172        // as PgDog.
173        //
174        // Why not use a slice instead? This vec (and others in this crate) typically contain
175        // other heap-allocated data types, like other vecs
176        //
177        // We don't implement `DerefMut`, so this vec is read-only. The `*mut Parameter`
178        // cast doesn't do anything.
179        //
180        // We use `std::mem::forget` below so this vec doesn't actually own this
181        // data.
182        //
183        // Cloning the vec is safe: the `Clone::clone` method uses `len` only and
184        // creates a new vec with that as its capacity. It then iterates over each
185        // element and calls its `Clone::clone` function.
186        //
187        // Since this data is immutable and we are not actually taking ownership, this
188        // call is safe.
189        let params = unsafe {
190            Vec::from_raw_parts(
191                value.params as *mut Parameter,
192                value.num_params as usize,
193                value.num_params as usize,
194            )
195        };
196
197        // SAFETY: Same note as above.
198        let format_codes = unsafe {
199            Vec::from_raw_parts(
200                value.format_codes as *mut ParameterFormat,
201                value.num_format_codes as usize,
202                value.num_format_codes as usize,
203            )
204        };
205
206        Self {
207            params: Some(params),
208            format_codes: Some(format_codes),
209            borrowed: true,
210        }
211    }
212}
213
214impl Drop for Parameters {
215    fn drop(&mut self) {
216        if self.borrowed {
217            std::mem::forget(self.params.take());
218            std::mem::forget(self.format_codes.take());
219        }
220    }
221}
222
223impl Default for PdParameters {
224    fn default() -> Self {
225        Self {
226            num_params: 0,
227            params: null::<c_void>() as *mut c_void,
228            num_format_codes: 0,
229            format_codes: null::<c_void>() as *mut c_void,
230        }
231    }
232}
233
234#[cfg(test)]
235mod test {
236    use super::*;
237
238    #[test]
239    fn test_empty_params() {
240        let params = PdParameters::default();
241        let params: Parameters = params.into();
242
243        println!("{:?}", params);
244    }
245}