use super::{ColumnScope, DependencyBuilder, References, RelationObject};
use uqa_core::RelationIdentity;
use uqa_sql::ast::ColumnDef;
use uqa_sql::catalog::dependencies::{
DependencyKind, ObjectAddress, FOREIGN_SERVER_CLASS, RELATION_CLASS, REWRITE_CLASS, TYPE_CLASS,
};
use uqa_sql::catalog::relation_oids::RelationCatalogOids;
use uqa_sql::SQLError;
impl DependencyBuilder<'_> {
pub(super) fn record_relations(&mut self) -> Result<(), SQLError> {
let snapshot = self.catalog.snapshot();
for (identity, table) in &snapshot.tables {
let oid = table.catalog_oids.relation;
self.record_relation(identity, oid, &table.columns, table.catalog_oids);
self.record_hierarchy(oid, &table.hierarchy)?;
}
for (identity, view) in snapshot.definitions.views.iter() {
let oids = view.relation_oids();
let relation = self.relation_object(oids.relation)?;
let columns = relation.columns.clone();
self.record_relation(identity, oids.relation, &columns, oids);
if let Some(rule) = oids.rule {
self.record_view_rule(rule, oids.relation, &view.query)?;
}
}
for (identity, table) in snapshot.definitions.foreign_tables.iter() {
let oids = table.relation_oids();
self.record_relation(identity, oids.relation, &table.columns, oids);
let server = ObjectAddress::whole(FOREIGN_SERVER_CLASS, table.server_oid()?);
self.objects.unpin(server);
self.recorder.record(
ObjectAddress::whole(RELATION_CLASS, oids.relation),
server,
DependencyKind::Normal,
);
}
for (identity, object_id) in snapshot.definitions.sequence_object_ids.iter() {
let oid = super::catalog_oid(
crate::catalog::sequence::catalog_oids::sequence_catalog_oid(
&snapshot.definitions.sequence_catalog_oids,
object_id,
),
)?;
let sequence = ObjectAddress::whole(RELATION_CLASS, oid);
self.record_namespace(sequence, &identity.schema);
let Some(owner) = snapshot
.definitions
.sequences
.get(identity)
.and_then(|state| state.owner.as_ref())
else {
continue;
};
let Some(column) = self.owned_column(owner.table_object_id, owner.column_object_id)
else {
continue;
};
let kind = match owner.dependency {
uqa_core::catalog_sequence::SequenceOwnerDependency::Automatic => {
DependencyKind::Auto
}
uqa_core::catalog_sequence::SequenceOwnerDependency::Internal => {
DependencyKind::Internal
}
};
self.recorder.record(sequence, column, kind);
}
Ok(())
}
fn record_relation(
&mut self,
identity: &RelationIdentity,
oid: u32,
columns: &[ColumnDef],
oids: RelationCatalogOids,
) {
let relation = ObjectAddress::whole(RELATION_CLASS, oid);
self.record_namespace(relation, &identity.schema);
for (index, column) in columns.iter().enumerate() {
let Ok(number) =
uqa_sql::catalog::relation_attributes::column_number(column, index).map(i32::from)
else {
continue;
};
let mut references = References::default();
if let Ok(ty) = u32::try_from(uqa_sql::catalog::type_metadata::pg_type_oid(&column.ty))
{
references.add_type(ty);
}
self.recorder.record_references(
ObjectAddress::column(oid, number),
references,
DependencyKind::Normal,
);
}
if let Some(row_type) = oids.row_type {
let row_type = ObjectAddress::whole(TYPE_CLASS, row_type);
self.recorder
.record(row_type, relation, DependencyKind::Internal);
if let Some(array) = oids.array_type {
self.recorder.record(
ObjectAddress::whole(TYPE_CLASS, array),
row_type,
DependencyKind::Internal,
);
}
}
}
fn record_hierarchy(
&mut self,
oid: u32,
hierarchy: &uqa_sql::ast::TableHierarchy,
) -> Result<(), SQLError> {
let relation = ObjectAddress::whole(RELATION_CLASS, oid);
let kind = if hierarchy.partition_bound.is_some() {
DependencyKind::Auto
} else {
DependencyKind::Normal
};
for parent in &hierarchy.parents {
if let Some(parent) = self.objects.relation_oid_by_name(parent) {
self.recorder
.record(relation, ObjectAddress::whole(RELATION_CLASS, parent), kind);
}
}
let Some(spec) = &hierarchy.partition_spec else {
return Ok(());
};
let table = self.relation_object(oid)?.clone();
let mut expressions = References::default();
let mut key_columns = Vec::new();
for key in &spec.keys {
match key {
uqa_sql::ast::Expr::Column(name) => key_columns.extend(table.column_number(name)),
expression => self.expressions().collect(
expression,
ColumnScope::Relation(oid, &table),
&mut expressions,
)?,
}
}
self.recorder.record_single_relation(
relation,
expressions,
oid,
(DependencyKind::Normal, DependencyKind::Internal),
true,
);
for column in key_columns {
self.recorder.record(
ObjectAddress::column(oid, column),
relation,
DependencyKind::Internal,
);
}
Ok(())
}
fn record_view_rule(
&mut self,
rule: u32,
view: u32,
query: &uqa_sql::plan::QueryPlan,
) -> Result<(), SQLError> {
let address = ObjectAddress::whole(REWRITE_CLASS, rule);
self.objects.add_member(
REWRITE_CLASS,
rule,
super::MemberObject::Rule {
name: "_RETURN".into(),
relation: view,
},
);
self.recorder.record(
address,
ObjectAddress::whole(RELATION_CLASS, view),
DependencyKind::Internal,
);
let references = self.query_references(query)?;
self.recorder
.record_references(address, references, DependencyKind::Normal);
Ok(())
}
fn owned_column(&self, table: [u8; 16], column: [u8; 16]) -> Option<ObjectAddress> {
let snapshot = self.catalog.snapshot();
let (oid, columns) = snapshot
.tables
.values()
.find(|candidate| candidate.object_id == table)
.map(|table| (table.catalog_oids.relation, table.columns.as_ref().clone()))
.or_else(|| {
snapshot
.definitions
.foreign_tables
.values()
.find(|candidate| candidate.object_id == table)
.map(|table| (table.relation_oids().relation, table.columns.clone()))
})?;
let index = columns
.iter()
.position(|candidate| candidate.object_id == Some(column))?;
Some(ObjectAddress::column(
oid,
i32::from(
uqa_sql::catalog::relation_attributes::column_number(&columns[index], index)
.ok()?,
),
))
}
pub(super) fn relation_object(&self, oid: u32) -> Result<&RelationObject, SQLError> {
self.objects
.relation(oid)
.ok_or_else(|| SQLError::Internal(format!("relation {oid} is not in the catalog")))
}
}