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}