Skip to main content

diesel/
serialize.rs

1//! Types and traits related to serializing values for the database
2
3use alloc::boxed::Box;
4use alloc::fmt;
5use core::error::Error;
6use core::result;
7
8use crate::backend::Backend;
9use crate::query_builder::BindCollector;
10use crate::query_builder::bind_collector::RawBytesBindCollector;
11
12#[doc(inline)]
13#[cfg(feature = "postgres_backend")]
14pub use crate::pg::serialize::WriteTuple;
15
16/// A specialized result type representing the result of serializing
17/// a value for the database.
18pub type Result = result::Result<IsNull, Box<dyn Error + Send + Sync>>;
19
20#[derive(#[automatically_derived]
impl ::core::fmt::Debug for IsNull {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self { IsNull::Yes => "Yes", IsNull::No => "No", })
    }
}Debug, #[automatically_derived]
impl ::core::marker::Copy for IsNull { }Copy, #[automatically_derived]
impl ::core::clone::Clone for IsNull {
    #[inline]
    fn clone(&self) -> IsNull { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for IsNull {
    #[inline]
    fn eq(&self, other: &IsNull) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for IsNull {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {}
}Eq)]
21/// Tiny enum to make the return type of `ToSql` more descriptive
22pub enum IsNull {
23    /// No data was written, as this type is null
24    Yes,
25    /// The value is not null
26    ///
27    /// This does not necessarily mean that any data was written to the buffer.
28    /// For example, an empty string has no data to be sent over the wire, but
29    /// also is not null.
30    No,
31}
32
33/// Wraps a buffer to be written by `ToSql` with additional backend specific
34/// utilities.
35pub struct Output<'a, 'b, DB>
36where
37    DB: Backend,
38    DB::MetadataLookup: 'a,
39{
40    out: <DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer,
41    metadata_lookup: Option<&'b mut DB::MetadataLookup>,
42}
43
44impl<'a, 'b, DB: Backend> Output<'a, 'b, DB> {
45    /// Construct a new `Output`
46    pub fn new(
47        out: <DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer,
48        metadata_lookup: &'b mut DB::MetadataLookup,
49    ) -> Self {
50        Output {
51            out,
52            metadata_lookup: Some(metadata_lookup),
53        }
54    }
55
56    /// Consume the current `Output` structure to access the inner buffer type
57    ///
58    /// This function is only useful for people implementing their own Backend.
59    pub fn into_inner(self) -> <DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer {
60        self.out
61    }
62
63    /// Returns the backend's mechanism for dynamically looking up type
64    /// metadata at runtime, if relevant for the given backend.
65    pub fn metadata_lookup(&mut self) -> &mut DB::MetadataLookup {
66        self.metadata_lookup.as_mut().expect("Lookup is there")
67    }
68
69    /// Set the inner buffer to a specific value
70    ///
71    /// Checkout the documentation of the type of `BindCollector::Buffer`
72    /// for your specific backend for supported types.
73    pub fn set_value<V>(&mut self, value: V)
74    where
75        V: Into<<DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer>,
76    {
77        self.out = value.into();
78    }
79}
80
81#[cfg(test)]
82impl<'a, DB: Backend> Output<'a, 'static, DB> {
83    /// Returns a `Output` suitable for testing `ToSql` implementations.
84    /// Unsafe to use for testing types which perform dynamic metadata lookup.
85    pub fn test(buffer: <DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer) -> Self {
86        Self {
87            out: buffer,
88            metadata_lookup: None,
89        }
90    }
91}
92
93#[cfg(feature = "std")]
94impl<DB> std::io::Write for Output<'_, '_, DB>
95where
96    for<'c> DB: Backend<BindCollector<'c> = RawBytesBindCollector<DB>>,
97{
98    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
99        self.out.0.write(buf)
100    }
101
102    fn flush(&mut self) -> std::io::Result<()> {
103        self.out.0.flush()
104    }
105
106    fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
107        self.out.0.write_all(buf)
108    }
109
110    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> std::io::Result<()> {
111        self.out.0.write_fmt(fmt)
112    }
113}
114
115impl<'a, DB> Output<'a, '_, DB>
116where
117    for<'c> DB: Backend<BindCollector<'c> = RawBytesBindCollector<DB>>,
118{
119    /// Call this method whenever you pass an instance of `Output<DB>` by value.
120    ///
121    /// Effectively copies `self`, with a narrower lifetime. When passing a
122    /// reference or a mutable reference, this is normally done by rust
123    /// implicitly. This is why you can pass `&mut Foo` to multiple functions,
124    /// even though mutable references are not `Copy`. However, this is only
125    /// done implicitly for references. For structs with lifetimes it must be
126    /// done explicitly. This method matches the semantics of what Rust would do
127    /// implicitly if you were passing a mutable reference
128    pub fn reborrow<'c>(&'c mut self) -> Output<'c, 'c, DB>
129    where
130        'a: 'c,
131    {
132        Output {
133            out: RawBytesBindCollector::<DB>::reborrow_buffer(&mut self.out),
134            metadata_lookup: match &mut self.metadata_lookup {
135                None => None,
136                Some(m) => Some(&mut **m),
137            },
138        }
139    }
140}
141
142impl<'a, DB> fmt::Debug for Output<'a, '_, DB>
143where
144    <DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer: fmt::Debug,
145    DB: Backend,
146{
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        self.out.fmt(f)
149    }
150}
151
152/// Serializes a single value to be sent to the database.
153///
154/// The output is sent as a bind parameter, and the data must be written in the
155/// expected format for the given backend.
156///
157/// When possible, implementations of this trait should prefer using an existing
158/// implementation, rather than writing to `out` directly. (For example, if you
159/// are implementing this for an enum, which is represented as an integer in the
160/// database, you should use `i32::to_sql(x, out)` instead of writing to `out`
161/// yourself.)
162///
163/// Any types which implement this trait should also
164/// [`#[derive(AsExpression)]`](derive@crate::expression::AsExpression).
165///
166/// ### Backend specific details
167///
168/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text.
169/// - For SQLite, all implementations should be written in terms of an existing
170///   `ToSql` implementation.
171/// - For MySQL, the expected bytes will depend on the return value of
172///   `type_metadata` for the given SQL type. See [`MysqlType`] for details.
173/// - For third party backends, consult that backend's documentation.
174///
175/// [`MysqlType`]: ../mysql/enum.MysqlType.html
176///
177/// ### Examples
178///
179/// Most implementations of this trait will be defined in terms of an existing
180/// implementation.
181///
182/// ```rust
183/// # use diesel::backend::Backend;
184/// # use diesel::expression::AsExpression;
185/// # use diesel::sql_types::*;
186/// # use diesel::serialize::{self, ToSql, Output};
187/// # use std::io::Write;
188/// #
189/// #[repr(i32)]
190/// #[derive(Debug, Clone, Copy, AsExpression)]
191/// #[diesel(sql_type = Integer)]
192/// pub enum MyEnum {
193///     A = 1,
194///     B = 2,
195/// }
196///
197/// impl<DB> ToSql<Integer, DB> for MyEnum
198/// where
199///     DB: Backend,
200///     i32: ToSql<Integer, DB>,
201/// {
202///     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
203///         match self {
204///             MyEnum::A => 1.to_sql(out),
205///             MyEnum::B => 2.to_sql(out),
206///         }
207///     }
208/// }
209/// ```
210///
211/// Example of creating a custom type mapping based on a MySQL [enum type](https://dev.mysql.com/doc/refman/8.0/en/enum.html)
212///
213/// This is designed to reuse the SQL type definition generated by diesel-cli
214///
215/// ```rust
216/// # use diesel::backend::Backend;
217/// # use diesel::expression::AsExpression;
218/// # use diesel::sql_types::*;
219/// # use diesel::serialize::{self, ToSql, Output, IsNull};
220/// # use std::io::Write;
221/// #
222/// pub mod sql_types {
223///     #[derive(diesel::sql_types::SqlType)]
224///     #[diesel(mysql_type(name = "Enum"))]
225///     pub struct PostEnum; //<- generated by diesel cli
226/// }
227/// #[derive(Debug, AsExpression, PartialEq, Clone)]
228/// #[diesel(sql_type = sql_types::PostEnum)]
229/// pub enum Post {
230///     FirstValue,
231///     SecondValue,
232/// }
233///
234/// # #[cfg(feature = "mysql")]
235/// impl ToSql<sql_types::PostEnum, diesel::mysql::Mysql> for Post {
236///     fn to_sql<'b>(
237///         &'b self,
238///         out: &mut Output<'b, '_, diesel::mysql::Mysql>,
239///     ) -> serialize::Result {
240///         match *self {
241///             // these string values need to match the labels used in your
242///             // enum definition in SQL. So this expects that you defined the
243///             /// relevant enum type as`ENUM('one', 'two')` in your `CREATE TABLE` statement
244///             Post::FirstValue => out.write_all(b"one")?,
245///             Post::SecondValue => out.write_all(b"two")?,
246///         }
247///         Ok(IsNull::No)
248///     }
249/// }
250/// ```
251///
252/// Using temporary values as part of the `ToSql` implementation requires additional
253/// work.
254///
255/// Backends using [`RawBytesBindCollector`] as [`BindCollector`] copy the serialized values as part
256/// of `Write` implementation. This includes the `Mysql` and the `Pg` backend provided by diesel.
257/// This means existing `ToSql` implementations can be used even with
258/// temporary values. For these it is required to call
259/// [`Output::reborrow`] to shorten the lifetime of the `Output` type correspondingly.
260///
261/// ```
262/// # use diesel::backend::Backend;
263/// # use diesel::expression::AsExpression;
264/// # use diesel::sql_types::*;
265/// # use diesel::serialize::{self, ToSql, Output};
266/// # use std::io::Write;
267/// #
268/// #[repr(i32)]
269/// #[derive(Debug, Clone, Copy, AsExpression)]
270/// #[diesel(sql_type = Integer)]
271/// pub enum MyEnum {
272///     A = 1,
273///     B = 2,
274/// }
275///
276/// # #[cfg(feature = "postgres")]
277/// impl ToSql<Integer, diesel::pg::Pg> for MyEnum
278/// where
279///     i32: ToSql<Integer, diesel::pg::Pg>,
280/// {
281///     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> serialize::Result {
282///         let v = *self as i32;
283///         <i32 as ToSql<Integer, diesel::pg::Pg>>::to_sql(&v, &mut out.reborrow())
284///     }
285/// }
286/// ````
287///
288/// For any other backend the [`Output::set_value`] method provides a way to
289/// set the output value directly. Checkout the documentation of the corresponding
290/// `BindCollector::Buffer` type for provided `From<T>` implementations for a list
291/// of accepted types. For the `Sqlite` backend see `SqliteBindValue`.
292///
293/// ```
294/// # use diesel::backend::Backend;
295/// # use diesel::expression::AsExpression;
296/// # use diesel::sql_types::*;
297/// # use diesel::serialize::{self, ToSql, Output, IsNull};
298/// # use std::io::Write;
299/// #
300/// #[repr(i32)]
301/// #[derive(Debug, Clone, Copy, AsExpression)]
302/// #[diesel(sql_type = Integer)]
303/// pub enum MyEnum {
304///     A = 1,
305///     B = 2,
306/// }
307///
308/// # #[cfg(feature = "__sqlite-shared")]
309/// impl ToSql<Integer, diesel::sqlite::Sqlite> for MyEnum
310/// where
311///     i32: ToSql<Integer, diesel::sqlite::Sqlite>,
312/// {
313///     fn to_sql<'b>(
314///         &'b self,
315///         out: &mut Output<'b, '_, diesel::sqlite::Sqlite>,
316///     ) -> serialize::Result {
317///         out.set_value(*self as i32);
318///         Ok(IsNull::No)
319///     }
320/// }
321/// ````
322pub trait ToSql<A, DB: Backend>: fmt::Debug {
323    /// See the trait documentation.
324    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> Result;
325}
326
327impl<A, T, DB> ToSql<A, DB> for &T
328where
329    DB: Backend,
330    T: ToSql<A, DB> + ?Sized,
331{
332    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> Result {
333        (*self).to_sql(out)
334    }
335}