Skip to main content

diesel/pg/types/
ipnet_address.rs

1extern crate libc;
2
3use ipnet::{IpNet, Ipv4Net, Ipv6Net};
4use std::io::prelude::*;
5use std::net::{Ipv4Addr, Ipv6Addr};
6
7use crate::deserialize::{self, FromSql, FromSqlRow};
8use crate::pg::{Pg, PgValue};
9#[cfg(test)]
10use crate::query_builder::bind_collector::ByteWrapper;
11use crate::serialize::{self, IsNull, Output, ToSql};
12use crate::sql_types::{Cidr, Inet};
13
14#[cfg(windows)]
15const AF_INET: u8 = 2;
16// Maybe not used, but defining to follow Rust's libstd/net/sys
17#[cfg(target_os = "redox")]
18const AF_INET: u8 = 1;
19
20#[allow(clippy::cast_possible_truncation)] // it's 2
21#[cfg(not(any(windows, target_os = "redox")))]
22const AF_INET: u8 = libc::AF_INET as u8;
23
24const PGSQL_AF_INET: u8 = AF_INET;
25const PGSQL_AF_INET6: u8 = AF_INET + 1;
26
27#[allow(dead_code)]
28mod foreign_derives {
29    use super::*;
30    use crate::expression::AsExpression;
31
32    #[derive(const _: () =
    {
        use diesel;
        impl<'__expr> diesel::expression::AsExpression<Inet> for
            &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Inet, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Inet>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
            for &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2> diesel::expression::AsExpression<Inet> for
            &'__expr2 &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Inet, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Inet>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
            for &'__expr2 &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<__DB>
            diesel::serialize::ToSql<diesel::sql_types::Nullable<Inet>, __DB>
            for IpNet where __DB: diesel::backend::Backend,
            Self: diesel::serialize::ToSql<Inet, __DB> {
            fn to_sql<'__b>(&'__b self,
                out: &mut diesel::serialize::Output<'__b, '_, __DB>)
                -> diesel::serialize::Result {
                diesel::serialize::ToSql::<Inet, __DB>::to_sql(self, out)
            }
        }
        impl diesel::expression::AsExpression<Inet> for IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Inet, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Inet>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>
            for IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Inet>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Inet>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<'__expr> diesel::expression::AsExpression<Cidr> for
            &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Cidr, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Cidr>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
            for &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2> diesel::expression::AsExpression<Cidr> for
            &'__expr2 &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Cidr, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Cidr>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        #[diagnostic::do_not_recommend]
        impl<'__expr, '__expr2>
            diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
            for &'__expr2 &'__expr IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl<__DB>
            diesel::serialize::ToSql<diesel::sql_types::Nullable<Cidr>, __DB>
            for IpNet where __DB: diesel::backend::Backend,
            Self: diesel::serialize::ToSql<Cidr, __DB> {
            fn to_sql<'__b>(&'__b self,
                out: &mut diesel::serialize::Output<'__b, '_, __DB>)
                -> diesel::serialize::Result {
                diesel::serialize::ToSql::<Cidr, __DB>::to_sql(self, out)
            }
        }
        impl diesel::expression::AsExpression<Cidr> for IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<Cidr, Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<Cidr>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
        impl diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>
            for IpNet {
            type Expression =
                diesel::internal::derives::as_expression::Bound<diesel::sql_types::Nullable<Cidr>,
                Self>;
            fn as_expression(self)
                ->
                    <Self as
                    diesel::expression::AsExpression<diesel::sql_types::Nullable<Cidr>>>::Expression {
                diesel::internal::derives::as_expression::Bound::new(self)
            }
        }
    };AsExpression, const _: () =
    {
        use diesel;
        impl<__DB, __ST> diesel::deserialize::Queryable<__ST, __DB> for IpNet
            where __DB: diesel::backend::Backend,
            __ST: diesel::sql_types::SingleValue,
            Self: diesel::deserialize::FromSql<__ST, __DB> {
            type Row = Self;
            fn build(row: Self) -> diesel::deserialize::Result<Self> {
                diesel::deserialize::Result::Ok(row)
            }
        }
    };FromSqlRow)]
