From 6cebce3740bceef96795c50f3a39c68146ebd76e Mon Sep 17 00:00:00 2001 From: Satya Date: Tue, 7 Jul 2026 21:38:08 -0400 Subject: [PATCH] =?UTF-8?q?queryer:=20array=20containment=20fires=20on=20p?= =?UTF-8?q?unc-response=20paths=20=E2=80=94=20compile=5Farray=20hands=20th?= =?UTF-8?q?e=20entity=20node=20the=20ITEMS=20PROXY=20as=20its=20schema=20(?= =?UTF-8?q?no=20properties),=20so=20property-shape=20checks=20that=20read?= =?UTF-8?q?=20node.schema=20silently=20missed;=20fall=20back=20to=20the=20?= =?UTF-8?q?type's=20own=20schema=20(Type.schemas[name]).=20Fixture=20adds?= =?UTF-8?q?=20get=5Fpeople=20punc=20+=20containment/cast=20filters=20throu?= =?UTF-8?q?gh=20it.=20Live=20case:=20get=5Fvendors=20trade=20filter=20stil?= =?UTF-8?q?l=20compiled=20=3D::jsonb=20after=201.0.174.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fixtures/queryer.json | 60 +++++++++++++++++++++++++++++++++++++++++ src/queryer/compiler.rs | 18 ++++++++++--- src/tests/fixtures.rs | 6 +++++ 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/fixtures/queryer.json b/fixtures/queryer.json index 7d9ae2e..1346153 100644 --- a/fixtures/queryer.json +++ b/fixtures/queryer.json @@ -70,6 +70,17 @@ } } } + }, + { + "name": "get_people", + "schemas": { + "get_people.response": { + "type": "array", + "items": { + "type": "person" + } + } + } } ], "enums": [], @@ -2731,6 +2742,55 @@ ] ] } + }, + { + "description": "Punc response items proxy: jsonb array containment filter falls back to the type schema", + "action": "query", + "schema_id": "get_people.response", + "filter": { + "tags": { + "$eq": "landscaping" + }, + "labels": { + "$of": [ + "vip", + "board" + ] + }, + "rating": { + "$gte": 4.0 + } + }, + "expect": { + "success": true, + "sql": [ + [ + "((SELECT jsonb_strip_nulls((", + " SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'id', entity_1.id,", + " 'type', entity_1.type,", + " 'archived', entity_1.archived,", + " 'created_at', entity_1.created_at,", + " 'name', organization_2.name,", + " 'first_name', person_3.first_name,", + " 'last_name', person_3.last_name,", + " 'age', person_3.age,", + " 'rating', person_3.rating,", + " 'tags', person_3.tags,", + " 'labels', person_3.labels", + " )), '[]'::jsonb)", + " FROM agreego.entity entity_1", + " JOIN agreego.organization organization_2", + " JOIN agreego.person person_3", + " WHERE", + " NOT entity_1.archived", + " AND person_3.labels ?| ARRAY()", + " AND person_3.rating >= ($2 #>> '{}')::NUMERIC", + " AND person_3.tags ? ($3 #>> '{)", + " ))))" + ] + ] + } } ] } diff --git a/src/queryer/compiler.rs b/src/queryer/compiler.rs index 2330432..1364ca9 100644 --- a/src/queryer/compiler.rs +++ b/src/queryer/compiler.rs @@ -762,14 +762,26 @@ impl<'a> Compiler<'a> { .and_then(|v| v.as_str()) == Some("jsonb"); if is_jsonb { - // Compiled schemas normalize to the list form ("type": ["array"]), - // hand-written ones may use the single form — accept both - let is_array_prop = node + // The node schema may be a punc-response proxy without properties — + // fall back to the type's own schema. Compiled schemas normalize to + // the list form ("type": ["array"]), hand-written ones may use the + // single form — accept both. + let prop_schema = node .schema .obj .properties .as_ref() .and_then(|p| p.get(field_name)) + .cloned() + .or_else(|| { + r#type + .schemas + .get(&r#type.name) + .and_then(|s| s.obj.properties.as_ref()) + .and_then(|p| p.get(field_name)) + .cloned() + }); + let is_array_prop = prop_schema .map(|ps| match &ps.obj.type_ { Some(crate::database::object::SchemaTypeOrArray::Single(t)) => t == "array", Some(crate::database::object::SchemaTypeOrArray::Multiple(ts)) => { diff --git a/src/tests/fixtures.rs b/src/tests/fixtures.rs index 5ac8af2..c81b5a8 100644 --- a/src/tests/fixtures.rs +++ b/src/tests/fixtures.rs @@ -1307,6 +1307,12 @@ fn test_queryer_0_16() { crate::tests::runner::run_test_case(&path, 0, 16).unwrap(); } +#[test] +fn test_queryer_0_17() { + let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 0, 17).unwrap(); +} + #[test] fn test_polymorphism_0_0() { let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));