mz_pgrepr/value/
record.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10// TODO(benesch): remove this module if upstream adds implementations of FromSql
11// to tuples directly: https://github.com/sfackler/rust-postgres/pull/626.
12
13use std::error::Error;
14
15use postgres_types::{FromSql, Kind, Type, WrongType, private};
16
17/// A wrapper for tuples that implements [`FromSql`] for PostgreSQL composite
18/// types.
19#[derive(Debug, PartialEq, Eq)]
20pub struct Record<T>(pub T);
21
22macro_rules! impl_tuple {
23    ($n:expr; $($ty_ident:ident),*; $($var_ident:ident),*) => {
24        impl<'a, $($ty_ident),*> FromSql<'a> for Record<($($ty_ident,)*)>
25        where
26            $($ty_ident: FromSql<'a>),*
27        {
28            fn from_sql(
29                _: &Type,
30                mut raw: &'a [u8],
31            ) -> Result<Record<($($ty_ident,)*)>, Box<dyn Error + Sync + Send>> {
32                let num_fields = private::read_be_i32(&mut raw)?;
33                let num_fields = u32::try_from(num_fields).map_err(|_| format!("number of fields cannot be negative: {}", num_fields))?;
34                if num_fields != $n {
35                    return Err(format!(
36                        "Postgres record field count does not match Rust tuple length: {} vs {}",
37                        num_fields,
38                        $n,
39                    ).into());
40                }
41
42                $(
43                    let oid = private::read_be_i32(&mut raw)?;
44                    let oid = u32::try_from(oid).map_err(|_| format!("OIDs cannot be negative: {}", oid))?;
45                    let ty = match Type::from_oid(oid) {
46                        None => {
47                            return Err(format!(
48                                "cannot decode OID {} inside of anonymous record",
49                                oid,
50                            ).into());
51                        }
52                        Some(ty) if !$ty_ident::accepts(&ty) => {
53                            return Err(Box::new(WrongType::new::<$ty_ident>(ty.clone())));
54                        }
55                        Some(ty) => ty,
56                    };
57                    let $var_ident = private::read_value(&ty, &mut raw)?;
58                )*
59
60                Ok(Record(($($var_ident,)*)))
61            }
62
63            fn accepts(ty: &Type) -> bool {
64                match ty.kind() {
65                    Kind::Pseudo => *ty == Type::RECORD,
66                    Kind::Composite(fields) => fields.len() == $n,
67                    _ => false,
68                }
69            }
70        }
71    };
72}
73
74impl_tuple!(0; ; );
75impl_tuple!(1; T0; v0);
76impl_tuple!(2; T0, T1; v0, v1);
77impl_tuple!(3; T0, T1, T2; v0, v1, v2);
78impl_tuple!(4; T0, T1, T2, T3; v0, v1, v2, v3);