checkpoint
This commit is contained in:
@ -161,12 +161,24 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
if let Some(one_of) = &self.obj.one_of {
|
if let Some(one_of) = &self.obj.one_of {
|
||||||
for (i, child) in one_of.iter().enumerate() {
|
for (i, child) in one_of.iter().enumerate() {
|
||||||
child.compile(db, root_id, format!("{}/oneOf/{}", path, i), visited, errors);
|
child.compile(
|
||||||
|
db,
|
||||||
|
root_id,
|
||||||
|
format!("{}/oneOf/{}", path, i),
|
||||||
|
visited,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(arr) = &self.obj.prefix_items {
|
if let Some(arr) = &self.obj.prefix_items {
|
||||||
for (i, child) in arr.iter().enumerate() {
|
for (i, child) in arr.iter().enumerate() {
|
||||||
child.compile(db, root_id, format!("{}/prefixItems/{}", path, i), visited, errors);
|
child.compile(
|
||||||
|
db,
|
||||||
|
root_id,
|
||||||
|
format!("{}/prefixItems/{}", path, i),
|
||||||
|
visited,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(child) = &self.obj.not {
|
if let Some(child) = &self.obj.not {
|
||||||
@ -178,13 +190,31 @@ impl Schema {
|
|||||||
if let Some(cases) = &self.obj.cases {
|
if let Some(cases) = &self.obj.cases {
|
||||||
for (i, c) in cases.iter().enumerate() {
|
for (i, c) in cases.iter().enumerate() {
|
||||||
if let Some(child) = &c.when {
|
if let Some(child) = &c.when {
|
||||||
child.compile(db, root_id, format!("{}/cases/{}/when", path, i), visited, errors);
|
child.compile(
|
||||||
|
db,
|
||||||
|
root_id,
|
||||||
|
format!("{}/cases/{}/when", path, i),
|
||||||
|
visited,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(child) = &c.then {
|
if let Some(child) = &c.then {
|
||||||
child.compile(db, root_id, format!("{}/cases/{}/then", path, i), visited, errors);
|
child.compile(
|
||||||
|
db,
|
||||||
|
root_id,
|
||||||
|
format!("{}/cases/{}/then", path, i),
|
||||||
|
visited,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(child) = &c.else_ {
|
if let Some(child) = &c.else_ {
|
||||||
child.compile(db, root_id, format!("{}/cases/{}/else", path, i), visited, errors);
|
child.compile(
|
||||||
|
db,
|
||||||
|
root_id,
|
||||||
|
format!("{}/cases/{}/else", path, i),
|
||||||
|
visited,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,7 +250,7 @@ impl Schema {
|
|||||||
if let Some(family) = &self.obj.family {
|
if let Some(family) = &self.obj.family {
|
||||||
// 1. Explicit horizontal routing
|
// 1. Explicit horizontal routing
|
||||||
parent_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
|
parent_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
|
||||||
} else if !path.contains('/') {
|
} else if path == root_id {
|
||||||
// 2. Root nodes trust their exact registry footprint
|
// 2. Root nodes trust their exact registry footprint
|
||||||
let base_type_name = path.split('.').next_back().unwrap_or(path).to_string();
|
let base_type_name = path.split('.').next_back().unwrap_or(path).to_string();
|
||||||
if db.types.contains_key(&base_type_name) {
|
if db.types.contains_key(&base_type_name) {
|
||||||
@ -234,7 +264,7 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if parent_type_name.is_none() {
|
if parent_type_name.is_none() {
|
||||||
// 4. Absolute fallback for completely anonymous inline structures
|
// 3. Absolute fallback for anonymous inline structures
|
||||||
let base_type_name = root_id
|
let base_type_name = root_id
|
||||||
.split('.')
|
.split('.')
|
||||||
.next_back()
|
.next_back()
|
||||||
@ -247,9 +277,17 @@ impl Schema {
|
|||||||
|
|
||||||
if let Some(p_type) = parent_type_name {
|
if let Some(p_type) = parent_type_name {
|
||||||
// Proceed only if the resolved table physically exists within the Postgres Type hierarchy
|
// Proceed only if the resolved table physically exists within the Postgres Type hierarchy
|
||||||
if db.types.contains_key(&p_type) {
|
if let Some(type_def) = db.types.get(&p_type) {
|
||||||
// Iterate over all discovered schema boundaries mapped inside the object
|
// Iterate over all discovered schema boundaries mapped inside the object
|
||||||
for (prop_name, prop_schema) in props {
|
for (prop_name, prop_schema) in props {
|
||||||
|
if let Some(field_types_map) = type_def.field_types.as_ref().and_then(|v| v.as_object()) {
|
||||||
|
if let Some(pg_type) = field_types_map.get(prop_name).and_then(|v| v.as_str()) {
|
||||||
|
if pg_type == "json" || pg_type == "jsonb" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut child_type_name = None;
|
let mut child_type_name = None;
|
||||||
let mut target_schema = prop_schema.clone();
|
let mut target_schema = prop_schema.clone();
|
||||||
let mut is_array = false;
|
let mut is_array = false;
|
||||||
@ -290,7 +328,13 @@ impl Schema {
|
|||||||
if db.types.contains_key(&c_type) {
|
if db.types.contains_key(&c_type) {
|
||||||
// Ensure the child Schema's AST has accurately compiled its own physical property keys so we can
|
// 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.
|
// inject them securely for Many-to-Many Twin Deduction disambiguation matching.
|
||||||
target_schema.compile(db, root_id, format!("{}/{}", path, prop_name), visited, errors);
|
target_schema.compile(
|
||||||
|
db,
|
||||||
|
root_id,
|
||||||
|
format!("{}/{}", path, prop_name),
|
||||||
|
visited,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
if let Some(compiled_target_props) = target_schema.obj.compiled_properties.get() {
|
if let Some(compiled_target_props) = target_schema.obj.compiled_properties.get() {
|
||||||
let keys_for_ambiguity: Vec<String> =
|
let keys_for_ambiguity: Vec<String> =
|
||||||
compiled_target_props.keys().cloned().collect();
|
compiled_target_props.keys().cloned().collect();
|
||||||
@ -379,7 +423,7 @@ impl Schema {
|
|||||||
for c in one_of {
|
for c in one_of {
|
||||||
let mut child_id = String::new();
|
let mut child_id = String::new();
|
||||||
let mut child_is_primitive = false;
|
let mut child_is_primitive = false;
|
||||||
|
|
||||||
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ {
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ {
|
||||||
if crate::database::object::is_primitive_type(t) {
|
if crate::database::object::is_primitive_type(t) {
|
||||||
child_is_primitive = true;
|
child_is_primitive = true;
|
||||||
@ -389,41 +433,41 @@ impl Schema {
|
|||||||
structural_types.insert("object".to_string());
|
structural_types.insert("object".to_string());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
disjoint_base = false;
|
disjoint_base = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !child_is_primitive {
|
if !child_is_primitive {
|
||||||
if let Some(t_val) = c.obj.get_discriminator_value("type", &child_id) {
|
if let Some(t_val) = c.obj.get_discriminator_value("type", &child_id) {
|
||||||
type_vals.insert(t_val);
|
type_vals.insert(t_val);
|
||||||
}
|
}
|
||||||
if let Some(k_val) = c.obj.get_discriminator_value("kind", &child_id) {
|
if let Some(k_val) = c.obj.get_discriminator_value("kind", &child_id) {
|
||||||
kind_vals.insert(k_val);
|
kind_vals.insert(k_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if disjoint_base && structural_types.len() == one_of.len() {
|
if disjoint_base && structural_types.len() == one_of.len() {
|
||||||
strategy = "".to_string();
|
strategy = "".to_string();
|
||||||
for (i, c) in one_of.iter().enumerate() {
|
for (i, c) in one_of.iter().enumerate() {
|
||||||
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ {
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ {
|
||||||
if crate::database::object::is_primitive_type(t) {
|
if crate::database::object::is_primitive_type(t) {
|
||||||
options.insert(t.clone(), (Some(i), None));
|
options.insert(t.clone(), (Some(i), None));
|
||||||
} else {
|
} else {
|
||||||
options.insert("object".to_string(), (Some(i), None));
|
options.insert("object".to_string(), (Some(i), None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
strategy = if type_vals.len() > 1 && type_vals.len() == one_of.len() {
|
strategy = if type_vals.len() > 1 && type_vals.len() == one_of.len() {
|
||||||
"type".to_string()
|
"type".to_string()
|
||||||
} else if kind_vals.len() > 1 && kind_vals.len() == one_of.len() {
|
} else if kind_vals.len() > 1 && kind_vals.len() == one_of.len() {
|
||||||
"kind".to_string()
|
"kind".to_string()
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
if strategy.is_empty() {
|
if strategy.is_empty() {
|
||||||
errors.push(crate::drop::Error {
|
errors.push(crate::drop::Error {
|
||||||
code: "AMBIGUOUS_POLYMORPHISM".to_string(),
|
code: "AMBIGUOUS_POLYMORPHISM".to_string(),
|
||||||
message: format!("oneOf boundaries must map mathematically unique 'type' or 'kind' discriminators, or strictly contain disjoint primitive types."),
|
message: format!("oneOf boundaries must map mathematically unique 'type' or 'kind' discriminators, or strictly contain disjoint primitive types."),
|
||||||
details: crate::drop::ErrorDetails {
|
details: crate::drop::ErrorDetails {
|
||||||
@ -432,20 +476,20 @@ impl Schema {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, c) in one_of.iter().enumerate() {
|
||||||
|
let mut child_id = String::new();
|
||||||
|
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ {
|
||||||
|
if !crate::database::object::is_primitive_type(t) {
|
||||||
|
child_id = t.clone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, c) in one_of.iter().enumerate() {
|
if let Some(val) = c.obj.get_discriminator_value(&strategy, &child_id) {
|
||||||
let mut child_id = String::new();
|
if options.contains_key(&val) {
|
||||||
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ {
|
errors.push(crate::drop::Error {
|
||||||
if !crate::database::object::is_primitive_type(t) {
|
|
||||||
child_id = t.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(val) = c.obj.get_discriminator_value(&strategy, &child_id) {
|
|
||||||
if options.contains_key(&val) {
|
|
||||||
errors.push(crate::drop::Error {
|
|
||||||
code: "POLYMORPHIC_COLLISION".to_string(),
|
code: "POLYMORPHIC_COLLISION".to_string(),
|
||||||
message: format!("Polymorphic boundary defines multiple candidates mapped to the identical discriminator value '{}'.", val),
|
message: format!("Polymorphic boundary defines multiple candidates mapped to the identical discriminator value '{}'.", val),
|
||||||
details: crate::drop::ErrorDetails {
|
details: crate::drop::ErrorDetails {
|
||||||
@ -454,12 +498,12 @@ impl Schema {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
options.insert(val, (Some(i), None));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options.insert(val, (Some(i), None));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@ -521,7 +565,7 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
} else if !crate::database::object::is_primitive_type(t) {
|
} else if !crate::database::object::is_primitive_type(t) {
|
||||||
Self::validate_identifier(t, "type", root_id, &path, errors);
|
Self::validate_identifier(t, "type", root_id, &path, errors);
|
||||||
|
|
||||||
// Is this an explicit inline ad-hoc composition?
|
// Is this an explicit inline ad-hoc composition?
|
||||||
if schema_arc.obj.properties.is_some() || schema_arc.obj.cases.is_some() {
|
if schema_arc.obj.properties.is_some() || schema_arc.obj.cases.is_some() {
|
||||||
to_insert.push((path.clone(), Arc::clone(schema_arc)));
|
to_insert.push((path.clone(), Arc::clone(schema_arc)));
|
||||||
@ -559,7 +603,13 @@ impl Schema {
|
|||||||
|
|
||||||
let mut map_arr = |arr: &Vec<Arc<Schema>>, sub: &str| {
|
let mut map_arr = |arr: &Vec<Arc<Schema>>, sub: &str| {
|
||||||
for (i, v) in arr.iter().enumerate() {
|
for (i, v) in arr.iter().enumerate() {
|
||||||
Self::collect_schemas(v, root_id, format!("{}/{}/{}", path, sub, i), to_insert, errors);
|
Self::collect_schemas(
|
||||||
|
v,
|
||||||
|
root_id,
|
||||||
|
format!("{}/{}/{}", path, sub, i),
|
||||||
|
to_insert,
|
||||||
|
errors,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -574,7 +624,7 @@ impl Schema {
|
|||||||
let mut map_opt = |opt: &Option<Arc<Schema>>, pass_path: bool, sub: &str| {
|
let mut map_opt = |opt: &Option<Arc<Schema>>, pass_path: bool, sub: &str| {
|
||||||
if let Some(v) = opt {
|
if let Some(v) = opt {
|
||||||
if pass_path {
|
if pass_path {
|
||||||
// Arrays explicitly push their wrapper natively.
|
// Arrays explicitly push their wrapper natively.
|
||||||
// 'items' becomes a transparent conduit, bypassing self-promotion and skipping the '/items' suffix.
|
// 'items' becomes a transparent conduit, bypassing self-promotion and skipping the '/items' suffix.
|
||||||
Self::collect_child_schemas(v, root_id, path.clone(), to_insert, errors);
|
Self::collect_child_schemas(v, root_id, path.clone(), to_insert, errors);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -473,6 +473,15 @@ impl<'a> Compiler<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ft) = r#type.field_types.as_ref().and_then(|v| v.as_object()) {
|
||||||
|
if let Some(pg_type) = ft.get(prop_key).and_then(|v| v.as_str()) {
|
||||||
|
if pg_type == "json" || pg_type == "jsonb" {
|
||||||
|
select_args.push(format!("'{}', {}.{}", prop_key, owner_alias, prop_key));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let child_node = Node {
|
let child_node = Node {
|
||||||
schema: std::sync::Arc::clone(prop_schema),
|
schema: std::sync::Arc::clone(prop_schema),
|
||||||
parent_alias: owner_alias.clone(),
|
parent_alias: owner_alias.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user