1use std::{
2 collections::{btree_map, BTreeMap},
3 mem,
4 ops::{Deref, DerefMut},
5 str,
6};
7
8use crate::{
9 decode::Decode,
10 encode::{Encode, IsNull},
11 error::BoxDynError,
12 types::Type,
13 PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, Postgres,
14};
15use serde::{Deserialize, Serialize};
16use sqlx_core::bytes::Buf;
17
18#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
92pub struct PgHstore(pub BTreeMap<String, Option<String>>);
93
94impl Deref for PgHstore {
95 type Target = BTreeMap<String, Option<String>>;
96
97 fn deref(&self) -> &Self::Target {
98 &self.0
99 }
100}
101
102impl DerefMut for PgHstore {
103 fn deref_mut(&mut self) -> &mut Self::Target {
104 &mut self.0
105 }
106}
107
108impl FromIterator<(String, String)> for PgHstore {
109 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
110 iter.into_iter().map(|(k, v)| (k, Some(v))).collect()
111 }
112}
113
114impl FromIterator<(String, Option<String>)> for PgHstore {
115 fn from_iter<T: IntoIterator<Item = (String, Option<String>)>>(iter: T) -> Self {
116 let mut result = Self::default();
117
118 for (key, value) in iter {
119 result.0.insert(key, value);
120 }
121
122 result
123 }
124}
125
126impl IntoIterator for PgHstore {
127 type Item = (String, Option<String>);
128 type IntoIter = btree_map::IntoIter<String, Option<String>>;
129
130 fn into_iter(self) -> Self::IntoIter {
131 self.0.into_iter()
132 }
133}
134
135impl Type<Postgres> for PgHstore {
136 fn type_info() -> PgTypeInfo {
137 PgTypeInfo::with_name("hstore")
138 }
139}
140
141impl PgHasArrayType for PgHstore {
142 fn array_type_info() -> PgTypeInfo {
143 PgTypeInfo::array_of("hstore")
144 }
145}
146
147impl<'r> Decode<'r, Postgres> for PgHstore {
148 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
149 let mut buf = <&[u8] as Decode<Postgres>>::decode(value)?;
150 let len = read_length(&mut buf)?;
151
152 let len =
153 usize::try_from(len).map_err(|_| format!("PgHstore: length out of range: {len}"))?;
154
155 let mut result = Self::default();
156
157 for i in 0..len {
158 let key = read_string(&mut buf)
159 .map_err(|e| format!("PgHstore: error reading {i}th key: {e}"))?
160 .ok_or_else(|| format!("PgHstore: expected {i}th key, got nothing"))?;
161
162 let value = read_string(&mut buf)
163 .map_err(|e| format!("PgHstore: error reading value for key {key:?}: {e}"))?;
164
165 result.insert(key, value);
166 }
167
168 if !buf.is_empty() {
169 tracing::warn!("{} unread bytes at the end of HSTORE value", buf.len());
170 }
171
172 Ok(result)
173 }
174}
175
176impl Encode<'_, Postgres> for PgHstore {
177 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
178 buf.extend_from_slice(&i32::to_be_bytes(
179 self.0
180 .len()
181 .try_into()
182 .map_err(|_| format!("PgHstore length out of range: {}", self.0.len()))?,
183 ));
184
185 for (i, (key, val)) in self.0.iter().enumerate() {
186 let key_bytes = key.as_bytes();
187
188 let key_len = i32::try_from(key_bytes.len()).map_err(|_| {
189 format!(
191 "PgHstore: length of {i}th key out of range: {} bytes",
192 key_bytes.len()
193 )
194 })?;
195
196 buf.extend_from_slice(&i32::to_be_bytes(key_len));
197 buf.extend_from_slice(key_bytes);
198
199 match val {
200 Some(val) => {
201 let val_bytes = val.as_bytes();
202
203 let val_len = i32::try_from(val_bytes.len()).map_err(|_| {
204 format!(
205 "PgHstore: value length for key {key:?} out of range: {} bytes",
206 val_bytes.len()
207 )
208 })?;
209 buf.extend_from_slice(&i32::to_be_bytes(val_len));
210 buf.extend_from_slice(val_bytes);
211 }
212 None => {
213 buf.extend_from_slice(&i32::to_be_bytes(-1));
214 }
215 }
216 }
217
218 Ok(IsNull::No)
219 }
220}
221
222fn read_length(buf: &mut &[u8]) -> Result<i32, String> {
223 if buf.len() < mem::size_of::<i32>() {
224 return Err(format!(
225 "expected {} bytes, got {}",
226 mem::size_of::<i32>(),
227 buf.len()
228 ));
229 }
230
231 Ok(buf.get_i32())
232}
233
234fn read_string(buf: &mut &[u8]) -> Result<Option<String>, String> {
235 let len = read_length(buf)?;
236
237 match len {
238 -1 => Ok(None),
239 len => {
240 let len =
241 usize::try_from(len).map_err(|_| format!("string length out of range: {len}"))?;
242
243 if buf.len() < len {
244 return Err(format!("expected {len} bytes, got {}", buf.len()));
245 }
246
247 let (val, rest) = buf.split_at(len);
248 *buf = rest;
249
250 Ok(Some(
251 str::from_utf8(val).map_err(|e| e.to_string())?.to_string(),
252 ))
253 }
254 }
255}
256
257#[cfg(test)]
258mod test {
259 use super::*;
260 use crate::PgValueFormat;
261
262 const EMPTY: &str = "00000000";
263
264 const NAME_SURNAME_AGE: &str =
265 "0000000300000003616765ffffffff000000046e616d65000000044a6f686e000000077375726e616d6500000003446f65";
266
267 #[test]
268 fn hstore_deserialize_ok() {
269 let empty = hex::decode(EMPTY).unwrap();
270 let name_surname_age = hex::decode(NAME_SURNAME_AGE).unwrap();
271
272 let empty = PgValueRef {
273 value: Some(empty.as_slice()),
274 row: None,
275 type_info: PgTypeInfo::with_name("hstore"),
276 format: PgValueFormat::Binary,
277 };
278
279 let name_surname = PgValueRef {
280 value: Some(name_surname_age.as_slice()),
281 row: None,
282 type_info: PgTypeInfo::with_name("hstore"),
283 format: PgValueFormat::Binary,
284 };
285
286 let res_empty = PgHstore::decode(empty).unwrap();
287 let res_name_surname = PgHstore::decode(name_surname).unwrap();
288
289 assert!(res_empty.is_empty());
290 assert_eq!(res_name_surname["name"], Some("John".to_string()));
291 assert_eq!(res_name_surname["surname"], Some("Doe".to_string()));
292 assert_eq!(res_name_surname["age"], None);
293 }
294
295 #[test]
296 #[should_panic(expected = "PgHstore: length out of range: -5")]
297 fn hstore_deserialize_buffer_length_error() {
298 let buf = PgValueRef {
299 value: Some(&[255, 255, 255, 251]),
300 row: None,
301 type_info: PgTypeInfo::with_name("hstore"),
302 format: PgValueFormat::Binary,
303 };
304
305 PgHstore::decode(buf).unwrap();
306 }
307
308 #[test]
309 fn hstore_serialize_ok() {
310 let mut buff = PgArgumentBuffer::default();
311 let _ = PgHstore::from_iter::<[(String, String); 0]>([])
312 .encode_by_ref(&mut buff)
313 .unwrap();
314
315 assert_eq!(hex::encode(buff.as_slice()), EMPTY);
316
317 buff.clear();
318
319 let _ = PgHstore::from_iter([
320 ("name".to_string(), Some("John".to_string())),
321 ("surname".to_string(), Some("Doe".to_string())),
322 ("age".to_string(), None),
323 ])
324 .encode_by_ref(&mut buff)
325 .unwrap();
326
327 assert_eq!(hex::encode(buff.as_slice()), NAME_SURNAME_AGE);
328 }
329}