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