queryer merger test progress
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
pub mod r#enum;
|
||||
pub mod executor;
|
||||
pub mod executors;
|
||||
pub mod formats;
|
||||
pub mod page;
|
||||
pub mod punc;
|
||||
@ -7,14 +7,26 @@ pub mod relation;
|
||||
pub mod schema;
|
||||
pub mod r#type;
|
||||
|
||||
use crate::database::r#enum::Enum;
|
||||
use crate::database::executor::{DatabaseExecutor, SpiExecutor};
|
||||
use crate::database::punc::{Punc, Stem};
|
||||
use crate::database::relation::Relation;
|
||||
use crate::database::schema::Schema;
|
||||
use crate::database::r#type::Type;
|
||||
// External mock exports inside the executor sub-folder
|
||||
|
||||
use r#enum::Enum;
|
||||
use executors::DatabaseExecutor;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use executors::pgrx::SpiExecutor;
|
||||
|
||||
#[cfg(test)]
|
||||
use executors::mock::MockExecutor;
|
||||
|
||||
pub mod stem;
|
||||
use punc::Punc;
|
||||
use relation::Relation;
|
||||
use schema::Schema;
|
||||
use serde_json::Value;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use stem::Stem;
|
||||
use r#type::Type;
|
||||
|
||||
pub struct Database {
|
||||
pub enums: HashMap<String, Enum>,
|
||||
@ -22,22 +34,28 @@ pub struct Database {
|
||||
pub puncs: HashMap<String, Punc>,
|
||||
pub relations: HashMap<String, Relation>,
|
||||
pub schemas: HashMap<String, Schema>,
|
||||
// Map of Schema ID -> { Entity Type -> Target Subschema Arc }
|
||||
pub stems: HashMap<String, HashMap<String, Arc<Stem>>>,
|
||||
pub descendants: HashMap<String, Vec<String>>,
|
||||
pub depths: HashMap<String, usize>,
|
||||
pub executor: Box<dyn DatabaseExecutor + Send + Sync>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn new(val: &serde_json::Value) -> Self {
|
||||
pub fn new(val: &serde_json::Value) -> Result<Self, crate::drop::Drop> {
|
||||
let mut db = Self {
|
||||
enums: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
relations: HashMap::new(),
|
||||
puncs: HashMap::new(),
|
||||
schemas: HashMap::new(),
|
||||
stems: HashMap::new(),
|
||||
descendants: HashMap::new(),
|
||||
depths: HashMap::new(),
|
||||
#[cfg(not(test))]
|
||||
executor: Box::new(SpiExecutor::new()),
|
||||
#[cfg(test)]
|
||||
executor: Box::new(MockExecutor::new()),
|
||||
};
|
||||
|
||||
if let Some(arr) = val.get("enums").and_then(|v| v.as_array()) {
|
||||
@ -86,8 +104,8 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
let _ = db.compile();
|
||||
db
|
||||
db.compile()?;
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
/// Override the default executor for unit testing
|
||||
@ -117,12 +135,12 @@ impl Database {
|
||||
}
|
||||
|
||||
/// Organizes the graph of the database, compiling regex, format functions, and caching relationships.
|
||||
fn compile(&mut self) -> Result<(), String> {
|
||||
pub fn compile(&mut self) -> Result<(), crate::drop::Drop> {
|
||||
self.collect_schemas();
|
||||
self.collect_depths();
|
||||
self.collect_descendants();
|
||||
self.compile_schemas();
|
||||
self.collect_stems();
|
||||
self.collect_stems()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -229,88 +247,78 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_stems(&mut self) {
|
||||
let mut st_map: HashMap<String, Vec<Stem>> = HashMap::new();
|
||||
for (name, _) in &self.puncs {
|
||||
let mut stems = Vec::new();
|
||||
let response_id = format!("{}.response", name);
|
||||
if let Some(resp_schema) = self.schemas.get(&response_id) {
|
||||
fn collect_stems(&mut self) -> Result<(), crate::drop::Drop> {
|
||||
let mut db_stems: HashMap<String, HashMap<String, Arc<Stem>>> = HashMap::new();
|
||||
let mut errors: Vec<crate::drop::Error> = Vec::new();
|
||||
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
for schema_id in schema_ids {
|
||||
if let Some(schema) = self.schemas.get(&schema_id) {
|
||||
let mut inner_map = HashMap::new();
|
||||
Self::discover_stems(
|
||||
&self.types,
|
||||
&self.schemas,
|
||||
&self.relations,
|
||||
&response_id,
|
||||
resp_schema,
|
||||
self,
|
||||
&schema_id,
|
||||
schema,
|
||||
String::from(""),
|
||||
None,
|
||||
None,
|
||||
&mut stems,
|
||||
&mut inner_map,
|
||||
&mut errors,
|
||||
);
|
||||
}
|
||||
st_map.insert(name.clone(), stems);
|
||||
}
|
||||
for (name, stems) in st_map {
|
||||
if let Some(p) = self.puncs.get_mut(&name) {
|
||||
p.stems = stems;
|
||||
if !inner_map.is_empty() {
|
||||
println!("SCHEMA: {} STEMS: {:?}", schema_id, inner_map.keys());
|
||||
db_stems.insert(schema_id, inner_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.stems = db_stems;
|
||||
|
||||
if !errors.is_empty() {
|
||||
return Err(crate::drop::Drop::with_errors(errors));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn discover_stems(
|
||||
types: &HashMap<String, Type>,
|
||||
schemas: &HashMap<String, Schema>,
|
||||
relations: &HashMap<String, Relation>,
|
||||
_schema_id: &str,
|
||||
db: &Database,
|
||||
root_schema_id: &str,
|
||||
schema: &Schema,
|
||||
current_path: String,
|
||||
mut current_path: String,
|
||||
parent_type: Option<String>,
|
||||
property_name: Option<String>,
|
||||
stems: &mut Vec<Stem>,
|
||||
inner_map: &mut HashMap<String, Arc<Stem>>,
|
||||
errors: &mut Vec<crate::drop::Error>,
|
||||
) {
|
||||
let mut is_entity = false;
|
||||
let mut entity_type = String::new();
|
||||
|
||||
// Check if this schema resolves to an Entity
|
||||
let mut current_ref = schema.obj.r#ref.clone();
|
||||
let mut depth = 0;
|
||||
while let Some(r) = current_ref {
|
||||
if types.contains_key(&r) {
|
||||
is_entity = true;
|
||||
entity_type = r.clone();
|
||||
break;
|
||||
}
|
||||
if let Some(s) = schemas.get(&r) {
|
||||
current_ref = s.obj.r#ref.clone();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
depth += 1;
|
||||
if depth > 20 {
|
||||
break;
|
||||
} // prevent infinite loop
|
||||
let mut examine_id = None;
|
||||
if let Some(ref r) = schema.obj.r#ref {
|
||||
examine_id = Some(r.clone());
|
||||
} else if let Some(ref id) = schema.obj.id {
|
||||
examine_id = Some(id.clone());
|
||||
}
|
||||
|
||||
if is_entity {
|
||||
let final_path = if current_path.is_empty() {
|
||||
"/".to_string()
|
||||
} else {
|
||||
current_path.clone()
|
||||
};
|
||||
if let Some(target) = examine_id {
|
||||
let parts: Vec<&str> = target.split('.').collect();
|
||||
if let Some(last_seg) = parts.last() {
|
||||
if db.types.contains_key(*last_seg) {
|
||||
is_entity = true;
|
||||
entity_type = last_seg.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut relation_col = None;
|
||||
let mut relation_col = None;
|
||||
if is_entity {
|
||||
if let (Some(pt), Some(prop)) = (&parent_type, &property_name) {
|
||||
let expected_col = format!("{}_id", prop);
|
||||
let mut found = false;
|
||||
|
||||
// Try to find the exact relation from the database schema
|
||||
for rel in relations.values() {
|
||||
if rel.source_type == *pt && rel.destination_type == entity_type {
|
||||
if rel.source_columns.contains(&expected_col) {
|
||||
relation_col = Some(expected_col.clone());
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
} else if rel.source_type == entity_type && rel.destination_type == *pt {
|
||||
for rel in db.relations.values() {
|
||||
if (rel.source_type == *pt && rel.destination_type == entity_type)
|
||||
|| (rel.source_type == entity_type && rel.destination_type == *pt)
|
||||
{
|
||||
if rel.source_columns.contains(&expected_col) {
|
||||
relation_col = Some(expected_col.clone());
|
||||
found = true;
|
||||
@ -318,64 +326,128 @@ impl Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// Fallback guess if explicit matching fails
|
||||
relation_col = Some(expected_col);
|
||||
}
|
||||
}
|
||||
|
||||
stems.push(Stem {
|
||||
path: final_path,
|
||||
let stem = Stem {
|
||||
r#type: entity_type.clone(),
|
||||
relation: relation_col,
|
||||
});
|
||||
schema: Arc::new(schema.clone()),
|
||||
};
|
||||
|
||||
let mut branch_path = current_path.clone();
|
||||
if !current_path.is_empty() {
|
||||
branch_path = format!("{}/{}", current_path, entity_type);
|
||||
}
|
||||
|
||||
if inner_map.contains_key(&branch_path) {
|
||||
errors.push(crate::drop::Error {
|
||||
code: "STEM_COLLISION".to_string(),
|
||||
message: format!("The stem path `{}` resolves to multiple Entity boundaries. This usually occurs during un-wrapped $family or oneOf polymorphic schemas where multiple Entities are directly assigned to the same property. To fix this, encapsulate the polymorphic branch.", branch_path),
|
||||
details: crate::drop::ErrorDetails {
|
||||
path: root_schema_id.to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
inner_map.insert(branch_path.clone(), Arc::new(stem));
|
||||
|
||||
// Update current_path for structural children
|
||||
current_path = branch_path;
|
||||
}
|
||||
|
||||
// Pass the new parent downwards
|
||||
let next_parent = if is_entity {
|
||||
Some(entity_type.clone())
|
||||
} else {
|
||||
parent_type.clone()
|
||||
};
|
||||
|
||||
// Properties branch
|
||||
if let Some(props) = &schema.obj.properties {
|
||||
for (k, v) in props {
|
||||
let next_path = format!(
|
||||
"{}/{}",
|
||||
if current_path.is_empty() {
|
||||
""
|
||||
} else {
|
||||
¤t_path
|
||||
},
|
||||
k
|
||||
);
|
||||
// Bypass target and source properties if we are in a relationship
|
||||
if let Some(parent_str) = &next_parent {
|
||||
if let Some(pt) = db.types.get(parent_str) {
|
||||
if pt.relationship && (k == "target" || k == "source") {
|
||||
Self::discover_stems(
|
||||
db,
|
||||
root_schema_id,
|
||||
v,
|
||||
current_path.clone(),
|
||||
next_parent.clone(),
|
||||
Some(k.clone()),
|
||||
inner_map,
|
||||
errors,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Standard Property Pathing
|
||||
let next_path = if current_path.is_empty() {
|
||||
k.clone()
|
||||
} else {
|
||||
format!("{}/{}", current_path, k)
|
||||
};
|
||||
|
||||
Self::discover_stems(
|
||||
types,
|
||||
schemas,
|
||||
relations,
|
||||
"",
|
||||
db,
|
||||
root_schema_id,
|
||||
v,
|
||||
next_path,
|
||||
next_parent.clone(),
|
||||
Some(k.clone()),
|
||||
stems,
|
||||
inner_map,
|
||||
errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Array Item branch
|
||||
if let Some(items) = &schema.obj.items {
|
||||
Self::discover_stems(
|
||||
types,
|
||||
schemas,
|
||||
relations,
|
||||
"",
|
||||
db,
|
||||
root_schema_id,
|
||||
items,
|
||||
current_path.clone(),
|
||||
next_parent.clone(),
|
||||
property_name.clone(),
|
||||
stems,
|
||||
inner_map,
|
||||
errors,
|
||||
);
|
||||
}
|
||||
|
||||
// Polymorphism branch
|
||||
if let Some(arr) = &schema.obj.one_of {
|
||||
for v in arr {
|
||||
Self::discover_stems(
|
||||
db,
|
||||
root_schema_id,
|
||||
v.as_ref(),
|
||||
current_path.clone(),
|
||||
next_parent.clone(),
|
||||
property_name.clone(),
|
||||
inner_map,
|
||||
errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(arr) = &schema.obj.all_of {
|
||||
for v in arr {
|
||||
Self::discover_stems(
|
||||
db,
|
||||
root_schema_id,
|
||||
v.as_ref(),
|
||||
current_path.clone(),
|
||||
next_parent.clone(),
|
||||
property_name.clone(),
|
||||
inner_map,
|
||||
errors,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user