use super::objects::{CatalogObjects, ConstraintOwner, MemberObject, RelationKind};
use uqa_core::RelationIdentity;
use uqa_sql::catalog::dependencies::{
ObjectAddress, ATTRIBUTE_DEFAULT_CLASS, CONSTRAINT_CLASS, FOREIGN_SERVER_CLASS,
FOREIGN_WRAPPER_CLASS, NAMESPACE_CLASS, PROCEDURE_CLASS, RELATION_CLASS, REWRITE_CLASS,
TRIGGER_CLASS, TYPE_CLASS,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CatalogObject {
Relation {
identity: RelationIdentity,
kind: RelationKind,
table: Option<RelationIdentity>,
},
Column {
relation: RelationIdentity,
kind: RelationKind,
name: String,
},
Type(u32),
ArrayType {
element: u32,
},
RowType {
relation: u32,
},
RelationConstraint {
relation: RelationIdentity,
kind: RelationKind,
name: String,
not_null: bool,
},
DomainConstraint {
domain: u32,
name: String,
not_null: bool,
},
ColumnDefault {
relation: RelationIdentity,
kind: RelationKind,
column: String,
},
Routine {
object_id: [u8; 16],
},
Rule {
relation: RelationIdentity,
kind: RelationKind,
name: String,
},
Trigger {
relation: RelationIdentity,
kind: RelationKind,
name: String,
},
Schema(String),
ForeignServer {
name: String,
object_id: [u8; 16],
},
ForeignWrapper {
name: String,
object_id: [u8; 16],
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum TypeObject {
Defined,
Array {
element: u32,
},
Row {
relation: u32,
},
}
impl CatalogObjects {
pub(super) fn catalog_object(&self, address: ObjectAddress) -> Option<CatalogObject> {
match address.class_id {
FOREIGN_WRAPPER_CLASS if address.sub_id == 0 => self
.foreign_wrapper_object(address.object_id)
.map(|(name, object_id)| CatalogObject::ForeignWrapper {
name: name.to_owned(),
object_id,
}),
FOREIGN_SERVER_CLASS if address.sub_id == 0 => self
.foreign_server_object(address.object_id)
.map(|(name, object_id)| CatalogObject::ForeignServer {
name: name.to_owned(),
object_id,
}),
RELATION_CLASS => {
let relation = self.relation(address.object_id)?;
if address.sub_id == 0 {
return Some(CatalogObject::Relation {
identity: relation.identity.clone(),
kind: relation.kind,
table: relation.table.clone(),
});
}
let column = relation
.columns
.iter()
.enumerate()
.find(|(index, column)| {
uqa_sql::catalog::relation_attributes::column_number(column, *index)
.is_ok_and(|number| i32::from(number) == address.sub_id)
})?
.1;
Some(CatalogObject::Column {
relation: relation.identity.clone(),
kind: relation.kind,
name: column.name.clone(),
})
}
TYPE_CLASS => Some(match self.type_object(address.object_id)? {
TypeObject::Defined => CatalogObject::Type(address.object_id),
TypeObject::Array { element } => CatalogObject::ArrayType { element },
TypeObject::Row { relation } => CatalogObject::RowType { relation },
}),
PROCEDURE_CLASS => self
.routine_object_id(address.object_id)
.map(|object_id| CatalogObject::Routine { object_id }),
NAMESPACE_CLASS => self
.namespace_name(address.object_id)
.map(|name| CatalogObject::Schema(name.to_string())),
CONSTRAINT_CLASS | ATTRIBUTE_DEFAULT_CLASS | REWRITE_CLASS | TRIGGER_CLASS => {
self.member_object(address)
}
_ => None,
}
}
fn member_object(&self, address: ObjectAddress) -> Option<CatalogObject> {
let relation = |oid: &u32| {
self.relation(*oid)
.map(|relation| (relation.identity.clone(), relation.kind))
};
Some(match self.member(address.class_id, address.object_id)? {
MemberObject::Constraint {
name,
owner: ConstraintOwner::Relation(owner),
not_null,
} => {
let (relation, kind) = relation(owner)?;
CatalogObject::RelationConstraint {
relation,
kind,
name: name.clone(),
not_null: *not_null,
}
}
MemberObject::Constraint {
name,
owner: ConstraintOwner::Domain(domain),
not_null,
} => CatalogObject::DomainConstraint {
domain: *domain,
name: name.clone(),
not_null: *not_null,
},
MemberObject::AttributeDefault {
relation: owner,
column,
} => {
let owner = self.relation(*owner)?;
let definition = owner
.columns
.iter()
.enumerate()
.find(|(index, definition)| {
uqa_sql::catalog::relation_attributes::column_number(definition, *index)
.is_ok_and(|number| i32::from(number) == *column)
})?
.1;
CatalogObject::ColumnDefault {
relation: owner.identity.clone(),
kind: owner.kind,
column: definition.name.clone(),
}
}
MemberObject::Rule {
name,
relation: owner,
} => {
let (relation, kind) = relation(owner)?;
CatalogObject::Rule {
relation,
kind,
name: name.clone(),
}
}
MemberObject::Trigger {
name,
relation: owner,
} => {
let (relation, kind) = relation(owner)?;
CatalogObject::Trigger {
relation,
kind,
name: name.clone(),
}
}
MemberObject::Membership { .. } => return None,
})
}
pub(super) fn relation_address(
&self,
identity: &RelationIdentity,
column: Option<&str>,
) -> Option<ObjectAddress> {
let oid = self.relation_oid(identity)?;
match column {
None => Some(ObjectAddress::whole(RELATION_CLASS, oid)),
Some(name) => Some(ObjectAddress::column(
oid,
self.relation(oid)?.column_number(name)?,
)),
}
}
pub(super) fn relation_member_address(
&self,
class_id: u32,
identity: &RelationIdentity,
name: &str,
) -> Option<ObjectAddress> {
let relation = self.relation_oid(identity)?;
self.members_of_class(class_id)
.find(|(_, member)| match member {
MemberObject::Constraint {
name: member,
owner: ConstraintOwner::Relation(owner),
..
}
| MemberObject::Rule {
name: member,
relation: owner,
}
| MemberObject::Trigger {
name: member,
relation: owner,
} => *owner == relation && member == name,
_ => false,
})
.map(|(oid, _)| ObjectAddress::whole(class_id, oid))
}
pub(super) fn column_default_address(
&self,
identity: &RelationIdentity,
column: &str,
) -> Option<ObjectAddress> {
let relation = self.relation_oid(identity)?;
let number = self.relation(relation)?.column_number(column)?;
self.members_of_class(ATTRIBUTE_DEFAULT_CLASS)
.find(|(_, member)| {
matches!(member, MemberObject::AttributeDefault { relation: owner, column }
if *owner == relation && *column == number)
})
.map(|(oid, _)| ObjectAddress::whole(ATTRIBUTE_DEFAULT_CLASS, oid))
}
pub(super) fn domain_constraint_address(
&self,
domain: u32,
name: &str,
) -> Option<ObjectAddress> {
self.members_of_class(CONSTRAINT_CLASS)
.find(|(_, member)| {
matches!(member, MemberObject::Constraint { name: member, owner: ConstraintOwner::Domain(owner), .. }
if *owner == domain && member == name)
})
.map(|(oid, _)| ObjectAddress::whole(CONSTRAINT_CLASS, oid))
}
}