129 lines
5.5 KiB
Rust
129 lines
5.5 KiB
Rust
use crate::database::schema::Schema;
|
|
|
|
impl Schema {
|
|
/// Dynamically infers and compiles all structural database relationships between this Schema
|
|
/// and its nested children. This functions recursively traverses the JSON Schema abstract syntax
|
|
/// tree, identifies physical PostgreSQL table boundaries, and locks the resulting relation
|
|
/// constraint paths directly onto the `compiled_edges` map in O(1) memory.
|
|
pub fn compile_edges(
|
|
&self,
|
|
db: &crate::database::Database,
|
|
root_id: &str,
|
|
path: &str,
|
|
props: &std::collections::BTreeMap<String, std::sync::Arc<Schema>>,
|
|
errors: &mut Vec<crate::drop::Error>,
|
|
) -> std::collections::BTreeMap<String, crate::database::edge::Edge> {
|
|
let mut schema_edges = std::collections::BTreeMap::new();
|
|
|
|
// Determine the physical Database Table Name this schema structurally represents
|
|
// Plucks the polymorphic discriminator via dot-notation (e.g. extracting "person" from "full.person")
|
|
let mut parent_type_name = None;
|
|
|
|
if let Some(family) = &self.obj.family {
|
|
// 1. Explicit horizontal routing
|
|
parent_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
|
|
} else if path == root_id {
|
|
// 2. Root nodes trust their exact registry footprint
|
|
let base_type_name = path.split('.').next_back().unwrap_or(path).to_string();
|
|
if db.types.contains_key(&base_type_name) {
|
|
parent_type_name = Some(base_type_name);
|
|
}
|
|
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
|
|
// 3. Nested graphs trust their explicit struct pointer reference
|
|
if !crate::database::object::is_primitive_type(t) {
|
|
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
|
}
|
|
}
|
|
|
|
if let Some(p_type) = parent_type_name {
|
|
// Proceed only if the resolved table physically exists within the Postgres Type hierarchy
|
|
if let Some(type_def) = db.types.get(&p_type) {
|
|
// Iterate over all discovered schema boundaries mapped inside the object
|
|
for (prop_name, prop_schema) in props {
|
|
let mut child_type_name = None;
|
|
let mut target_schema = prop_schema.clone();
|
|
let mut is_array = false;
|
|
|
|
// Structurally unpack the inner target entity if the object maps to an array list
|
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
|
|
&prop_schema.obj.type_
|
|
{
|
|
if t == "array" {
|
|
is_array = true;
|
|
if let Some(items) = &prop_schema.obj.items {
|
|
target_schema = items.clone();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine the physical Postgres table backing the nested child schema recursively
|
|
if let Some(family) = &target_schema.obj.family {
|
|
child_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
|
|
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
|
|
&target_schema.obj.type_
|
|
{
|
|
if !crate::database::object::is_primitive_type(t) {
|
|
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
|
}
|
|
} else if let Some(arr) = &target_schema.obj.one_of {
|
|
if let Some(first) = arr.first() {
|
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &first.obj.type_
|
|
{
|
|
if !crate::database::object::is_primitive_type(t) {
|
|
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(c_type) = child_type_name {
|
|
// Skip edge compilation for JSONB columns — they store data inline, not relationally.
|
|
// The physical column type from field_types is the single source of truth.
|
|
if let Some(ft) = type_def
|
|
.field_types
|
|
.as_ref()
|
|
.and_then(|v| v.get(prop_name.as_str()))
|
|
.and_then(|v| v.as_str())
|
|
{
|
|
if ft == "jsonb" {
|
|
continue;
|
|
}
|
|
}
|
|
if db.types.contains_key(&c_type) {
|
|
// Ensure the child Schema's AST has accurately compiled its own physical property keys so we can
|
|
// inject them securely for Many-to-Many Twin Deduction disambiguation matching.
|
|
target_schema.compile(db, root_id, format!("{}/{}", path, prop_name), errors);
|
|
|
|
if let Some(compiled_target_props) = target_schema.obj.compiled_properties.get() {
|
|
let keys_for_ambiguity: Vec<String> =
|
|
compiled_target_props.keys().cloned().collect();
|
|
|
|
// Interrogate the Database catalog graph to discover the exact Foreign Key Constraint connecting the components
|
|
if let Some((relation, is_forward)) = db.resolve_relation(
|
|
&p_type,
|
|
&c_type,
|
|
prop_name,
|
|
Some(&keys_for_ambiguity),
|
|
is_array,
|
|
Some(root_id),
|
|
&format!("{}/{}", path, prop_name),
|
|
errors,
|
|
) {
|
|
schema_edges.insert(
|
|
prop_name.clone(),
|
|
crate::database::edge::Edge {
|
|
constraint: relation.constraint.clone(),
|
|
forward: is_forward,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
schema_edges
|
|
}
|
|
}
|