use rudb_catalog::{Catalog, Rows, Table};
use rudb_common::{Result, Session, Value};
use rudb_functions::link_fields;
use rudb_graph::{Cardinality, Relationship, Side, parse_links};
use rudb_plan::{Plan, Slice};
use crate::metadata::{Metadata, text};
pub(crate) fn links(
session: &Session,
catalog: &Catalog,
plan: &Plan,
index: u32,
columns: Slice,
) -> Result<Metadata> {
let declared = parse_links(session.links()).unwrap_or_default();
let mut rows = Vec::with_capacity(declared.len());
for link in &declared {
rows.push(row(catalog, link));
}
Metadata::new("rudb_links", &link_fields(), &rows, plan, index, columns)
}
fn row(catalog: &Catalog, link: &Relationship) -> Vec<Value> {
let stored = key_map_of(catalog, &link.parent);
let (cardinality, note) = verdict(catalog, link, stored.as_ref());
vec![
text(&link.name()),
text(&link.child.table),
text(&link.child.columns.join(", ")),
text(&link.parent.table),
text(&link.parent.columns.join(", ")),
text(cardinality),
stored.as_ref().map_or(Value::Null, |map| text(map.form.label())),
stored
.as_ref()
.map_or(Value::Null, |map| Value::BigInt(i64::try_from(map.bytes).unwrap_or(i64::MAX))),
Value::Null,
Value::Null,
note.map_or(Value::Null, text),
]
}
struct Stored {
form: rudb_graph::Form,
bytes: usize,
distinct: bool,
}
fn key_map_of(catalog: &Catalog, parent: &Side) -> Option<Stored> {
if parent.columns.len() != 1 {
return None;
}
let table = table_named(catalog, &parent.table)?;
let column = table.column_index(&parent.columns[0])?;
let Rows::Native(reader) = table.rows() else { return None };
let map = rudb_native::graph::key_map(reader, column)?;
Some(Stored { form: map.form(), bytes: map.bytes(), distinct: map.observed().distinct })
}
fn table_named<'a>(catalog: &'a Catalog, name: &str) -> Option<&'a Table> {
catalog
.databases()
.iter()
.flat_map(|database| database.schemas().iter().flat_map(rudb_catalog::Schema::tables))
.find(|table| table.name().table.eq_ignore_ascii_case(name))
}
fn verdict(
catalog: &Catalog,
link: &Relationship,
stored: Option<&Stored>,
) -> (&'static str, Option<&'static str>) {
let Some(stored) = stored else {
if table_named(catalog, &link.parent.table).is_none() {
return (Cardinality::Unverified.label(), Some("no table of that name"));
}
if link.parent.columns.len() != 1 {
return (
Cardinality::Unverified.label(),
Some("a composite key needs a folded key map, which is not built"),
);
}
return (Cardinality::Unverified.label(), Some("no key map is stored"));
};
if !stored.distinct {
return (
Cardinality::Unverified.label(),
Some("the parent key repeats, so this is not a many to one relationship"),
);
}
(Cardinality::AtMostOne.label(), None)
}