Skip to content

Commit e7cd057

Browse files
joeydewaalkriswuollett
authored andcommitted
feat(Postgres): support nested domain types (#3641)
* feat(Postgres): support nested domain types * chore: clippy * fix(postgres): Recurse when looking for type info.
1 parent cbc69eb commit e7cd057

File tree

4 files changed

+86
-22
lines changed

4 files changed

+86
-22
lines changed

sqlx-postgres/src/type_info.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ impl PgType {
10131013
/// If `soft_eq` is true and `self` or `other` is `DeclareWithOid` but not both, return `true`
10141014
/// before checking names.
10151015
fn eq_impl(&self, other: &Self, soft_eq: bool) -> bool {
1016-
if let (Some(a), Some(b)) = (self.try_oid(), other.try_oid()) {
1016+
if let (Some(a), Some(b)) = (self.try_base_oid(), other.try_base_oid()) {
10171017
// If there are OIDs available, use OIDs to perform a direct match
10181018
return a == b;
10191019
}
@@ -1035,6 +1035,18 @@ impl PgType {
10351035
// Otherwise, perform a match on the name
10361036
name_eq(self.name(), other.name())
10371037
}
1038+
1039+
// Tries to return the OID of the type, returns the OID of the base_type for domain types
1040+
#[inline(always)]
1041+
fn try_base_oid(&self) -> Option<Oid> {
1042+
match self {
1043+
PgType::Custom(custom) => match &custom.kind {
1044+
PgTypeKind::Domain(domain) => domain.try_oid(),
1045+
_ => Some(custom.oid),
1046+
},
1047+
ty => ty.try_oid(),
1048+
}
1049+
}
10381050
}
10391051

10401052
impl TypeInfo for PgTypeInfo {

sqlx-postgres/src/types/record.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,27 +103,7 @@ impl<'r> PgRecordDecoder<'r> {
103103
match self.fmt {
104104
PgValueFormat::Binary => {
105105
let element_type_oid = Oid(self.buf.get_u32());
106-
let element_type_opt = match self.typ.0.kind() {
107-
PgTypeKind::Simple if self.typ.0 == PgType::Record => {
108-
PgTypeInfo::try_from_oid(element_type_oid)
109-
}
110-
111-
PgTypeKind::Composite(fields) => {
112-
let ty = fields[self.ind].1.clone();
113-
if ty.0.oid() != element_type_oid {
114-
return Err("unexpected mismatch of composite type information".into());
115-
}
116-
117-
Some(ty)
118-
}
119-
120-
_ => {
121-
return Err(
122-
"unexpected non-composite type being decoded as a composite type"
123-
.into(),
124-
);
125-
}
126-
};
106+
let element_type_opt = self.find_type_info(&self.typ, element_type_oid)?;
127107

128108
if let Some(ty) = &element_type_opt {
129109
if !ty.is_null() && !T::compatible(ty) {
@@ -202,4 +182,24 @@ impl<'r> PgRecordDecoder<'r> {
202182
}
203183
}
204184
}
185+
186+
fn find_type_info(
187+
&self,
188+
typ: &PgTypeInfo,
189+
oid: Oid,
190+
) -> Result<Option<PgTypeInfo>, BoxDynError> {
191+
match typ.kind() {
192+
PgTypeKind::Simple if typ.0 == PgType::Record => Ok(PgTypeInfo::try_from_oid(oid)),
193+
PgTypeKind::Composite(fields) => {
194+
let ty = fields[self.ind].1.clone();
195+
if ty.0.oid() != oid {
196+
return Err("unexpected mismatch of composite type information".into());
197+
}
198+
199+
Ok(Some(ty))
200+
}
201+
PgTypeKind::Domain(domain) => self.find_type_info(domain, oid),
202+
_ => Err("unexpected custom type being decoded as a composite type".into()),
203+
}
204+
}
205205
}

tests/postgres/setup.sql

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,16 @@ CREATE TABLE circles (
6868
c circle,
6969
EXCLUDE USING gist (c WITH &&)
7070
);
71+
72+
CREATE DOMAIN positive_int AS integer CHECK (VALUE >= 0);
73+
CREATE DOMAIN percentage AS positive_int CHECK (VALUE <= 100);
74+
75+
CREATE TYPE person as (
76+
id int,
77+
age positive_int,
78+
percent percentage
79+
);
80+
81+
CREATE TYPE leaf_composite AS (prim integer);
82+
CREATE DOMAIN domain AS leaf_composite;
83+
CREATE TYPE root_composite AS (domain domain);

tests/postgres/types.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,45 @@ test_type!(ltree_vec<Vec<sqlx::postgres::types::PgLTree>>(Postgres,
697697
]
698698
));
699699

700+
#[derive(sqlx::Type, Debug, PartialEq)]
701+
#[sqlx(type_name = "positive_int")]
702+
struct PositiveInt(i32);
703+
704+
#[derive(sqlx::Type, Debug, PartialEq)]
705+
#[sqlx(type_name = "percentage")]
706+
struct Percentage(PositiveInt);
707+
708+
#[derive(sqlx::Type, Debug, PartialEq)]
709+
struct Person {
710+
id: i32,
711+
age: PositiveInt,
712+
percent: Percentage,
713+
}
714+
715+
test_type!(nested_domain_types_1<Person>(Postgres,
716+
"ROW(1, 21::positive_int, 50::percentage)::person" == Person { id: 1, age: PositiveInt(21), percent: Percentage(PositiveInt(50)) })
717+
);
718+
719+
#[derive(sqlx::Type, Debug, PartialEq)]
720+
#[sqlx(type_name = "leaf_composite")]
721+
struct LeafComposite {
722+
prim: i32,
723+
}
724+
725+
#[derive(sqlx::Type, Debug, PartialEq)]
726+
#[sqlx(type_name = "domain")]
727+
struct Domain(LeafComposite);
728+
729+
#[derive(sqlx::Type, Debug, PartialEq)]
730+
#[sqlx(type_name = "root_composite")]
731+
struct RootComposite {
732+
domain: Domain,
733+
}
734+
735+
test_type!(nested_domain_types_2<RootComposite>(Postgres,
736+
"ROW(ROW(1))::root_composite" == RootComposite { domain: Domain(LeafComposite { prim: 1})})
737+
);
738+
700739
test_type!(test_arc<Arc<i32>>(Postgres, "1::INT4" == Arc::new(1i32)));
701740
test_type!(test_cow<Cow<'_, i32>>(Postgres, "1::INT4" == Cow::<i32>::Owned(1i32)));
702741
test_type!(test_box<Box<i32>>(Postgres, "1::INT4" == Box::new(1i32)));

0 commit comments

Comments
 (0)