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