sqlx_core/
encode.rs

1//! Provides [`Encode`] for encoding values for the database.
2
3use std::borrow::Cow;
4use std::mem;
5use std::rc::Rc;
6use std::sync::Arc;
7
8use crate::database::Database;
9use crate::error::BoxDynError;
10
11/// The return type of [Encode::encode].
12#[must_use]
13pub enum IsNull {
14    /// The value is null; no data was written.
15    Yes,
16
17    /// The value is not null.
18    ///
19    /// This does not mean that data was written.
20    No,
21}
22
23impl IsNull {
24    pub fn is_null(&self) -> bool {
25        matches!(self, IsNull::Yes)
26    }
27}
28
29/// Encode a single value to be sent to the database.
30pub trait Encode<'q, DB: Database> {
31    /// Writes the value of `self` into `buf` in the expected format for the database.
32    fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer) -> Result<IsNull, BoxDynError>
33    where
34        Self: Sized,
35    {
36        self.encode_by_ref(buf)
37    }
38
39    /// Writes the value of `self` into `buf` without moving `self`.
40    ///
41    /// Where possible, make use of `encode` instead as it can take advantage of re-using
42    /// memory.
43    fn encode_by_ref(
44        &self,
45        buf: &mut <DB as Database>::ArgumentBuffer,
46    ) -> Result<IsNull, BoxDynError>;
47
48    fn produces(&self) -> Option<DB::TypeInfo> {
49        // `produces` is inherently a hook to allow database drivers to produce value-dependent
50        // type information; if the driver doesn't need this, it can leave this as `None`
51        None
52    }
53
54    #[inline]
55    fn size_hint(&self) -> usize {
56        mem::size_of_val(self)
57    }
58}
59
60impl<'q, T, DB: Database> Encode<'q, DB> for &'_ T
61where
62    T: Encode<'q, DB>,
63{
64    #[inline]
65    fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer) -> Result<IsNull, BoxDynError> {
66        <T as Encode<DB>>::encode_by_ref(self, buf)
67    }
68
69    #[inline]
70    fn encode_by_ref(
71        &self,
72        buf: &mut <DB as Database>::ArgumentBuffer,
73    ) -> Result<IsNull, BoxDynError> {
74        <&T as Encode<DB>>::encode(self, buf)
75    }
76
77    #[inline]
78    fn produces(&self) -> Option<DB::TypeInfo> {
79        (**self).produces()
80    }
81
82    #[inline]
83    fn size_hint(&self) -> usize {
84        (**self).size_hint()
85    }
86}
87
88#[macro_export]
89macro_rules! impl_encode_for_option {
90    ($DB:ident) => {
91        impl<'q, T> $crate::encode::Encode<'q, $DB> for Option<T>
92        where
93            T: $crate::encode::Encode<'q, $DB> + $crate::types::Type<$DB> + 'q,
94        {
95            #[inline]
96            fn produces(&self) -> Option<<$DB as $crate::database::Database>::TypeInfo> {
97                if let Some(v) = self {
98                    v.produces()
99                } else {
100                    T::type_info().into()
101                }
102            }
103
104            #[inline]
105            fn encode(
106                self,
107                buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer,
108            ) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> {
109                if let Some(v) = self {
110                    v.encode(buf)
111                } else {
112                    Ok($crate::encode::IsNull::Yes)
113                }
114            }
115
116            #[inline]
117            fn encode_by_ref(
118                &self,
119                buf: &mut <$DB as $crate::database::Database>::ArgumentBuffer,
120            ) -> Result<$crate::encode::IsNull, $crate::error::BoxDynError> {
121                if let Some(v) = self {
122                    v.encode_by_ref(buf)
123                } else {
124                    Ok($crate::encode::IsNull::Yes)
125                }
126            }
127
128            #[inline]
129            fn size_hint(&self) -> usize {
130                self.as_ref().map_or(0, $crate::encode::Encode::size_hint)
131            }
132        }
133    };
134}
135
136macro_rules! impl_encode_for_smartpointer {
137    ($smart_pointer:ty) => {
138        impl<'q, T, DB: Database> Encode<'q, DB> for $smart_pointer
139        where
140            T: Encode<'q, DB>,
141        {
142            #[inline]
143            fn encode(
144                self,
145                buf: &mut <DB as Database>::ArgumentBuffer,
146            ) -> Result<IsNull, BoxDynError> {
147                <T as Encode<DB>>::encode_by_ref(self.as_ref(), buf)
148            }
149
150            #[inline]
151            fn encode_by_ref(
152                &self,
153                buf: &mut <DB as Database>::ArgumentBuffer,
154            ) -> Result<IsNull, BoxDynError> {
155                <&T as Encode<DB>>::encode(self, buf)
156            }
157
158            #[inline]
159            fn produces(&self) -> Option<DB::TypeInfo> {
160                (**self).produces()
161            }
162
163            #[inline]
164            fn size_hint(&self) -> usize {
165                (**self).size_hint()
166            }
167        }
168    };
169}
170
171impl_encode_for_smartpointer!(Arc<T>);
172impl_encode_for_smartpointer!(Box<T>);
173impl_encode_for_smartpointer!(Rc<T>);
174
175impl<'q, T, DB: Database> Encode<'q, DB> for Cow<'q, T>
176where
177    T: Encode<'q, DB>,
178    T: ToOwned,
179{
180    #[inline]
181    fn encode(self, buf: &mut <DB as Database>::ArgumentBuffer) -> Result<IsNull, BoxDynError> {
182        <&T as Encode<DB>>::encode_by_ref(&self.as_ref(), buf)
183    }
184
185    #[inline]
186    fn encode_by_ref(
187        &self,
188        buf: &mut <DB as Database>::ArgumentBuffer,
189    ) -> Result<IsNull, BoxDynError> {
190        <&T as Encode<DB>>::encode_by_ref(&self.as_ref(), buf)
191    }
192
193    #[inline]
194    fn produces(&self) -> Option<DB::TypeInfo> {
195        <&T as Encode<DB>>::produces(&self.as_ref())
196    }
197
198    #[inline]
199    fn size_hint(&self) -> usize {
200        <&T as Encode<DB>>::size_hint(&self.as_ref())
201    }
202}
203
204#[macro_export]
205macro_rules! forward_encode_impl {
206    ($for_type:ty, $forward_to:ty, $db:ident) => {
207        impl<'q> Encode<'q, $db> for $for_type {
208            fn encode_by_ref(
209                &self,
210                buf: &mut <$db as sqlx_core::database::Database>::ArgumentBuffer,
211            ) -> Result<IsNull, BoxDynError> {
212                <$forward_to as Encode<$db>>::encode(self.as_ref(), buf)
213            }
214        }
215    };
216}