diesel/
serialize.rs

1//! Types and traits related to serializing values for the database
2
3use std::error::Error;
4use std::fmt;
5use std::io::{self, Write};
6use std::result;
7
8use crate::backend::Backend;
9use crate::query_builder::bind_collector::RawBytesBindCollector;
10use crate::query_builder::BindCollector;
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(Debug, Copy, Clone, PartialEq, 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
93impl<DB> Write for Output<'_, '_, DB>
94where
95    for<'c> DB: Backend<BindCollector<'c> = RawBytesBindCollector<DB>>,
96{
97    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
98        self.out.0.write(buf)
99    }
100
101    fn flush(&mut self) -> io::Result<()> {
102        self.out.0.flush()
103    }
104
105    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
106        self.out.0.write_all(buf)
107    }
108
109    fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> {
110        self.out.0.write_fmt(fmt)
111    }
112}
113
114impl<'a, DB> Output<'a, '_, DB>
115where
116    for<'c> DB: Backend<BindCollector<'c> = RawBytesBindCollector<DB>>,
117{
118    /// Call this method whenever you pass an instance of `Output<DB>` by value.
119    ///
120    /// Effectively copies `self`, with a narrower lifetime. When passing a
121    /// reference or a mutable reference, this is normally done by rust
122    /// implicitly. This is why you can pass `&mut Foo` to multiple functions,
123    /// even though mutable references are not `Copy`. However, this is only
124    /// done implicitly for references. For structs with lifetimes it must be
125    /// done explicitly. This method matches the semantics of what Rust would do
126    /// implicitly if you were passing a mutable reference
127    pub fn reborrow<'c>(&'c mut self) -> Output<'c, 'c, DB>
128    where
129        'a: 'c,
130    {
131        Output {
132            out: RawBytesBindCollector::<DB>::reborrow_buffer(&mut self.out),
133            metadata_lookup: match &mut self.metadata_lookup {
134                None => None,
135                Some(m) => Some(&mut **m),
136            },
137        }
138    }
139}
140
141impl<'a, DB> fmt::Debug for Output<'a, '_, DB>
142where
143    <DB::BindCollector<'a> as BindCollector<'a, DB>>::Buffer: fmt::Debug,
144    DB: Backend,
145{
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        self.out.fmt(f)
148    }
149}
150
151/// Serializes a single value to be sent to the database.
152///
153/// The output is sent as a bind parameter, and the data must be written in the
154/// expected format for the given backend.
155///
156/// When possible, implementations of this trait should prefer using an existing
157/// implementation, rather than writing to `out` directly. (For example, if you
158/// are implementing this for an enum, which is represented as an integer in the
159/// database, you should use `i32::to_sql(x, out)` instead of writing to `out`
160/// yourself.)
161///
162/// Any types which implement this trait should also
163/// [`#[derive(AsExpression)]`](derive@crate::expression::AsExpression).
164///
165/// ### Backend specific details
166///
167/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text.
168/// - For SQLite, all implementations should be written in terms of an existing
169///   `ToSql` implementation.
170/// - For MySQL, the expected bytes will depend on the return value of
171///   `type_metadata` for the given SQL type. See [`MysqlType`] for details.
172/// - For third party backends, consult that backend's documentation.
173///
174/// [`MysqlType`]: ../mysql/enum.MysqlType.html
175///
176/// ### Examples
177///
178/// Most implementations of this trait will be defined in terms of an existing
179/// implementation.
180///
181/// ```rust
182/// # use diesel::backend::Backend;
183/// # use diesel::expression::AsExpression;
184/// # use diesel::sql_types::*;
185/// # use diesel::serialize::{self, ToSql, Output};
186/// # use std::io::Write;
187/// #
188/// #[repr(i32)]
189/// #[derive(Debug, Clone, Copy, AsExpression)]
190/// #[diesel(sql_type = Integer)]
191/// pub enum MyEnum {
192///     A = 1,
193///     B = 2,
194/// }
195///
196/// impl<DB> ToSql<Integer, DB> for MyEnum
197/// where
198///     DB: Backend,
199///     i32: ToSql<Integer, DB>,
200/// {
201///     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
202///         match self {
203///             MyEnum::A => 1.to_sql(out),
204///             MyEnum::B => 2.to_sql(out),
205///         }
206///     }
207/// }
208/// ```
209///
210/// Example of creating a custom type mapping based on a MySQL [enum type](https://dev.mysql.com/doc/refman/8.0/en/enum.html)
211///
212/// This is designed to reuse the SQL type definition generated by diesel-cli
213///
214/// ```rust
215/// # use diesel::backend::Backend;
216/// # use diesel::expression::AsExpression;
217/// # use diesel::sql_types::*;
218/// # use diesel::serialize::{self, ToSql, Output, IsNull};
219/// # use std::io::Write;
220/// #
221/// pub mod sql_types {
222///    #[derive(diesel::sql_types::SqlType)]
223///    #[diesel(mysql_type(name = "Enum"))]
224///    pub struct PostEnum; //<- generated by diesel cli
225/// }
226/// #[derive(Debug, AsExpression, PartialEq, Clone)]
227/// #[diesel(sql_type = sql_types::PostEnum)]
228/// pub enum Post {
229///    FirstValue,
230///    SecondValue,
231/// }
232///
233/// # #[cfg(feature = "mysql")]
234/// impl ToSql<sql_types::PostEnum, diesel::mysql::Mysql> for Post {
235///    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::mysql::Mysql>) -> serialize::Result {
236///        match *self {
237///            // these string values need to match the labels used in your
238///            // enum definition in SQL. So this expects that you defined the
239///            /// relevant enum type as`ENUM('one', 'two')` in your `CREATE TABLE` statement
240///            Post::FirstValue => out.write_all(b"one")?,
241///            Post::SecondValue => out.write_all(b"two")?,
242///        }
243///        Ok(IsNull::No)
244///    }
245/// }
246/// ```
247///
248/// Using temporary values as part of the `ToSql` implementation requires additional
249/// work.
250///
251/// Backends using [`RawBytesBindCollector`] as [`BindCollector`] copy the serialized values as part
252/// of `Write` implementation. This includes the `Mysql` and the `Pg` backend provided by diesel.
253/// This means existing `ToSql` implementations can be used even with
254/// temporary values. For these it is required to call
255/// [`Output::reborrow`] to shorten the lifetime of the `Output` type correspondingly.
256///
257/// ```
258/// # use diesel::backend::Backend;
259/// # use diesel::expression::AsExpression;
260/// # use diesel::sql_types::*;
261/// # use diesel::serialize::{self, ToSql, Output};
262/// # use std::io::Write;
263/// #
264/// #[repr(i32)]
265/// #[derive(Debug, Clone, Copy, AsExpression)]
266/// #[diesel(sql_type = Integer)]
267/// pub enum MyEnum {
268///     A = 1,
269///     B = 2,
270/// }
271///
272/// # #[cfg(feature = "postgres")]
273/// impl ToSql<Integer, diesel::pg::Pg> for MyEnum
274/// where
275///     i32: ToSql<Integer, diesel::pg::Pg>,
276/// {
277///     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::pg::Pg>) -> serialize::Result {
278///         let v = *self as i32;
279///         <i32 as ToSql<Integer, diesel::pg::Pg>>::to_sql(&v, &mut out.reborrow())
280///     }
281/// }
282/// ````
283///
284/// For any other backend the [`Output::set_value`] method provides a way to
285/// set the output value directly. Checkout the documentation of the corresponding
286/// `BindCollector::Buffer` type for provided `From<T>` implementations for a list
287/// of accepted types. For the `Sqlite` backend see `SqliteBindValue`.
288///
289/// ```
290/// # use diesel::backend::Backend;
291/// # use diesel::expression::AsExpression;
292/// # use diesel::sql_types::*;
293/// # use diesel::serialize::{self, ToSql, Output, IsNull};
294/// # use std::io::Write;
295/// #
296/// #[repr(i32)]
297/// #[derive(Debug, Clone, Copy, AsExpression)]
298/// #[diesel(sql_type = Integer)]
299/// pub enum MyEnum {
300///     A = 1,
301///     B = 2,
302/// }
303///
304/// # #[cfg(feature = "sqlite")]
305/// impl ToSql<Integer, diesel::sqlite::Sqlite> for MyEnum
306/// where
307///     i32: ToSql<Integer, diesel::sqlite::Sqlite>,
308/// {
309///     fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, diesel::sqlite::Sqlite>) -> serialize::Result {
310///         out.set_value(*self as i32);
311///         Ok(IsNull::No)
312///     }
313/// }
314/// ````
315pub trait ToSql<A, DB: Backend>: fmt::Debug {
316    /// See the trait documentation.
317    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> Result;
318}
319
320impl<A, T, DB> ToSql<A, DB> for &T
321where
322    DB: Backend,
323    T: ToSql<A, DB> + ?Sized,
324{
325    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> Result {
326        (*self).to_sql(out)
327    }
328}