rbdc_pg/types/
array.rs

1use crate::arguments::PgArgumentBuffer;
2use crate::type_info::{PgType, PgTypeInfo, PgTypeKind};
3use crate::types::decode::Decode;
4use crate::types::encode::{Encode, IsNull};
5use crate::types::{Oid, TypeInfo};
6use crate::value::{PgValue, PgValueFormat};
7use bytes::Buf;
8use rbdc::Error;
9use rbs::Value;
10use std::borrow::Cow;
11
12impl<T: Decode + TypeInfo> Decode for Vec<T> {
13    fn decode(value: PgValue) -> Result<Self, Error> {
14        let format = value.format();
15        match format {
16            PgValueFormat::Binary => {
17                // https://github.com/postgres/postgres/blob/a995b371ae29de2d38c4b7881cf414b1560e9746/src/backend/utils/adt/arrayfuncs.c#L1548
18
19                let mut buf = value.as_bytes()?;
20
21                // number of dimensions in the array
22                let ndim = buf.get_i32();
23
24                if ndim == 0 {
25                    // zero dimensions is an empty array
26                    return Ok(Vec::new());
27                }
28
29                if ndim != 1 {
30                    return Err(format!("encountered an array of {} dimensions; only one-dimensional arrays are supported", ndim).into());
31                }
32
33                // appears to have been used in the past to communicate potential NULLS
34                // but reading source code back through our supported postgres versions (9.5+)
35                // this is never used for anything
36                let _flags = buf.get_i32();
37
38                // the OID of the element
39                let element_type_oid = Oid(buf.get_u32());
40                let element_type_info: PgTypeInfo = PgTypeInfo::try_from_oid(element_type_oid)
41                    .or_else(|| value.type_info.try_array_element().map(Cow::into_owned))
42                    .ok_or_else(|| {
43                        Error::from(format!(
44                            "failed to resolve array element type for oid {}",
45                            element_type_oid.0
46                        ))
47                    })?;
48
49                // length of the array axis
50                let len = buf.get_i32();
51
52                // the lower bound, we only support arrays starting from "1"
53                let lower = buf.get_i32();
54
55                if lower != 1 {
56                    return Err(format!("encountered an array with a lower bound of {} in the first dimension; only arrays starting at one are supported", lower).into());
57                }
58
59                let mut elements = Vec::with_capacity(len as usize);
60
61                for _ in 0..len {
62                    elements.push(T::decode(PgValue::get(
63                        &mut buf,
64                        format,
65                        element_type_info.clone(),
66                        value.timezone_sec,
67                    ))?)
68                }
69
70                Ok(elements)
71            }
72
73            PgValueFormat::Text => {
74                // no type is provided from the database for the element
75                let mut element_type_info = PgTypeInfo::UNKNOWN;
76                match value.type_info.kind() {
77                    PgTypeKind::Simple => {}
78                    PgTypeKind::Pseudo => {}
79                    PgTypeKind::Domain(_) => {}
80                    PgTypeKind::Composite(_) => {}
81                    PgTypeKind::Array(item) => {
82                        element_type_info = item.clone();
83                    }
84                    PgTypeKind::Enum(_) => {}
85                    PgTypeKind::Range(_) => {}
86                }
87
88                let s = value.as_str()?;
89
90                // https://github.com/postgres/postgres/blob/a995b371ae29de2d38c4b7881cf414b1560e9746/src/backend/utils/adt/arrayfuncs.c#L718
91
92                // trim the wrapping braces
93                let s = &s[1..(s.len() - 1)];
94
95                if s.is_empty() {
96                    // short-circuit empty arrays up here
97                    return Ok(Vec::new());
98                }
99
100                // NOTE: Nearly *all* types use ',' as the sequence delimiter. Yes, there is one
101                //       that does not. The BOX (not PostGIS) type uses ';' as a delimiter.
102                //       We detect the element type and use the appropriate delimiter.
103
104                let delimiter = if element_type_info.0 == crate::type_info::PgType::Box {
105                    ';'
106                } else {
107                    ','
108                };
109                let mut done = false;
110                let mut in_quotes = false;
111                let mut in_escape = false;
112                // Save timezone_sec from the outer PgValue before we shadow it
113                let timezone_sec = value.timezone_sec;
114                let mut value = String::with_capacity(10);
115                let mut chars = s.chars();
116                let mut elements = Vec::with_capacity(4);
117
118                while !done {
119                    loop {
120                        match chars.next() {
121                            Some(ch) => match ch {
122                                _ if in_escape => {
123                                    value.push(ch);
124                                    in_escape = false;
125                                }
126
127                                '"' => {
128                                    in_quotes = !in_quotes;
129                                }
130
131                                '\\' => {
132                                    in_escape = true;
133                                }
134
135                                _ if ch == delimiter && !in_quotes => {
136                                    break;
137                                }
138
139                                _ => {
140                                    value.push(ch);
141                                }
142                            },
143
144                            None => {
145                                done = true;
146                                break;
147                            }
148                        }
149                    }
150
151                    let value_opt = if value == "NULL" {
152                        None
153                    } else {
154                        Some(value.as_bytes().to_vec())
155                    };
156                    elements.push(T::decode(PgValue {
157                        value: value_opt,
158                        type_info: element_type_info.clone(),
159                        format,
160                        timezone_sec,
161                    })?);
162
163                    value.clear();
164                }
165
166                Ok(elements)
167            }
168        }
169    }
170}
171
172fn element_type_info<T: TypeInfo>(arg: &Vec<T>) -> PgTypeInfo {
173    if arg.len() == 0 {
174        PgTypeInfo::UNKNOWN
175    } else {
176        arg[0].type_info()
177    }
178}
179
180impl Encode for Vec<Value> {
181    fn encode(self, buf: &mut PgArgumentBuffer) -> Result<IsNull, Error> {
182        let type_info = element_type_info(&self);
183        buf.extend(&1_i32.to_be_bytes()); // number of dimensions
184        buf.extend(&0_i32.to_be_bytes()); // flags
185                                          // element type
186        match type_info.0 {
187            PgType::DeclareWithName(name) => buf.patch_type_by_name(&name),
188            ty => {
189                buf.extend(&ty.oid().0.to_be_bytes());
190            }
191        }
192        buf.extend(&(self.len() as i32).to_be_bytes()); // len
193        buf.extend(&1_i32.to_be_bytes()); // lower bound
194        for element in self {
195            buf.encode(element)?;
196        }
197        Ok(IsNull::No)
198    }
199}