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 : #![doc(html_root_url = "https://docs.rs/postgres-types/0.2")]
6 : #![warn(clippy::all, rust_2018_idioms, missing_docs)]
7 :
8 : use fallible_iterator::FallibleIterator;
9 : use postgres_protocol2::types;
10 : use std::any::type_name;
11 : use std::error::Error;
12 : use std::fmt;
13 : use std::sync::Arc;
14 :
15 : use crate::type_gen::{Inner, Other};
16 :
17 : #[doc(inline)]
18 : pub use postgres_protocol2::Oid;
19 :
20 : use bytes::BytesMut;
21 :
22 : /// Generates a simple implementation of `ToSql::accepts` which accepts the
23 : /// types passed to it.
24 : macro_rules! accepts {
25 : ($($expected:ident),+) => (
26 0 : fn accepts(ty: &$crate::Type) -> bool {
27 0 : matches!(*ty, $($crate::Type::$expected)|+)
28 0 : }
29 : )
30 : }
31 :
32 : /// Generates an implementation of `ToSql::to_sql_checked`.
33 : ///
34 : /// All `ToSql` implementations should use this macro.
35 : macro_rules! to_sql_checked {
36 : () => {
37 0 : fn to_sql_checked(
38 0 : &self,
39 0 : ty: &$crate::Type,
40 0 : out: &mut $crate::private::BytesMut,
41 0 : ) -> ::std::result::Result<
42 0 : $crate::IsNull,
43 0 : Box<dyn ::std::error::Error + ::std::marker::Sync + ::std::marker::Send>,
44 0 : > {
45 0 : $crate::__to_sql_checked(self, ty, out)
46 0 : }
47 : };
48 : }
49 :
50 : // WARNING: this function is not considered part of this crate's public API.
51 : // It is subject to change at any time.
52 : #[doc(hidden)]
53 0 : pub fn __to_sql_checked<T>(
54 0 : v: &T,
55 0 : ty: &Type,
56 0 : out: &mut BytesMut,
57 0 : ) -> Result<IsNull, Box<dyn Error + Sync + Send>>
58 0 : where
59 0 : T: ToSql,
60 0 : {
61 0 : if !T::accepts(ty) {
62 0 : return Err(Box::new(WrongType::new::<T>(ty.clone())));
63 0 : }
64 0 : v.to_sql(ty, out)
65 0 : }
66 :
67 : // mod pg_lsn;
68 : #[doc(hidden)]
69 : pub mod private;
70 : // mod special;
71 : mod type_gen;
72 :
73 : /// A Postgres type.
74 : #[derive(PartialEq, Eq, Clone, Hash)]
75 : pub struct Type(Inner);
76 :
77 : impl fmt::Debug for Type {
78 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
79 0 : fmt::Debug::fmt(&self.0, fmt)
80 0 : }
81 : }
82 :
83 : impl fmt::Display for Type {
84 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
85 0 : match self.schema() {
86 0 : "public" | "pg_catalog" => {}
87 0 : schema => write!(fmt, "{}.", schema)?,
88 : }
89 0 : fmt.write_str(self.name())
90 0 : }
91 : }
92 :
93 : impl Type {
94 : /// Creates a new `Type`.
95 0 : pub fn new(name: String, oid: Oid, kind: Kind, schema: String) -> Type {
96 0 : Type(Inner::Other(Arc::new(Other {
97 0 : name,
98 0 : oid,
99 0 : kind,
100 0 : schema,
101 0 : })))
102 0 : }
103 :
104 : /// Returns the `Type` corresponding to the provided `Oid` if it
105 : /// corresponds to a built-in type.
106 0 : pub fn from_oid(oid: Oid) -> Option<Type> {
107 0 : Inner::from_oid(oid).map(Type)
108 0 : }
109 :
110 : /// Returns the OID of the `Type`.
111 0 : pub fn oid(&self) -> Oid {
112 0 : self.0.oid()
113 0 : }
114 :
115 : /// Returns the kind of this type.
116 76 : pub fn kind(&self) -> &Kind {
117 76 : self.0.kind()
118 76 : }
119 :
120 : /// Returns the schema of this type.
121 0 : pub fn schema(&self) -> &str {
122 0 : match self.0 {
123 0 : Inner::Other(ref u) => &u.schema,
124 0 : _ => "pg_catalog",
125 : }
126 0 : }
127 :
128 : /// Returns the name of this type.
129 0 : pub fn name(&self) -> &str {
130 0 : self.0.name()
131 0 : }
132 : }
133 :
134 : /// Represents the kind of a Postgres type.
135 : #[derive(Debug, Clone, PartialEq, Eq, Hash)]
136 : #[non_exhaustive]
137 : pub enum Kind {
138 : /// A simple type like `VARCHAR` or `INTEGER`.
139 : Simple,
140 : /// An enumerated type along with its variants.
141 : Enum(Vec<String>),
142 : /// A pseudo-type.
143 : Pseudo,
144 : /// An array type along with the type of its elements.
145 : Array(Type),
146 : /// A range type along with the type of its elements.
147 : Range(Type),
148 : /// A multirange type along with the type of its elements.
149 : Multirange(Type),
150 : /// A domain type along with its underlying type.
151 : Domain(Type),
152 : /// A composite type along with information about its fields.
153 : Composite(Vec<Field>),
154 : }
155 :
156 : /// Information about a field of a composite type.
157 : #[derive(Debug, Clone, PartialEq, Eq, Hash)]
158 : pub struct Field {
159 : name: String,
160 : type_: Type,
161 : }
162 :
163 : impl Field {
164 : /// Creates a new `Field`.
165 0 : pub fn new(name: String, type_: Type) -> Field {
166 0 : Field { name, type_ }
167 0 : }
168 :
169 : /// Returns the name of the field.
170 0 : pub fn name(&self) -> &str {
171 0 : &self.name
172 0 : }
173 :
174 : /// Returns the type of the field.
175 0 : pub fn type_(&self) -> &Type {
176 0 : &self.type_
177 0 : }
178 : }
179 :
180 : /// An error indicating that a `NULL` Postgres value was passed to a `FromSql`
181 : /// implementation that does not support `NULL` values.
182 : #[derive(Debug, Clone, Copy)]
183 : pub struct WasNull;
184 :
185 : impl fmt::Display for WasNull {
186 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
187 0 : fmt.write_str("a Postgres value was `NULL`")
188 0 : }
189 : }
190 :
191 : impl Error for WasNull {}
192 :
193 : /// An error indicating that a conversion was attempted between incompatible
194 : /// Rust and Postgres types.
195 : #[derive(Debug)]
196 : pub struct WrongType {
197 : postgres: Type,
198 : rust: &'static str,
199 : }
200 :
201 : impl fmt::Display for WrongType {
202 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
203 0 : write!(
204 0 : fmt,
205 0 : "cannot convert between the Rust type `{}` and the Postgres type `{}`",
206 0 : self.rust, self.postgres,
207 0 : )
208 0 : }
209 : }
210 :
211 : impl Error for WrongType {}
212 :
213 : impl WrongType {
214 : /// Creates a new `WrongType` error.
215 0 : pub fn new<T>(ty: Type) -> WrongType {
216 0 : WrongType {
217 0 : postgres: ty,
218 0 : rust: type_name::<T>(),
219 0 : }
220 0 : }
221 : }
222 :
223 : /// An error indicating that a as_text conversion was attempted on a binary
224 : /// result.
225 : #[derive(Debug)]
226 : pub struct WrongFormat {}
227 :
228 : impl Error for WrongFormat {}
229 :
230 : impl fmt::Display for WrongFormat {
231 0 : fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
232 0 : write!(
233 0 : fmt,
234 0 : "cannot read column as text while it is in binary format"
235 0 : )
236 0 : }
237 : }
238 :
239 : /// A trait for types that can be created from a Postgres value.
240 : pub trait FromSql<'a>: Sized {
241 : /// Creates a new value of this type from a buffer of data of the specified
242 : /// Postgres `Type` in its binary format.
243 : ///
244 : /// The caller of this method is responsible for ensuring that this type
245 : /// is compatible with the Postgres `Type`.
246 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>>;
247 :
248 : /// Creates a new value of this type from a `NULL` SQL value.
249 : ///
250 : /// The caller of this method is responsible for ensuring that this type
251 : /// is compatible with the Postgres `Type`.
252 : ///
253 : /// The default implementation returns `Err(Box::new(WasNull))`.
254 : #[allow(unused_variables)]
255 0 : fn from_sql_null(ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
256 0 : Err(Box::new(WasNull))
257 0 : }
258 :
259 : /// A convenience function that delegates to `from_sql` and `from_sql_null` depending on the
260 : /// value of `raw`.
261 0 : fn from_sql_nullable(
262 0 : ty: &Type,
263 0 : raw: Option<&'a [u8]>,
264 0 : ) -> Result<Self, Box<dyn Error + Sync + Send>> {
265 0 : match raw {
266 0 : Some(raw) => Self::from_sql(ty, raw),
267 0 : None => Self::from_sql_null(ty),
268 : }
269 0 : }
270 :
271 : /// Determines if a value of this type can be created from the specified
272 : /// Postgres `Type`.
273 : fn accepts(ty: &Type) -> bool;
274 : }
275 :
276 : /// A trait for types which can be created from a Postgres value without borrowing any data.
277 : ///
278 : /// This is primarily useful for trait bounds on functions.
279 : pub trait FromSqlOwned: for<'a> FromSql<'a> {}
280 :
281 : impl<T> FromSqlOwned for T where T: for<'a> FromSql<'a> {}
282 :
283 : impl<'a, T: FromSql<'a>> FromSql<'a> for Option<T> {
284 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Option<T>, Box<dyn Error + Sync + Send>> {
285 0 : <T as FromSql>::from_sql(ty, raw).map(Some)
286 0 : }
287 :
288 0 : fn from_sql_null(_: &Type) -> Result<Option<T>, Box<dyn Error + Sync + Send>> {
289 0 : Ok(None)
290 0 : }
291 :
292 0 : fn accepts(ty: &Type) -> bool {
293 0 : <T as FromSql>::accepts(ty)
294 0 : }
295 : }
296 :
297 : impl<'a, T: FromSql<'a>> FromSql<'a> for Vec<T> {
298 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Vec<T>, Box<dyn Error + Sync + Send>> {
299 0 : let member_type = match *ty.kind() {
300 0 : Kind::Array(ref member) => member,
301 0 : _ => panic!("expected array type"),
302 : };
303 :
304 0 : let array = types::array_from_sql(raw)?;
305 0 : if array.dimensions().count()? > 1 {
306 0 : return Err("array contains too many dimensions".into());
307 0 : }
308 0 :
309 0 : array
310 0 : .values()
311 0 : .map(|v| T::from_sql_nullable(member_type, v))
312 0 : .collect()
313 0 : }
314 :
315 0 : fn accepts(ty: &Type) -> bool {
316 0 : match *ty.kind() {
317 0 : Kind::Array(ref inner) => T::accepts(inner),
318 0 : _ => false,
319 : }
320 0 : }
321 : }
322 :
323 : impl<'a> FromSql<'a> for String {
324 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<String, Box<dyn Error + Sync + Send>> {
325 0 : <&str as FromSql>::from_sql(ty, raw).map(ToString::to_string)
326 0 : }
327 :
328 0 : fn accepts(ty: &Type) -> bool {
329 0 : <&str as FromSql>::accepts(ty)
330 0 : }
331 : }
332 :
333 : impl<'a> FromSql<'a> for &'a str {
334 0 : fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<&'a str, Box<dyn Error + Sync + Send>> {
335 0 : match *ty {
336 0 : ref ty if ty.name() == "ltree" => types::ltree_from_sql(raw),
337 0 : ref ty if ty.name() == "lquery" => types::lquery_from_sql(raw),
338 0 : ref ty if ty.name() == "ltxtquery" => types::ltxtquery_from_sql(raw),
339 0 : _ => types::text_from_sql(raw),
340 : }
341 0 : }
342 :
343 0 : fn accepts(ty: &Type) -> bool {
344 0 : match *ty {
345 0 : Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::NAME | Type::UNKNOWN => true,
346 0 : ref ty
347 0 : if (ty.name() == "citext"
348 0 : || ty.name() == "ltree"
349 0 : || ty.name() == "lquery"
350 0 : || ty.name() == "ltxtquery") =>
351 0 : {
352 0 : true
353 : }
354 0 : _ => false,
355 : }
356 0 : }
357 : }
358 :
359 : macro_rules! simple_from {
360 : ($t:ty, $f:ident, $($expected:ident),+) => {
361 : impl<'a> FromSql<'a> for $t {
362 0 : fn from_sql(_: &Type, raw: &'a [u8]) -> Result<$t, Box<dyn Error + Sync + Send>> {
363 0 : types::$f(raw)
364 0 : }
365 :
366 : accepts!($($expected),+);
367 : }
368 : }
369 : }
370 :
371 : simple_from!(i8, char_from_sql, CHAR);
372 : simple_from!(u32, oid_from_sql, OID);
373 :
374 : /// An enum representing the nullability of a Postgres value.
375 : pub enum IsNull {
376 : /// The value is NULL.
377 : Yes,
378 : /// The value is not NULL.
379 : No,
380 : }
381 :
382 : /// A trait for types that can be converted into Postgres values.
383 : pub trait ToSql: fmt::Debug {
384 : /// Converts the value of `self` into the binary format of the specified
385 : /// Postgres `Type`, appending it to `out`.
386 : ///
387 : /// The caller of this method is responsible for ensuring that this type
388 : /// is compatible with the Postgres `Type`.
389 : ///
390 : /// The return value indicates if this value should be represented as
391 : /// `NULL`. If this is the case, implementations **must not** write
392 : /// anything to `out`.
393 : fn to_sql(&self, ty: &Type, out: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>>
394 : where
395 : Self: Sized;
396 :
397 : /// Determines if a value of this type can be converted to the specified
398 : /// Postgres `Type`.
399 : fn accepts(ty: &Type) -> bool
400 : where
401 : Self: Sized;
402 :
403 : /// An adaptor method used internally by Rust-Postgres.
404 : ///
405 : /// *All* implementations of this method should be generated by the
406 : /// `to_sql_checked!()` macro.
407 : fn to_sql_checked(
408 : &self,
409 : ty: &Type,
410 : out: &mut BytesMut,
411 : ) -> Result<IsNull, Box<dyn Error + Sync + Send>>;
412 :
413 : /// Specify the encode format
414 0 : fn encode_format(&self, _ty: &Type) -> Format {
415 0 : Format::Binary
416 0 : }
417 : }
418 :
419 : /// Supported Postgres message format types
420 : ///
421 : /// Using Text format in a message assumes a Postgres `SERVER_ENCODING` of `UTF8`
422 : #[derive(Clone, Copy, Debug, PartialEq)]
423 : pub enum Format {
424 : /// Text format (UTF-8)
425 : Text,
426 : /// Compact, typed binary format
427 : Binary,
428 : }
429 :
430 : impl ToSql for &str {
431 0 : fn to_sql(&self, ty: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
432 0 : match *ty {
433 0 : ref ty if ty.name() == "ltree" => types::ltree_to_sql(self, w),
434 0 : ref ty if ty.name() == "lquery" => types::lquery_to_sql(self, w),
435 0 : ref ty if ty.name() == "ltxtquery" => types::ltxtquery_to_sql(self, w),
436 0 : _ => types::text_to_sql(self, w),
437 : }
438 0 : Ok(IsNull::No)
439 0 : }
440 :
441 0 : fn accepts(ty: &Type) -> bool {
442 0 : match *ty {
443 0 : Type::VARCHAR | Type::TEXT | Type::BPCHAR | Type::NAME | Type::UNKNOWN => true,
444 0 : ref ty
445 0 : if (ty.name() == "citext"
446 0 : || ty.name() == "ltree"
447 0 : || ty.name() == "lquery"
448 0 : || ty.name() == "ltxtquery") =>
449 0 : {
450 0 : true
451 : }
452 0 : _ => false,
453 : }
454 0 : }
455 :
456 : to_sql_checked!();
457 : }
458 :
459 : macro_rules! simple_to {
460 : ($t:ty, $f:ident, $($expected:ident),+) => {
461 : impl ToSql for $t {
462 0 : fn to_sql(&self,
463 0 : _: &Type,
464 0 : w: &mut BytesMut)
465 0 : -> Result<IsNull, Box<dyn Error + Sync + Send>> {
466 0 : types::$f(*self, w);
467 0 : Ok(IsNull::No)
468 0 : }
469 :
470 : accepts!($($expected),+);
471 :
472 : to_sql_checked!();
473 : }
474 : }
475 : }
476 :
477 : simple_to!(u32, oid_to_sql, OID);
|