33    #[diesel(foreign_derive)]
34    #[diesel(sql_type = Inet)]
35    #[diesel(sql_type = Cidr)]
36    struct IpNetworkProxy(IpNet);
37}
38
39macro_rules! err {
40    () => {
41        Err("invalid network address format".into())
42    };
43    ($msg:expr) => {
44        Err(format!("invalid network address format. {}", $msg).into())
45    };
46}
47
48macro_rules! assert_or_error {
49    ($cond:expr) => {
50        if !$cond {
51            return err!();
52        }
53    };
54
55    ($cond:expr, $msg:expr) => {
56        if !$cond {
57            return err!($msg);
58        }
59    };
60}
61
62macro_rules! impl_Sql {
63    ($ty: ty, $net_type: expr) => {
64        #[cfg(all(feature = "postgres_backend", feature = "ipnet-address"))]
65        impl FromSql<$ty, Pg> for IpNet {
66            fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
67                // https://github.com/postgres/postgres/blob/55c3391d1e6a201b5b891781d21fe682a8c64fe6/src/include/utils/inet.h#L23-L28
68                let bytes = value.as_bytes();
69                assert_or_error!(4 <= bytes.len(), "input is too short.");
70                let af = bytes[0];
71                let prefix = bytes[1];
72                let net_type = bytes[2];
73                let len = bytes[3];
74                assert_or_error!(
75                    net_type == $net_type,
76                    format!("returned type isn't a {}", stringify!($ty))
77                );
78                if af == PGSQL_AF_INET {
79                    assert_or_error!(bytes.len() == 8);
80                    assert_or_error!(len == 4, "the data isn't the size of ipv4");
81                    let b = &bytes[4..];
82                    let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
83                    let inet = Ipv4Net::new(addr, prefix)?;
84                    Ok(IpNet::V4(inet))
85                } else if af == PGSQL_AF_INET6 {
86                    assert_or_error!(bytes.len() == 20);
87                    assert_or_error!(len == 16, "the data isn't the size of ipv6");
88                    let b = &bytes[4..];
89                    let addr = Ipv6Addr::from([
90                        b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11],
91                        b[12], b[13], b[14], b[15],
92                    ]);
93                    let inet = Ipv6Net::new(addr, prefix)?;
94                    Ok(IpNet::V6(inet))
95                } else {
96                    err!()
97                }
98            }
99        }
100
101        #[cfg(all(feature = "postgres_backend", feature = "ipnet-address"))]
102        impl ToSql<$ty, Pg> for IpNet {
103            fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
104                let net_type = $net_type;
105                match *self {
106                    IpNet::V4(ref net) => {
107                        let mut data = [0u8; 8];
108                        let af = PGSQL_AF_INET;
109                        let prefix = net.prefix_len();
110                        let len: u8 = 4;
111                        let addr = if (net_type == 0) {
112                            net.addr().octets()
113                        } else {
114                            net.network().octets()
115                        };
116                        data[0] = af;
117                        data[1] = prefix;
118                        data[2] = net_type;
119                        data[3] = len;
120                        data[4..].copy_from_slice(&addr);
121                        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
122                    }
123                    IpNet::V6(ref net) => {
124                        let mut data = [0u8; 20];
125                        let af = PGSQL_AF_INET6;
126                        let prefix = net.prefix_len();
127                        let len: u8 = 16;
128                        let addr = if (net_type == 0) {
129                            net.addr().octets()
130                        } else {
131                            net.network().octets()
132                        };
133                        data[0] = af;
134                        data[1] = prefix;
135                        data[2] = net_type;
136                        data[3] = len;
137                        data[4..].copy_from_slice(&addr);
138                        out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
139                    }
140                }
141            }
142        }
143    };
144}
145
146impl FromSql<Inet, Pg> for IpNet {
    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
        let bytes = value.as_bytes();
        if !(4 <= bytes.len()) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        "input is too short."))
                            }).into());
        };
        let af = bytes[0];
        let prefix = bytes[1];
        let net_type = bytes[2];
        let len = bytes[3];
        if !(net_type == 0) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        ::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("returned type isn\'t a {0}",
                                                        "Inet"))
                                            })))
                            }).into());
        };
        if af == PGSQL_AF_INET {
            if !(bytes.len() == 8) {
                return Err("invalid network address format".into());
            };
            if !(len == 4) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv4"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
            let inet = Ipv4Net::new(addr, prefix)?;
            Ok(IpNet::V4(inet))
        } else if af == PGSQL_AF_INET6 {
            if !(bytes.len() == 20) {
                return Err("invalid network address format".into());
            };
            if !(len == 16) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv6"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr =
                Ipv6Addr::from([b[0], b[1], b[2], b[3], b[4], b[5], b[6],
                            b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14],
                            b[15]]);
            let inet = Ipv6Net::new(addr, prefix)?;
            Ok(IpNet::V6(inet))
        } else { Err("invalid network address format".into()) }
    }
}
impl ToSql<Inet, Pg> for IpNet {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        let net_type = 0;
        match *self {
            IpNet::V4(ref net) => {
                let mut data = [0u8; 8];
                let af = PGSQL_AF_INET;
                let prefix = net.prefix_len();
                let len: u8 = 4;
                let addr =
                    if (net_type == 0) {
                        net.addr().octets()
                    } else { net.network().octets() };
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
            IpNet::V6(ref net) => {
                let mut data = [0u8; 20];
                let af = PGSQL_AF_INET6;
                let prefix = net.prefix_len();
                let len: u8 = 16;
                let addr =
                    if (net_type == 0) {
                        net.addr().octets()
                    } else { net.network().octets() };
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
        }
    }
}impl_Sql!(Inet, 0);
147impl FromSql<Cidr, Pg> for IpNet {
    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
        let bytes = value.as_bytes();
        if !(4 <= bytes.len()) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        "input is too short."))
                            }).into());
        };
        let af = bytes[0];
        let prefix = bytes[1];
        let net_type = bytes[2];
        let len = bytes[3];
        if !(net_type == 1) {
            return Err(::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                        ::alloc::__export::must_use({
                                                ::alloc::fmt::format(format_args!("returned type isn\'t a {0}",
                                                        "Cidr"))
                                            })))
                            }).into());
        };
        if af == PGSQL_AF_INET {
            if !(bytes.len() == 8) {
                return Err("invalid network address format".into());
            };
            if !(len == 4) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv4"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr = Ipv4Addr::new(b[0], b[1], b[2], b[3]);
            let inet = Ipv4Net::new(addr, prefix)?;
            Ok(IpNet::V4(inet))
        } else if af == PGSQL_AF_INET6 {
            if !(bytes.len() == 20) {
                return Err("invalid network address format".into());
            };
            if !(len == 16) {
                return Err(::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("invalid network address format. {0}",
                                            "the data isn't the size of ipv6"))
                                }).into());
            };
            let b = &bytes[4..];
            let addr =
                Ipv6Addr::from([b[0], b[1], b[2], b[3], b[4], b[5], b[6],
                            b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14],
                            b[15]]);
            let inet = Ipv6Net::new(addr, prefix)?;
            Ok(IpNet::V6(inet))
        } else { Err("invalid network address format".into()) }
    }
}
impl ToSql<Cidr, Pg> for IpNet {
    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>)
        -> serialize::Result {
        let net_type = 1;
        match *self {
            IpNet::V4(ref net) => {
                let mut data = [0u8; 8];
                let af = PGSQL_AF_INET;
                let prefix = net.prefix_len();
                let len: u8 = 4;
                let addr =
                    if (net_type == 0) {
                        net.addr().octets()
                    } else { net.network().octets() };
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
            IpNet::V6(ref net) => {
                let mut data = [0u8; 20];
                let af = PGSQL_AF_INET6;
                let prefix = net.prefix_len();
                let len: u8 = 16;
                let addr =
                    if (net_type == 0) {
                        net.addr().octets()
                    } else { net.network().octets() };
                data[0] = af;
                data[1] = prefix;
                data[2] = net_type;
                data[3] = len;
                data[4..].copy_from_slice(&addr);
                out.write_all(&data).map(|_| IsNull::No).map_err(Into::into)
            }
        }
    }
}impl_Sql!(Cidr, 1);
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use crate::test_helpers::format_error;
153
154    #[diesel_test_helper::test]
155    fn v4address_to_sql() {
156        macro_rules! test_to_sql {
157            ($ty:ty, $net_type:expr) => {
158                let mut buffer = Vec::new();
159                {
160                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
161                    let test_address =
162                        IpNet::V4(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
163                    ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
164                }
165                assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]);
166            };
167        }
168
169        test_to_sql!(Inet, 0);
170        test_to_sql!(Cidr, 1);
171    }
172
173    #[diesel_test_helper::test]
174    fn v4_masked_address_to_sql() {
175        macro_rules! test_to_sql {
176            ($ty:ty, $net_type:expr, $last_byte:expr) => {
177                let mut buffer = Vec::new();
178                {
179                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
180                    let test_address =
181                        IpNet::V4(Ipv4Net::new(Ipv4Addr::new(192, 168, 0, 1), 24).unwrap());
182                    ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
183                }
184                assert_eq!(
185                    buffer,
186                    vec![PGSQL_AF_INET, 24, $net_type, 4, 192, 168, 0, $last_byte]
187                );
188            };
189        }
190
191        test_to_sql!(Inet, 0, 1);
192        test_to_sql!(Cidr, 1, 0);
193    }
194
195    #[diesel_test_helper::test]
196    fn some_v4address_from_sql() {
197        macro_rules! test_some_address_from_sql {
198            ($ty:tt) => {
199                let input_address =
200                    IpNet::V4(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap());
201                let mut buffer = Vec::new();
202                {
203                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
204                    ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
205                }
206                let output_address =
207                    FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
208                assert_eq!(input_address, output_address);
209            };
210        }
211
212        test_some_address_from_sql!(Cidr);
213        test_some_address_from_sql!(Inet);
214    }
215
216    #[diesel_test_helper::test]
217    fn v6address_to_sql() {
218        macro_rules! test_to_sql {
219            ($ty:ty, $net_type:expr) => {
220                let mut buffer = Vec::new();
221                {
222                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
223                    let test_address = IpNet::V6(
224                        Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), 64).unwrap(),
225                    );
226                    ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
227                }
228                assert_eq!(
229                    buffer,
230                    vec![
231                        PGSQL_AF_INET6,
232                        64,
233                        $net_type,
234                        16,
235                        0,
236                        0xfd,
237                        0,
238                        0,
239                        0,
240                        0,
241                        0,
242                        0,
243                        0,
244                        0,
245                        0,
246                        0,
247                        0,
248                        0,
249                        0,
250                        0,
251                    ]
252                );
253            };
254        }
255
256        test_to_sql!(Inet, 0);
257        test_to_sql!(Cidr, 1);
258    }
259
260    #[diesel_test_helper::test]
261    fn v6_masked_address_from_sql() {
262        macro_rules! test_to_sql {
263            ($ty:ty, $net_type:expr, $last_byte:expr) => {
264                let mut buffer = Vec::new();
265                {
266                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
267                    let test_address = IpNet::V6(
268                        Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(),
269                    );
270                    ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap();
271                }
272                assert_eq!(
273                    buffer,
274                    vec![
275                        PGSQL_AF_INET6,
276                        64,
277                        $net_type,
278                        16,
279                        0,
280                        0xfd,
281                        0,
282                        0,
283                        0,
284                        0,
285                        0,
286                        0,
287                        0,
288                        0,
289                        0,
290                        0,
291                        0,
292                        0,
293                        0,
294                        $last_byte,
295                    ]
296                );
297            };
298        }
299
300        test_to_sql!(Inet, 0, 1);
301        test_to_sql!(Cidr, 1, 0);
302    }
303
304    #[diesel_test_helper::test]
305    fn some_v6address_from_sql() {
306        macro_rules! test_some_address_from_sql {
307            ($ty:tt) => {
308                let input_address =
309                    IpNet::V6(Ipv6Net::new(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), 64).unwrap());
310                let mut buffer = Vec::new();
311                {
312                    let mut bytes = Output::test(ByteWrapper(&mut buffer));
313                    ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap();
314                }
315                let output_address =
316                    FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap();
317                assert_eq!(input_address, output_address);
318            };
319        }
320
321        test_some_address_from_sql!(Inet);
322        test_some_address_from_sql!(Cidr);
323    }
324
325    #[diesel_test_helper::test]
326    fn bad_address_from_sql() {
327        macro_rules! bad_address_from_sql {
328            ($ty:tt) => {
329                let address: Result<IpNet, _> =
330                    FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&[7, PGSQL_AF_INET, 0]));
331                assert_eq!(
332                    format_error(&*address.unwrap_err()),
333                    "invalid network address format. input is too short."
334                );
335            };
336        }
337
338        bad_address_from_sql!(Inet);
339        bad_address_from_sql!(Cidr);
340    }
341
342    #[diesel_test_helper::test]
343    fn no_address_from_sql() {
344        macro_rules! test_no_address_from_sql {
345            ($ty:ty) => {
346                let address: Result<IpNet, _> = FromSql::<$ty, Pg>::from_nullable_sql(None);
347                assert_eq!(
348                    format_error(&*address.unwrap_err()),
349                    "Unexpected null for non-null column"
350                );
351            };
352        }
353
354        test_no_address_from_sql!(Inet);
355        test_no_address_from_sql!(Cidr);
356    }
357}