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::ops::{Deref, DerefMut};
7use std::result;
8
9use backend::Backend;
10use sql_types::TypeMetadata;
11
12#[cfg(feature = "postgres")]
13pub use pg::serialize::*;
14
15/// A specialized result type representing the result of serializing
16/// a value for the database.
17pub type Result = result::Result<IsNull, Box<dyn Error + Send + Sync>>;
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
20/// Tiny enum to make the return type of `ToSql` more descriptive
21pub enum IsNull {
22    /// No data was written, as this type is null
23    Yes,
24    /// The value is not null
25    ///
26    /// This does not necessarily mean that any data was written to the buffer.
27    /// For example, an empty string has no data to be sent over the wire, but
28    /// also is not null.
29    No,
30}
31
32/// Wraps a buffer to be written by `ToSql` with additional backend specific
33/// utilities.
34#[derive(Clone, Copy)]
35pub struct Output<'a, T, DB>
36where
37    DB: TypeMetadata,
38    DB::MetadataLookup: 'a,
39{
40    out: T,
41    metadata_lookup: Option<&'a DB::MetadataLookup>,
42}
43
44impl<'a, T, DB: TypeMetadata> Output<'a, T, DB> {
45    /// Construct a new `Output`
46    pub fn new(out: T, metadata_lookup: &'a DB::MetadataLookup) -> Self {
47        Output {
48            out,
49            metadata_lookup: Some(metadata_lookup),
50        }
51    }
52
53    /// Create a new `Output` with the given buffer
54    pub fn with_buffer<U>(&self, new_out: U) -> Output<'a, U, DB> {
55        Output {
56            out: new_out,
57            metadata_lookup: self.metadata_lookup,
58        }
59    }
60
61    /// Return the raw buffer this type is wrapping
62    pub fn into_inner(self) -> T {
63        self.out
64    }
65
66    /// Returns the backend's mechanism for dynamically looking up type
67    /// metadata at runtime, if relevant for the given backend.
68    pub fn metadata_lookup(&self) -> &'a DB::MetadataLookup {
69        self.metadata_lookup.expect("Lookup is there")
70    }
71}
72
73#[cfg(test)]
74impl<DB: TypeMetadata> Output<'static, Vec<u8>, DB> {
75    /// Returns a `Output` suitable for testing `ToSql` implementations.
76    /// Unsafe to use for testing types which perform dynamic metadata lookup.
77    pub fn test() -> Self {
78        Self {
79            out: Vec::new(),
80            metadata_lookup: None,
81        }
82    }
83}
84
85impl<'a, T: Write, DB: TypeMetadata> Write for Output<'a, T, DB> {
86    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
87        self.out.write(buf)
88    }
89
90    fn flush(&mut self) -> io::Result<()> {
91        self.out.flush()
92    }
93
94    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
95        self.out.write_all(buf)
96    }
97
98    fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
99        self.out.write_fmt(fmt)
100    }
101}
102
103impl<'a, T, DB: TypeMetadata> Deref for Output<'a, T, DB> {
104    type Target = T;
105
106    fn deref(&self) -> &Self::Target {
107        &self.out
108    }
109}
110
111impl<'a, T, DB: TypeMetadata> DerefMut for Output<'a, T, DB> {
112    fn deref_mut(&mut self) -> &mut Self::Target {
113        &mut self.out
114    }
115}
116
117impl<'a, T, U, DB> PartialEq<U> for Output<'a, T, DB>
118where
119    DB: TypeMetadata,
120    T: PartialEq<U>,
121{
122    fn eq(&self, rhs: &U) -> bool {
123        self.out == *rhs
124    }
125}
126
127impl<'a, T, DB> fmt::Debug for Output<'a, T, DB>
128where
129    T: fmt::Debug,
130    DB: TypeMetadata,
131{
132    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133        self.out.fmt(f)
134    }
135}
136
137/// Serializes a single value to be sent to the database.
138///
139/// The output is sent as a bind parameter, and the data must be written in the
140/// expected format for the given backend.
141///
142/// When possible, implementations of this trait should prefer using an existing
143/// implementation, rather than writing to `out` directly. (For example, if you
144/// are implementing this for an enum, which is represented as an integer in the
145/// database, you should use `i32::to_sql(x, out)` instead of writing to `out`
146/// yourself.
147///
148/// Any types which implement this trait should also `#[derive(AsExpression)]`.
149///
150/// ### Backend specific details
151///
152/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text.
153/// - For SQLite, all implementations should be written in terms of an existing
154///   `ToSql` implementation.
155/// - For MySQL, the expected bytes will depend on the return value of
156///   `type_metadata` for the given SQL type. See [`MysqlType`] for details.
157/// - For third party backends, consult that backend's documentation.
158///
159/// [`MysqlType`]: ../mysql/enum.MysqlType.html
160///
161/// ### Examples
162///
163/// Most implementations of this trait will be defined in terms of an existing
164/// implementation.
165///
166/// ```rust
167/// # use diesel::backend::Backend;
168/// # use diesel::sql_types::*;
169/// # use diesel::serialize::{self, ToSql, Output};
170/// # use std::io::Write;
171/// #
172/// #[repr(i32)]
173/// #[derive(Debug, Clone, Copy)]
174/// pub enum MyEnum {
175///     A = 1,
176///     B = 2,
177/// }
178///
179/// impl<DB> ToSql<Integer, DB> for MyEnum
180/// where
181///     DB: Backend,
182///     i32: ToSql<Integer, DB>,
183/// {
184///     fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
185///         (*self as i32).to_sql(out)
186///     }
187/// }
188/// ```
189pub trait ToSql<A, DB: Backend>: fmt::Debug {
190    /// See the trait documentation.
191    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> Result;
192}
193
194impl<'a, A, T, DB> ToSql<A, DB> for &'a T
195where
196    DB: Backend,
197    T: ToSql<A, DB> + ?Sized,
198{
199    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> Result {
200        (*self).to_sql(out)
201    }
202}