diesel/type_impls/
option.rs

1use std::io::Write;
2
3use backend::Backend;
4use deserialize::{self, FromSql, FromSqlRow, Queryable, QueryableByName};
5use expression::bound::Bound;
6use expression::*;
7use query_builder::QueryId;
8use result::UnexpectedNullError;
9use row::NamedRow;
10use serialize::{self, IsNull, Output, ToSql};
11use sql_types::{HasSqlType, NotNull, Nullable};
12
13#[cfg(feature = "mysql")]
14use sql_types::IsSigned;
15
16impl<T, DB> HasSqlType<Nullable<T>> for DB
17where
18    DB: Backend + HasSqlType<T>,
19    T: NotNull,
20{
21    fn metadata(lookup: &DB::MetadataLookup) -> DB::TypeMetadata {
22        <DB as HasSqlType<T>>::metadata(lookup)
23    }
24
25    #[cfg(feature = "with-deprecated")]
26    #[allow(deprecated)]
27    fn row_metadata(out: &mut Vec<DB::TypeMetadata>, lookup: &DB::MetadataLookup) {
28        <DB as HasSqlType<T>>::row_metadata(out, lookup)
29    }
30
31    #[cfg(feature = "mysql")]
32    fn mysql_row_metadata(
33        out: &mut Vec<(DB::TypeMetadata, IsSigned)>,
34        lookup: &DB::MetadataLookup,
35    ) {
36        <DB as HasSqlType<T>>::mysql_row_metadata(out, lookup)
37    }
38}
39
40impl<T> QueryId for Nullable<T>
41where
42    T: QueryId + NotNull,
43{
44    type QueryId = T::QueryId;
45
46    const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID;
47}
48
49impl<T, ST, DB> FromSql<Nullable<ST>, DB> for Option<T>
50where
51    T: FromSql<ST, DB>,
52    DB: Backend,
53    ST: NotNull,
54{
55    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
56        match bytes {
57            Some(_) => T::from_sql(bytes).map(Some),
58            None => Ok(None),
59        }
60    }
61}
62
63impl<T, ST, DB> Queryable<Nullable<ST>, DB> for Option<T>
64where
65    T: Queryable<ST, DB>,
66    DB: Backend,
67    Option<T::Row>: FromSqlRow<Nullable<ST>, DB>,
68    ST: NotNull,
69{
70    type Row = Option<T::Row>;
71
72    fn build(row: Self::Row) -> Self {
73        row.map(T::build)
74    }
75}
76
77impl<T, DB> QueryableByName<DB> for Option<T>
78where
79    T: QueryableByName<DB>,
80    DB: Backend,
81{
82    fn build<R: NamedRow<DB>>(row: &R) -> deserialize::Result<Self> {
83        match T::build(row) {
84            Ok(v) => Ok(Some(v)),
85            Err(e) => {
86                if e.is::<UnexpectedNullError>() {
87                    Ok(None)
88                } else {
89                    Err(e)
90                }
91            }
92        }
93    }
94}
95
96impl<T, ST, DB> FromSqlRow<Nullable<ST>, DB> for Option<T>
97where
98    T: FromSqlRow<ST, DB>,
99    DB: Backend,
100    ST: NotNull,
101{
102    const FIELDS_NEEDED: usize = T::FIELDS_NEEDED;
103
104    fn build_from_row<R: ::row::Row<DB>>(row: &mut R) -> deserialize::Result<Self> {
105        let fields_needed = Self::FIELDS_NEEDED;
106        if row.next_is_null(fields_needed) {
107            row.advance(fields_needed);
108            Ok(None)
109        } else {
110            T::build_from_row(row).map(Some)
111        }
112    }
113}
114
115impl<T, ST, DB> ToSql<Nullable<ST>, DB> for Option<T>
116where
117    T: ToSql<ST, DB>,
118    DB: Backend,
119    ST: NotNull,
120{
121    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
122        if let Some(ref value) = *self {
123            value.to_sql(out)
124        } else {
125            Ok(IsNull::Yes)
126        }
127    }
128}
129
130impl<T, ST> AsExpression<Nullable<ST>> for Option<T>
131where
132    ST: NotNull,
133{
134    type Expression = Bound<Nullable<ST>, Self>;
135
136    fn as_expression(self) -> Self::Expression {
137        Bound::new(self)
138    }
139}
140
141impl<'a, T, ST> AsExpression<Nullable<ST>> for &'a Option<T>
142where
143    ST: NotNull,
144{
145    type Expression = Bound<Nullable<ST>, Self>;
146
147    fn as_expression(self) -> Self::Expression {
148        Bound::new(self)
149    }
150}
151
152#[cfg(all(test, feature = "postgres"))]
153use pg::Pg;
154#[cfg(all(test, feature = "postgres"))]
155use sql_types;
156
157#[test]
158#[cfg(feature = "postgres")]
159fn option_to_sql() {
160    type Type = sql_types::Nullable<sql_types::VarChar>;
161    let mut bytes = Output::test();
162
163    let is_null = ToSql::<Type, Pg>::to_sql(&None::<String>, &mut bytes).unwrap();
164    assert_eq!(IsNull::Yes, is_null);
165    assert!(bytes.is_empty());
166
167    let is_null = ToSql::<Type, Pg>::to_sql(&Some(""), &mut bytes).unwrap();
168    assert_eq!(IsNull::No, is_null);
169    assert!(bytes.is_empty());
170
171    let is_null = ToSql::<Type, Pg>::to_sql(&Some("Sean"), &mut bytes).unwrap();
172    let expectd_bytes = b"Sean".to_vec();
173    assert_eq!(IsNull::No, is_null);
174    assert_eq!(bytes, expectd_bytes);
175}