mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
feat(Postgres): support nested domain types (#3641)
* feat(Postgres): support nested domain types * chore: clippy * fix(postgres): Recurse when looking for type info.
This commit is contained in:
parent
1228d243be
commit
9f28837bca
@ -1013,7 +1013,7 @@ impl PgType {
|
||||
/// If `soft_eq` is true and `self` or `other` is `DeclareWithOid` but not both, return `true`
|
||||
/// before checking names.
|
||||
fn eq_impl(&self, other: &Self, soft_eq: bool) -> bool {
|
||||
if let (Some(a), Some(b)) = (self.try_oid(), other.try_oid()) {
|
||||
if let (Some(a), Some(b)) = (self.try_base_oid(), other.try_base_oid()) {
|
||||
// If there are OIDs available, use OIDs to perform a direct match
|
||||
return a == b;
|
||||
}
|
||||
@ -1035,6 +1035,18 @@ impl PgType {
|
||||
// Otherwise, perform a match on the name
|
||||
name_eq(self.name(), other.name())
|
||||
}
|
||||
|
||||
// Tries to return the OID of the type, returns the OID of the base_type for domain types
|
||||
#[inline(always)]
|
||||
fn try_base_oid(&self) -> Option<Oid> {
|
||||
match self {
|
||||
PgType::Custom(custom) => match &custom.kind {
|
||||
PgTypeKind::Domain(domain) => domain.try_oid(),
|
||||
_ => Some(custom.oid),
|
||||
},
|
||||
ty => ty.try_oid(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeInfo for PgTypeInfo {
|
||||
|
||||
@ -103,27 +103,7 @@ impl<'r> PgRecordDecoder<'r> {
|
||||
match self.fmt {
|
||||
PgValueFormat::Binary => {
|
||||
let element_type_oid = Oid(self.buf.get_u32());
|
||||
let element_type_opt = match self.typ.0.kind() {
|
||||
PgTypeKind::Simple if self.typ.0 == PgType::Record => {
|
||||
PgTypeInfo::try_from_oid(element_type_oid)
|
||||
}
|
||||
|
||||
PgTypeKind::Composite(fields) => {
|
||||
let ty = fields[self.ind].1.clone();
|
||||
if ty.0.oid() != element_type_oid {
|
||||
return Err("unexpected mismatch of composite type information".into());
|
||||
}
|
||||
|
||||
Some(ty)
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(
|
||||
"unexpected non-composite type being decoded as a composite type"
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
};
|
||||
let element_type_opt = self.find_type_info(&self.typ, element_type_oid)?;
|
||||
|
||||
if let Some(ty) = &element_type_opt {
|
||||
if !ty.is_null() && !T::compatible(ty) {
|
||||
@ -202,4 +182,24 @@ impl<'r> PgRecordDecoder<'r> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_type_info(
|
||||
&self,
|
||||
typ: &PgTypeInfo,
|
||||
oid: Oid,
|
||||
) -> Result<Option<PgTypeInfo>, BoxDynError> {
|
||||
match typ.kind() {
|
||||
PgTypeKind::Simple if typ.0 == PgType::Record => Ok(PgTypeInfo::try_from_oid(oid)),
|
||||
PgTypeKind::Composite(fields) => {
|
||||
let ty = fields[self.ind].1.clone();
|
||||
if ty.0.oid() != oid {
|
||||
return Err("unexpected mismatch of composite type information".into());
|
||||
}
|
||||
|
||||
Ok(Some(ty))
|
||||
}
|
||||
PgTypeKind::Domain(domain) => self.find_type_info(domain, oid),
|
||||
_ => Err("unexpected custom type being decoded as a composite type".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,3 +68,16 @@ CREATE TABLE circles (
|
||||
c circle,
|
||||
EXCLUDE USING gist (c WITH &&)
|
||||
);
|
||||
|
||||
CREATE DOMAIN positive_int AS integer CHECK (VALUE >= 0);
|
||||
CREATE DOMAIN percentage AS positive_int CHECK (VALUE <= 100);
|
||||
|
||||
CREATE TYPE person as (
|
||||
id int,
|
||||
age positive_int,
|
||||
percent percentage
|
||||
);
|
||||
|
||||
CREATE TYPE leaf_composite AS (prim integer);
|
||||
CREATE DOMAIN domain AS leaf_composite;
|
||||
CREATE TYPE root_composite AS (domain domain);
|
||||
|
||||
@ -697,6 +697,45 @@ test_type!(ltree_vec<Vec<sqlx::postgres::types::PgLTree>>(Postgres,
|
||||
]
|
||||
));
|
||||
|
||||
#[derive(sqlx::Type, Debug, PartialEq)]
|
||||
#[sqlx(type_name = "positive_int")]
|
||||
struct PositiveInt(i32);
|
||||
|
||||
#[derive(sqlx::Type, Debug, PartialEq)]
|
||||
#[sqlx(type_name = "percentage")]
|
||||
struct Percentage(PositiveInt);
|
||||
|
||||
#[derive(sqlx::Type, Debug, PartialEq)]
|
||||
struct Person {
|
||||
id: i32,
|
||||
age: PositiveInt,
|
||||
percent: Percentage,
|
||||
}
|
||||
|
||||
test_type!(nested_domain_types_1<Person>(Postgres,
|
||||
"ROW(1, 21::positive_int, 50::percentage)::person" == Person { id: 1, age: PositiveInt(21), percent: Percentage(PositiveInt(50)) })
|
||||
);
|
||||
|
||||
#[derive(sqlx::Type, Debug, PartialEq)]
|
||||
#[sqlx(type_name = "leaf_composite")]
|
||||
struct LeafComposite {
|
||||
prim: i32,
|
||||
}
|
||||
|
||||
#[derive(sqlx::Type, Debug, PartialEq)]
|
||||
#[sqlx(type_name = "domain")]
|
||||
struct Domain(LeafComposite);
|
||||
|
||||
#[derive(sqlx::Type, Debug, PartialEq)]
|
||||
#[sqlx(type_name = "root_composite")]
|
||||
struct RootComposite {
|
||||
domain: Domain,
|
||||
}
|
||||
|
||||
test_type!(nested_domain_types_2<RootComposite>(Postgres,
|
||||
"ROW(ROW(1))::root_composite" == RootComposite { domain: Domain(LeafComposite { prim: 1})})
|
||||
);
|
||||
|
||||
test_type!(test_arc<Arc<i32>>(Postgres, "1::INT4" == Arc::new(1i32)));
|
||||
test_type!(test_cow<Cow<'_, i32>>(Postgres, "1::INT4" == Cow::<i32>::Owned(1i32)));
|
||||
test_type!(test_box<Box<i32>>(Postgres, "1::INT4" == Box::new(1i32)));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user