From 24adf3ffc69bc016f40d12fdee3b9ae38f52918d Mon Sep 17 00:00:00 2001 From: Alex Groleau Date: Tue, 14 Apr 2026 13:06:53 -0400 Subject: [PATCH] checkpoint --- src/database/schema.rs | 160 ++++++++++++++++++++++++++-------------- src/queryer/compiler.rs | 9 +++ 2 files changed, 114 insertions(+), 55 deletions(-) diff --git a/src/database/schema.rs b/src/database/schema.rs index 1ba2b62..7ad9285 100644 --- a/src/database/schema.rs +++ b/src/database/schema.rs @@ -161,12 +161,24 @@ impl Schema { } if let Some(one_of) = &self.obj.one_of { 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 { 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 { @@ -178,13 +190,31 @@ impl Schema { if let Some(cases) = &self.obj.cases { for (i, c) in cases.iter().enumerate() { 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 { - 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_ { - 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 { // 1. Explicit horizontal routing 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 let base_type_name = path.split('.').next_back().unwrap_or(path).to_string(); if db.types.contains_key(&base_type_name) { @@ -234,7 +264,7 @@ impl Schema { } 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 .split('.') .next_back() @@ -247,9 +277,17 @@ impl Schema { if let Some(p_type) = parent_type_name { // 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 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 target_schema = prop_schema.clone(); let mut is_array = false; @@ -290,7 +328,13 @@ impl Schema { 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), 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() { let keys_for_ambiguity: Vec = compiled_target_props.keys().cloned().collect(); @@ -379,7 +423,7 @@ impl Schema { for c in one_of { let mut child_id = String::new(); let mut child_is_primitive = false; - + if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ { if crate::database::object::is_primitive_type(t) { child_is_primitive = true; @@ -389,41 +433,41 @@ impl Schema { structural_types.insert("object".to_string()); } } else { - disjoint_base = false; + disjoint_base = false; } if !child_is_primitive { - if let Some(t_val) = c.obj.get_discriminator_value("type", &child_id) { - type_vals.insert(t_val); - } - if let Some(k_val) = c.obj.get_discriminator_value("kind", &child_id) { - kind_vals.insert(k_val); - } + if let Some(t_val) = c.obj.get_discriminator_value("type", &child_id) { + type_vals.insert(t_val); + } + if let Some(k_val) = c.obj.get_discriminator_value("kind", &child_id) { + kind_vals.insert(k_val); + } } } if disjoint_base && structural_types.len() == one_of.len() { - strategy = "".to_string(); - for (i, c) in one_of.iter().enumerate() { - if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ { - if crate::database::object::is_primitive_type(t) { - options.insert(t.clone(), (Some(i), None)); - } else { - options.insert("object".to_string(), (Some(i), None)); - } - } - } + strategy = "".to_string(); + for (i, c) in one_of.iter().enumerate() { + if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &c.obj.type_ { + if crate::database::object::is_primitive_type(t) { + options.insert(t.clone(), (Some(i), None)); + } else { + options.insert("object".to_string(), (Some(i), None)); + } + } + } } else { - strategy = if type_vals.len() > 1 && type_vals.len() == one_of.len() { - "type".to_string() - } else if kind_vals.len() > 1 && kind_vals.len() == one_of.len() { - "kind".to_string() - } else { - "".to_string() - }; + strategy = if type_vals.len() > 1 && type_vals.len() == one_of.len() { + "type".to_string() + } else if kind_vals.len() > 1 && kind_vals.len() == one_of.len() { + "kind".to_string() + } else { + "".to_string() + }; - if strategy.is_empty() { - errors.push(crate::drop::Error { + if strategy.is_empty() { + errors.push(crate::drop::Error { code: "AMBIGUOUS_POLYMORPHISM".to_string(), message: format!("oneOf boundaries must map mathematically unique 'type' or 'kind' discriminators, or strictly contain disjoint primitive types."), details: crate::drop::ErrorDetails { @@ -432,20 +476,20 @@ impl Schema { ..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() { - 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(); - } - } - - if let Some(val) = c.obj.get_discriminator_value(&strategy, &child_id) { - if options.contains_key(&val) { - errors.push(crate::drop::Error { + 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(), message: format!("Polymorphic boundary defines multiple candidates mapped to the identical discriminator value '{}'.", val), details: crate::drop::ErrorDetails { @@ -454,12 +498,12 @@ impl Schema { ..Default::default() } }); - continue; - } - - options.insert(val, (Some(i), None)); + continue; } + + options.insert(val, (Some(i), None)); } + } } } else { return; @@ -521,7 +565,7 @@ impl Schema { } } else if !crate::database::object::is_primitive_type(t) { Self::validate_identifier(t, "type", root_id, &path, errors); - + // Is this an explicit inline ad-hoc composition? if schema_arc.obj.properties.is_some() || schema_arc.obj.cases.is_some() { to_insert.push((path.clone(), Arc::clone(schema_arc))); @@ -559,7 +603,13 @@ impl Schema { let mut map_arr = |arr: &Vec>, sub: &str| { 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>, pass_path: bool, sub: &str| { if let Some(v) = opt { 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. Self::collect_child_schemas(v, root_id, path.clone(), to_insert, errors); } else { diff --git a/src/queryer/compiler.rs b/src/queryer/compiler.rs index 75c1aac..0473257 100644 --- a/src/queryer/compiler.rs +++ b/src/queryer/compiler.rs @@ -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 { schema: std::sync::Arc::clone(prop_schema), parent_alias: owner_alias.clone(),