Line data Source code
1 : //! Conversions to and from Postgres types.
2 : //!
3 : //! This crate is used by the `tokio-postgres` and `postgres` crates. You normally don't need to depend directly on it
4 : //! unless you want to define your own `ToSql` or `FromSql` definitions.
5 : #![warn(clippy::all, missing_docs)]
6 :
7 : use std::any::type_name;
8 : use std::error::Error;
9 : use std::fmt;
10 : use std::sync::Arc;
11 :
12 : use fallible_iterator::FallibleIterator;
13 : #[doc(inline)]
14 : pub use postgres_protocol2::Oid;
15 : use postgres_protocol2::types;
16 :
17 : use crate::type_gen::{Inner, Other};
18 :
19 : /// Generates a simple implementation of `ToSql::accepts` which accepts the
20 : /// types passed to it.
21 : macro_rules! accepts {
22 : ($($expected:ident),+) => (
23 0 : fn accepts(ty: &$crate::Type) -> bool {
24 0 : matches!(*ty, $($crate::Type::$expected)|+)
25 0 : }
26 : )
27 : }
28 :
29 : // mod pg_lsn;
30 : #[doc(hidden)]
31 : pub mod private;
32 : // mod special;
33 : mod type_gen;
34 :
35 : /// A Postgres type.
36 : #[derive(PartialEq, Eq, Clone, Hash)]
37 : pub struct Type(Inner);
38 :
39 : impl fmt::Debug for Type {
40 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
41 0 : fmt::Debug::fmt(&self.0, fmt)
42 0 : }
43 : }
44 :
45 : impl fmt::Display for Type {
46 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
47 0 : match self.schema() {
48 0 : "public" | "pg_catalog" => {}
49 0 : schema => write!(fmt, "{schema}.")?,
50 : }
51 0 : fmt.write_str(self.name())
52 0 : }
53 : }
54 :
55 : impl Type {
56 : /// Creates a new `Type`.
57 0 : pub fn new(name: String, oid: Oid, kind: Kind, schema: String) -> Type {
58 0 : Type(Inner::Other(Arc::new(Other {
59 0 : name,
60 0 : oid,
61 0 : kind,
62 0 : schema,
63 0 : })))
64 0 : }
65 :
66 : /// Returns the `Type` corresponding to the provided `Oid` if it
67 : /// corresponds to a built-in type.
68 0 : pub fn from_oid(oid: Oid) -> Option<Type> {
69 0 : Inner::from_oid(oid).map(Type)
70 0 : }
71 :
72 : /// Returns the OID of the `Type`.
73 0 : pub fn oid(&self) -> Oid {
74 0 : self.0.oid()
75 0 : }
76 :
77 : /// Returns the kind of this type.
78 76 : pub fn kind(&self) -> &Kind {
79 76 : self.0.kind()
80 76 : }
81 :
82 : /// Returns the schema of this type.
83 0 : pub fn schema(&self) -> &str {
84 0 : match self.0 {
85 0 : Inner::Other(ref u) => &u.schema,
86 0 : _ => "pg_catalog",
87 : }
88 0 : }
89 :
90 : /// Returns the name of this type.
91 0 : pub fn name(&self) -> &str {
92 0 : self.0.name()
93 0 : }
94 : }
95 :
96 : /// Represents the kind of a Postgres type.
97 : #[derive(Debug, Clone, PartialEq, Eq, Hash)]
98 : #[non_exhaustive]
99 : pub enum Kind {
100 : /// A simple type like `VARCHAR` or `INTEGER`.
101 : Simple,
102 : /// An enumerated type.
103 : Enum,
104 : /// A pseudo-type.
105 : Pseudo,
106 : /// An array type along with the type of its elements.
107 : Array(Type),
108 : /// A range type along with the type of its elements.
109 : Range(Oid),
110 : /// A multirange type along with the type of its elements.
111 : Multirange(Type),
112 : /// A domain type along with its underlying type.
113 : Domain(Oid),
114 : /// A composite type.
115 : Composite(Oid),
116 : }
117 :
118 : /// Information about a field of a composite type.
119 : #[derive(Debug, Clone, PartialEq, Eq, Hash)]
120 : pub struct Field {
121 : name: String,
122 : type_: Type,
123 : }
124 :
125 : impl Field {
126 : /// Creates a new `Field`.
127 0 : pub fn new(name: String, type_: Type) -> Field {
128 0 : Field { name, type_ }
129 0 : }
130 :
131 : /// Returns the name of the field.
132 0 : pub fn name(&self) -> &str {
133 0 : &self.name
134 0 : }
135 :
136 : /// Returns the type of the field.
137 0 : pub fn type_(&self) -> &Type {
138 0 : &self.type_
139 0 : }
140 : }
141 :
142 : /// An error indicating that a `NULL` Postgres value was passed to a `FromSql`
143 : /// implementation that does not support `NULL` values.
144 : #[derive(Debug, Clone, Copy)]
145 : pub struct WasNull;
146 :
147 : impl fmt::Display for WasNull {
148 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
149 0 : fmt.write_str("a Postgres value was `NULL`")
150 0 : }
151 : }
152 :
153 : impl Error for WasNull {}
154 :
155 : /// An error indicating that a conversion was attempted between incompatible
156 : /// Rust and Postgres types.
157 : #[derive(Debug)]
158 : pub struct WrongType {
159 : postgres: Type,
160 : rust: &'static str,
161 : }
162 :
163 : impl fmt::Display for WrongType {
164 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
165 0 : write!(
166 0 : fmt,
167 0 : "cannot convert between the Rust type `{}` and the Postgres type `{}`",
168 : self.rust, self.postgres,
169 : )
170 0 : }
171 : }
172 :
173 : impl Error for WrongType {}
174 :
175 : impl WrongType {
176 : /// Creates a new `WrongType` error.
177 0 : pub fn new<T>(ty: Type) -> WrongType {
178 0 : WrongType {
179 0 : postgres: ty,
180 0 : rust: type_name::<T>(),
181 0 : }
182 0 : }
183 : }
184 :
185 : /// An error indicating that a as_text conversion was attempted on a binary
186 : /// result.
187 : #[derive(Debug)]
188 : pub struct WrongFormat {}
189 :
190 : impl Error for WrongFormat {}
191 :
192 : impl fmt::Display for WrongFormat {
193 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
194 0 : write!(
195 0 : fmt,
196 0 : "cannot read column as text while it is in binary format"
197 : )
198 0 : }
199 : }
200 :
201 : /// A trait for types that can be created from a Postgres value.
202 : pub trait FromSql<'a>: Sized {
203 : /// Creates a new value of this type from a buffer of data of the specified
204 : /// Postgres `Type` in its binary format.
205 : ///
206 : /// The caller of this method is responsible for ensuring that this type
207 : /// is compatible with the Postgres `Type`.
208 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>>;
209 :
210 : /// Creates a new value of this type from a `NULL` SQL value.
211 : ///
212 : /// The caller of this method is responsible for ensuring that this type
213 : /// is compatible with the Postgres `Type`.
214 : ///
215 : /// The default implementation returns `Err(Box::new(WasNull))`.
216 : #[allow(unused_variables)]
217 0 : fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
218 0 : Err(Box::new(WasNull))
219 0 : }
220 :
221 : /// A convenience function that delegates to `from_sql` and `from_sql_null` depending on the
222 : /// value of `raw`.
223 0 : fn from_sql_nullable(
224 0 : ty: &Type,
225 0 : raw: Option<&'a [u8]>,
226 0 : ) -> Result<Self, Box<dyn Error + Sync + Send>> {
227 0 : match raw {
228 0 : Some(raw) => Self::from_sql(ty, raw),
229 0 : None => Self::from_sql_null(ty),
230 : }
231 0 : }
232 :
233 : /// Determines if a value of this type can be created from the specified
234 : /// Postgres `Type`.
235 : fn accepts(ty: &Type) -> bool;
236 : }
237 :
238 : /// A trait for types which can be created from a Postgres value without borrowing any data.
239 : ///
240 : /// This is primarily useful for trait bounds on functions.
241 : pub trait FromSqlOwned: for<'a> FromSql<'a> {}
242 :
243 : impl<T> FromSqlOwned for T where T: for<'a> FromSql<'a> {}
244 :
245 : impl<'a, T: FromSql<'a>> FromSql<'a> for Option<T> {
246 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Option<T>, Box<dyn Error + Sync + Send>> {
247 0 : <T as FromSql>::from_sql(ty, raw).map(Some)
248 0 : }
249 :
250 0 : fn from_sql_null(_: &Type) -> Result<Option<T>, Box<dyn Error + Sync + Send>> {
251 0 : Ok(None)
252 0 : }
253 :
254 0 : fn accepts(ty: &Type) -> bool {
255 0 : <T as FromSql>::accepts(ty)
256 0 : }
257 : }
258 :
259 : impl<'a, T: FromSql<'a>> FromSql<'a> for Vec<T> {
260 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Vec<T>, Box<dyn Error + Sync + Send>> {
261 0 : let member_type = match *ty.kind() {
262 0 : Kind::Array(ref member) => member,
263 0 : _ => panic!("expected array type"),
264 : };
265 :
266 0 : let array = types::array_from_sql(raw)?;
267 0 : if array.dimensions().count()? > 1 {
268 0 : return Err("array contains too many dimensions".into());
269 0 : }
270 :
271 0 : array
272 0 : .values()
273 0 : .map(|v| T::from_sql_nullable(member_type, v))
274 0 : .collect()
275 0 : }
276 :
277 0 : fn accepts(ty: &Type) -> bool {
278 0 : match *ty.kind() {
279 0 : Kind::Array(ref inner) => T::accepts(inner),
280 0 : _ => false,
281 : }
282 0 : }
283 : }
284 :
285 : impl<'a> FromSql<'a> for String {
286 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<String, Box<dyn Error + Sync + Send>> {
287 0 : <&str as FromSql>::from_sql(ty, raw).map(ToString::to_string)
288 0 : }
289 :
290 0 : fn accepts(ty: &Type) -> bool {
291 0 : <&str as FromSql>::accepts(ty)
292 0 : }
293 : }
294 :
295 : impl<'a> FromSql<'a> for &'a str {
296 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<&'a str, Box<dyn Error + Sync + Send>> {
297 0 : match *ty {
298 0 : ref ty if ty.name() == "ltree" => types::ltree_from_sql(raw),
299 0 : ref ty if ty.name() == "lquery" => types::lquery_from_sql(raw),
300 0 : ref ty if ty.name() == "ltxtquery" => types::ltxtquery_from_sql(raw),
301 0 : _ => types::text_from_sql(raw),
302 : }
303 0 : }
304 :
305 0 : fn accepts(ty: &Type) -> bool {
306 0 : match *ty {
307 0 : Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::NAME | Type::UNKNOWN => true,
308 0 : ref ty
309 0 : if (ty.name() == "citext"
310 0 : || ty.name() == "ltree"
311 0 : || ty.name() == "lquery"
312 0 : || ty.name() == "ltxtquery") =>
313 : {
314 0 : true
315 : }
316 0 : _ => false,
317 : }
318 0 : }
319 : }
320 :
321 : macro_rules! simple_from {
322 : ($t:ty, $f:ident, $($expected:ident),+) => {
323 : impl<'a> FromSql<'a> for $t {
324 0 : fn from_sql(_: &Type, raw: &'a [u8]) -> Result<$t, Box<dyn Error + Sync + Send>> {
325 0 : types::$f(raw)
326 0 : }
327 :
328 : accepts!($($expected),+);
329 : }
330 : }
331 : }
332 :
333 : simple_from!(i8, char_from_sql, CHAR);
334 : simple_from!(u32, oid_from_sql, OID);
335 :
336 : /// An enum representing the nullability of a Postgres value.
337 : pub enum IsNull {
338 : /// The value is NULL.
339 : Yes,
340 : /// The value is not NULL.
341 : No,
342 : }
343 :
344 : /// Supported Postgres message format types
345 : ///
346 : /// Using Text format in a message assumes a Postgres `SERVER_ENCODING` of `UTF8`
347 : #[derive(Clone, Copy, Debug, PartialEq)]
348 : pub enum Format {
349 : /// Text format (UTF-8)
350 : Text,
351 : /// Compact, typed binary format
352 : Binary,
353 : }
|