From 3bb7eb312a15c7d5e369a196f6a8bf12928e1b3d Mon Sep 17 00:00:00 2001 From: Alex Groleau Date: Thu, 9 Jul 2026 14:26:45 -0400 Subject: [PATCH] generator checkpoint --- fixtures/objectTypes.json | 3 + fixtures/queryer.json | 436 +++++++++++++++++++++++++++------- flow | 1 - src/database/compile/edges.rs | 63 +++-- src/database/compile/mod.rs | 3 + src/queryer/compiler.rs | 125 +++++++++- src/tests/fixtures.rs | 6 + src/tests/formatter.rs | 3 +- src/tests/mod.rs | 5 +- 9 files changed, 529 insertions(+), 116 deletions(-) diff --git a/fixtures/objectTypes.json b/fixtures/objectTypes.json index ed67d5f..dbd22d0 100644 --- a/fixtures/objectTypes.json +++ b/fixtures/objectTypes.json @@ -191,6 +191,9 @@ }, { "name": "request", + "field_types": { + "inv": "jsonb" + }, "schemas": { "request": { "type": "object", diff --git a/fixtures/queryer.json b/fixtures/queryer.json index fafb704..03c4d4b 100644 --- a/fixtures/queryer.json +++ b/fixtures/queryer.json @@ -1293,8 +1293,8 @@ " 'age', person_3.age", " )", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", - " JOIN agreego.person person_3", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", + " JOIN agreego.person person_3 ON person_3.id = organization_2.id", " WHERE", " NOT entity_1.archived", "))))" @@ -1336,15 +1336,15 @@ " 'number', phone_number_8.number", " )", " FROM agreego.entity entity_7", - " JOIN agreego.phone_number phone_number_8", + " JOIN agreego.phone_number phone_number_8 ON phone_number_8.id = entity_7.id", " WHERE", " NOT entity_7.archived", " AND relationship_5.target_id = entity_7.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_4", - " JOIN agreego.relationship relationship_5", - " JOIN agreego.contact contact_6", + " JOIN agreego.relationship relationship_5 ON relationship_5.id = entity_4.id", + " JOIN agreego.contact contact_6 ON contact_6.id = relationship_5.id", " WHERE", " NOT entity_4.archived", " AND relationship_5.target_type = 'phone_number'", @@ -1366,15 +1366,15 @@ " 'address', email_address_13.address", " )", " FROM agreego.entity entity_12", - " JOIN agreego.email_address email_address_13", + " JOIN agreego.email_address email_address_13 ON email_address_13.id = entity_12.id", " WHERE", " NOT entity_12.archived", " AND relationship_10.target_id = entity_12.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_9", - " JOIN agreego.relationship relationship_10", - " JOIN agreego.contact contact_11", + " JOIN agreego.relationship relationship_10 ON relationship_10.id = entity_9.id", + " JOIN agreego.contact contact_11 ON contact_11.id = relationship_10.id", " WHERE", " NOT entity_9.archived", " AND relationship_10.target_type = 'email_address'", @@ -1396,15 +1396,15 @@ " 'city', address_18.city", " )", " FROM agreego.entity entity_17", - " JOIN agreego.address address_18", + " JOIN agreego.address address_18 ON address_18.id = entity_17.id", " WHERE", " NOT entity_17.archived", " AND relationship_15.target_id = entity_17.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_14", - " JOIN agreego.relationship relationship_15", - " JOIN agreego.contact contact_16", + " JOIN agreego.relationship relationship_15 ON relationship_15.id = entity_14.id", + " JOIN agreego.contact contact_16 ON contact_16.id = relationship_15.id", " WHERE", " NOT entity_14.archived", " AND relationship_15.target_type = 'address'", @@ -1427,7 +1427,7 @@ " 'number', phone_number_23.number", " )", " FROM agreego.entity entity_22", - " JOIN agreego.phone_number phone_number_23", + " JOIN agreego.phone_number phone_number_23 ON phone_number_23.id = entity_22.id", " WHERE", " NOT entity_22.archived", " AND relationship_20.target_id = entity_22.id", @@ -1441,7 +1441,7 @@ " 'address', email_address_25.address", " )", " FROM agreego.entity entity_24", - " JOIN agreego.email_address email_address_25", + " JOIN agreego.email_address email_address_25 ON email_address_25.id = entity_24.id", " WHERE", " NOT entity_24.archived", " AND relationship_20.target_id = entity_24.id", @@ -1455,7 +1455,7 @@ " 'city', address_27.city", " )", " FROM agreego.entity entity_26", - " JOIN agreego.address address_27", + " JOIN agreego.address address_27 ON address_27.id = entity_26.id", " WHERE", " NOT entity_26.archived", " AND relationship_20.target_id = entity_26.id", @@ -1464,16 +1464,16 @@ " END", " )), '[]'::jsonb)", " FROM agreego.entity entity_19", - " JOIN agreego.relationship relationship_20", - " JOIN agreego.contact contact_21", + " JOIN agreego.relationship relationship_20 ON relationship_20.id = entity_19.id", + " JOIN agreego.contact contact_21 ON contact_21.id = relationship_20.id", " WHERE", " NOT entity_19.archived", " AND relationship_20.source_id = entity_1.id", " )", " )", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", - " JOIN agreego.person person_3", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", + " JOIN agreego.person person_3 ON person_3.id = organization_2.id", " WHERE", " NOT entity_1.archived", "))))" @@ -1586,7 +1586,7 @@ " 'number', phone_number_8.number", " )", " FROM agreego.entity entity_7", - " JOIN agreego.phone_number phone_number_8", + " JOIN agreego.phone_number phone_number_8 ON phone_number_8.id = entity_7.id", " WHERE", " NOT entity_7.archived", " AND phone_number_8.number ILIKE $32 #>> '{}'", @@ -1594,8 +1594,8 @@ " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_4", - " JOIN agreego.relationship relationship_5", - " JOIN agreego.contact contact_6", + " JOIN agreego.relationship relationship_5 ON relationship_5.id = entity_4.id", + " JOIN agreego.contact contact_6 ON contact_6.id = relationship_5.id", " WHERE", " NOT entity_4.archived", " AND relationship_5.target_type = 'phone_number'", @@ -1617,15 +1617,15 @@ " 'address', email_address_13.address", " )", " FROM agreego.entity entity_12", - " JOIN agreego.email_address email_address_13", + " JOIN agreego.email_address email_address_13 ON email_address_13.id = entity_12.id", " WHERE", " NOT entity_12.archived", " AND relationship_10.target_id = entity_12.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_9", - " JOIN agreego.relationship relationship_10", - " JOIN agreego.contact contact_11", + " JOIN agreego.relationship relationship_10 ON relationship_10.id = entity_9.id", + " JOIN agreego.contact contact_11 ON contact_11.id = relationship_10.id", " WHERE", " NOT entity_9.archived", " AND relationship_10.target_type = 'email_address'", @@ -1647,15 +1647,15 @@ " 'city', address_18.city", " )", " FROM agreego.entity entity_17", - " JOIN agreego.address address_18", + " JOIN agreego.address address_18 ON address_18.id = entity_17.id", " WHERE", " NOT entity_17.archived", " AND relationship_15.target_id = entity_17.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_14", - " JOIN agreego.relationship relationship_15", - " JOIN agreego.contact contact_16", + " JOIN agreego.relationship relationship_15 ON relationship_15.id = entity_14.id", + " JOIN agreego.contact contact_16 ON contact_16.id = relationship_15.id", " WHERE", " NOT entity_14.archived", " AND relationship_15.target_type = 'address'", @@ -1678,7 +1678,7 @@ " 'number', phone_number_23.number", " )", " FROM agreego.entity entity_22", - " JOIN agreego.phone_number phone_number_23", + " JOIN agreego.phone_number phone_number_23 ON phone_number_23.id = entity_22.id", " WHERE", " NOT entity_22.archived", " AND relationship_20.target_id = entity_22.id", @@ -1692,7 +1692,7 @@ " 'address', email_address_25.address", " )", " FROM agreego.entity entity_24", - " JOIN agreego.email_address email_address_25", + " JOIN agreego.email_address email_address_25 ON email_address_25.id = entity_24.id", " WHERE", " NOT entity_24.archived", " AND relationship_20.target_id = entity_24.id", @@ -1706,7 +1706,7 @@ " 'city', address_27.city", " )", " FROM agreego.entity entity_26", - " JOIN agreego.address address_27", + " JOIN agreego.address address_27 ON address_27.id = entity_26.id", " WHERE", " NOT entity_26.archived", " AND relationship_20.target_id = entity_26.id", @@ -1715,8 +1715,8 @@ " END", " )), '[]'::jsonb)", " FROM agreego.entity entity_19", - " JOIN agreego.relationship relationship_20", - " JOIN agreego.contact contact_21", + " JOIN agreego.relationship relationship_20 ON relationship_20.id = entity_19.id", + " JOIN agreego.contact contact_21 ON contact_21.id = relationship_20.id", " WHERE", " NOT entity_19.archived", " AND contact_21.is_primary = ($11 #>> '{}')::BOOLEAN", @@ -1724,8 +1724,8 @@ " )", " )", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", - " JOIN agreego.person person_3", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", + " JOIN agreego.person person_3 ON person_3.id = organization_2.id", " WHERE", " NOT entity_1.archived", " AND person_3.age = ($1 #>> '{}')::NUMERIC", @@ -1787,15 +1787,15 @@ " 'address', email_address_5.address", " )", " FROM agreego.entity entity_4", - " JOIN agreego.email_address email_address_5", + " JOIN agreego.email_address email_address_5 ON email_address_5.id = entity_4.id", " WHERE", " NOT entity_4.archived", " AND relationship_2.target_id = entity_4.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_1", - " JOIN agreego.relationship relationship_2", - " JOIN agreego.contact contact_3", + " JOIN agreego.relationship relationship_2 ON relationship_2.id = entity_1.id", + " JOIN agreego.contact contact_3 ON contact_3.id = relationship_2.id", " WHERE", " NOT entity_1.archived", " AND relationship_2.target_type = 'email_address'", @@ -1832,8 +1832,8 @@ " 'age', person_5.age", " )", " FROM agreego.entity entity_3", - " JOIN agreego.organization organization_4", - " JOIN agreego.person person_5", + " JOIN agreego.organization organization_4 ON organization_4.id = entity_3.id", + " JOIN agreego.person person_5 ON person_5.id = organization_4.id", " WHERE", " NOT entity_3.archived", " AND order_2.customer_id = person_5.id", @@ -1849,14 +1849,14 @@ " 'price', order_line_7.price", " )), '[]'::jsonb)", " FROM agreego.entity entity_6", - " JOIN agreego.order_line order_line_7", + " JOIN agreego.order_line order_line_7 ON order_line_7.id = entity_6.id", " WHERE", " NOT entity_6.archived", " AND order_line_7.order_id = order_2.id", " )", " )", " FROM agreego.entity entity_1", - " JOIN agreego.order order_2", + " JOIN agreego.order order_2 ON order_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -1881,7 +1881,7 @@ " 'name', organization_2.name", " )", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -1910,8 +1910,8 @@ " 'role', bot_5.role", " )", " FROM agreego.entity entity_3", - " JOIN agreego.organization organization_4", - " JOIN agreego.bot bot_5", + " JOIN agreego.organization organization_4 ON organization_4.id = entity_3.id", + " JOIN agreego.bot bot_5 ON bot_5.id = organization_4.id", " WHERE", " NOT entity_3.archived", " AND entity_3.id = entity_1.id", @@ -1925,7 +1925,7 @@ " 'name', organization_7.name", " )", " FROM agreego.entity entity_6", - " JOIN agreego.organization organization_7", + " JOIN agreego.organization organization_7 ON organization_7.id = entity_6.id", " WHERE", " NOT entity_6.archived", " AND entity_6.id = entity_1.id", @@ -1942,8 +1942,8 @@ " 'age', person_10.age", " )", " FROM agreego.entity entity_8", - " JOIN agreego.organization organization_9", - " JOIN agreego.person person_10", + " JOIN agreego.organization organization_9 ON organization_9.id = entity_8.id", + " JOIN agreego.person person_10 ON person_10.id = organization_9.id", " WHERE", " NOT entity_8.archived", " AND entity_8.id = entity_1.id", @@ -1951,7 +1951,7 @@ " ELSE NULL", " END), '[]'::jsonb)", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -1979,8 +1979,8 @@ " 'token', bot_5.token", " )", " FROM agreego.entity entity_3", - " JOIN agreego.organization organization_4", - " JOIN agreego.bot bot_5", + " JOIN agreego.organization organization_4 ON organization_4.id = entity_3.id", + " JOIN agreego.bot bot_5 ON bot_5.id = organization_4.id", " WHERE", " NOT entity_3.archived", " AND entity_3.id = entity_1.id", @@ -1996,8 +1996,8 @@ " 'last_name', person_8.last_name", " )", " FROM agreego.entity entity_6", - " JOIN agreego.organization organization_7", - " JOIN agreego.person person_8", + " JOIN agreego.organization organization_7 ON organization_7.id = entity_6.id", + " JOIN agreego.person person_8 ON person_8.id = organization_7.id", " WHERE", " NOT entity_6.archived", " AND entity_6.id = entity_1.id", @@ -2005,7 +2005,7 @@ " ELSE NULL", " END", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -2049,15 +2049,15 @@ " 'number', phone_number_10.number", " )", " FROM agreego.entity entity_9", - " JOIN agreego.phone_number phone_number_10", + " JOIN agreego.phone_number phone_number_10 ON phone_number_10.id = entity_9.id", " WHERE", " NOT entity_9.archived", " AND relationship_7.target_id = entity_9.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_6", - " JOIN agreego.relationship relationship_7", - " JOIN agreego.contact contact_8", + " JOIN agreego.relationship relationship_7 ON relationship_7.id = entity_6.id", + " JOIN agreego.contact contact_8 ON contact_8.id = relationship_7.id", " WHERE", " NOT entity_6.archived", " AND relationship_7.target_type = 'phone_number'", @@ -2079,15 +2079,15 @@ " 'address', email_address_15.address", " )", " FROM agreego.entity entity_14", - " JOIN agreego.email_address email_address_15", + " JOIN agreego.email_address email_address_15 ON email_address_15.id = entity_14.id", " WHERE", " NOT entity_14.archived", " AND relationship_12.target_id = entity_14.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_11", - " JOIN agreego.relationship relationship_12", - " JOIN agreego.contact contact_13", + " JOIN agreego.relationship relationship_12 ON relationship_12.id = entity_11.id", + " JOIN agreego.contact contact_13 ON contact_13.id = relationship_12.id", " WHERE", " NOT entity_11.archived", " AND relationship_12.target_type = 'email_address'", @@ -2109,15 +2109,15 @@ " 'city', address_20.city", " )", " FROM agreego.entity entity_19", - " JOIN agreego.address address_20", + " JOIN agreego.address address_20 ON address_20.id = entity_19.id", " WHERE", " NOT entity_19.archived", " AND relationship_17.target_id = entity_19.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_16", - " JOIN agreego.relationship relationship_17", - " JOIN agreego.contact contact_18", + " JOIN agreego.relationship relationship_17 ON relationship_17.id = entity_16.id", + " JOIN agreego.contact contact_18 ON contact_18.id = relationship_17.id", " WHERE", " NOT entity_16.archived", " AND relationship_17.target_type = 'address'", @@ -2140,7 +2140,7 @@ " 'number', phone_number_25.number", " )", " FROM agreego.entity entity_24", - " JOIN agreego.phone_number phone_number_25", + " JOIN agreego.phone_number phone_number_25 ON phone_number_25.id = entity_24.id", " WHERE", " NOT entity_24.archived", " AND relationship_22.target_id = entity_24.id", @@ -2154,7 +2154,7 @@ " 'address', email_address_27.address", " )", " FROM agreego.entity entity_26", - " JOIN agreego.email_address email_address_27", + " JOIN agreego.email_address email_address_27 ON email_address_27.id = entity_26.id", " WHERE", " NOT entity_26.archived", " AND relationship_22.target_id = entity_26.id", @@ -2168,7 +2168,7 @@ " 'city', address_29.city", " )", " FROM agreego.entity entity_28", - " JOIN agreego.address address_29", + " JOIN agreego.address address_29 ON address_29.id = entity_28.id", " WHERE", " NOT entity_28.archived", " AND relationship_22.target_id = entity_28.id", @@ -2177,16 +2177,16 @@ " END", " )), '[]'::jsonb)", " FROM agreego.entity entity_21", - " JOIN agreego.relationship relationship_22", - " JOIN agreego.contact contact_23", + " JOIN agreego.relationship relationship_22 ON relationship_22.id = entity_21.id", + " JOIN agreego.contact contact_23 ON contact_23.id = relationship_22.id", " WHERE", " NOT entity_21.archived", " AND relationship_22.source_id = entity_3.id", " )", " )", " FROM agreego.entity entity_3", - " JOIN agreego.organization organization_4", - " JOIN agreego.person person_5", + " JOIN agreego.organization organization_4 ON organization_4.id = entity_3.id", + " JOIN agreego.person person_5 ON person_5.id = organization_4.id", " WHERE", " NOT entity_3.archived", " AND entity_3.id = entity_1.id", @@ -2194,7 +2194,7 @@ " ELSE NULL", " END", " FROM agreego.entity entity_1", - " JOIN agreego.organization organization_2", + " JOIN agreego.organization organization_2 ON organization_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -2230,15 +2230,15 @@ " 'age', person_5.age", " )", " FROM agreego.entity entity_3", - " JOIN agreego.organization organization_4", - " JOIN agreego.person person_5", + " JOIN agreego.organization organization_4 ON organization_4.id = entity_3.id", + " JOIN agreego.person person_5 ON person_5.id = organization_4.id", " WHERE", " NOT entity_3.archived", " AND order_2.customer_id = person_5.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_1", - " JOIN agreego.order order_2", + " JOIN agreego.order order_2 ON order_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -2273,7 +2273,7 @@ " ELSE NULL", " END), '[]'::jsonb)", " FROM agreego.entity entity_1", - " JOIN agreego.widget widget_2", + " JOIN agreego.widget widget_2 ON widget_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -2306,7 +2306,7 @@ " 'price', invoice_line_4.price", " )), '[]'::jsonb)", " FROM agreego.entity entity_3", - " JOIN agreego.invoice_line invoice_line_4", + " JOIN agreego.invoice_line invoice_line_4 ON invoice_line_4.id = entity_3.id", " WHERE", " NOT entity_3.archived", " AND invoice_line_4.invoice_id = invoice_2.id", @@ -2317,7 +2317,7 @@ " 'metadata_nested_lines', invoice_2.metadata_nested_lines", " )", " FROM agreego.entity entity_1", - " JOIN agreego.invoice invoice_2", + " JOIN agreego.invoice invoice_2 ON invoice_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -2344,7 +2344,7 @@ " 'card_number', account_2.card_number", " )", " FROM agreego.entity entity_1", - " JOIN agreego.account account_2", + " JOIN agreego.account account_2 ON account_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", "))))" @@ -2381,8 +2381,8 @@ " 'role', bot_7.role", " )", " FROM agreego.entity entity_5", - " JOIN agreego.organization organization_6", - " JOIN agreego.bot bot_7", + " JOIN agreego.organization organization_6 ON organization_6.id = entity_5.id", + " JOIN agreego.bot bot_7 ON bot_7.id = organization_6.id", " WHERE", " NOT entity_5.archived", " AND entity_5.id = entity_3.id", @@ -2396,7 +2396,7 @@ " 'name', organization_9.name", " )", " FROM agreego.entity entity_8", - " JOIN agreego.organization organization_9", + " JOIN agreego.organization organization_9 ON organization_9.id = entity_8.id", " WHERE", " NOT entity_8.archived", " AND entity_8.id = entity_3.id", @@ -2413,8 +2413,8 @@ " 'age', person_12.age", " )", " FROM agreego.entity entity_10", - " JOIN agreego.organization organization_11", - " JOIN agreego.person person_12", + " JOIN agreego.organization organization_11 ON organization_11.id = entity_10.id", + " JOIN agreego.person person_12 ON person_12.id = organization_11.id", " WHERE", " NOT entity_10.archived", " AND entity_10.id = entity_3.id", @@ -2422,14 +2422,14 @@ " ELSE NULL", " END", " FROM agreego.entity entity_3", - " JOIN agreego.organization organization_4", + " JOIN agreego.organization organization_4 ON organization_4.id = entity_3.id", " WHERE", " NOT entity_3.archived", " AND order_2.counterparty_id = entity_3.id", " )", " )), '[]'::jsonb)", " FROM agreego.entity entity_1", - " JOIN agreego.order order_2", + " JOIN agreego.order order_2 ON order_2.id = entity_1.id", " WHERE", " NOT entity_1.archived", " AND order_2.counterparty_type IN ('bot', 'organization', 'person')", @@ -2439,5 +2439,273 @@ } } ] + }, + { + "description": "Static relation constraints queries", + "database": { + "puncs": [ + { + "name": "get_pet", + "schemas": { + "get_pet.response": { + "type": "full.pet" + } + } + } + ], + "enums": [], + "relations": [ + { + "type": "relation", + "id": "00000000-0000-0000-0000-000000000001", + "constraint": "fk_attachment_attachable_entity", + "source_type": "attachment", + "source_columns": [ + "attachable_id" + ], + "destination_type": "entity", + "destination_columns": [ + "id" + ], + "prefix": "attachable" + } + ], + "types": [ + { + "name": "entity", + "hierarchy": [ + "entity" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "archived" + ] + }, + "field_types": { + "id": "uuid", + "archived": "boolean", + "type": "text" + }, + "schemas": { + "entity": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "archived": { + "type": "boolean" + } + } + } + }, + "fields": [ + "id", + "type", + "archived" + ], + "variations": [ + "entity", + "pet", + "attachment" + ] + }, + { + "name": "pet", + "hierarchy": [ + "entity", + "pet" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "archived" + ], + "pet": [ + "name" + ] + }, + "field_types": { + "name": "text" + }, + "schemas": { + "pet": { + "type": "entity", + "properties": { + "name": { + "type": "string" + } + } + }, + "full.pet": { + "type": "pet", + "properties": { + "cover_attachment": { + "type": [ + "attachment", + "null" + ], + "properties": { + "kind": { + "const": "cover" + } + } + }, + "cover_attachments": { + "type": "array", + "items": { + "type": "attachment", + "properties": { + "kind": { + "const": "cover" + } + } + } + }, + "media_attachments": { + "type": "array", + "items": { + "type": "attachment", + "properties": { + "kind": { + "enum": [ + "cover", + "thumbnail" + ] + } + } + } + } + } + } + }, + "fields": [ + "id", + "type", + "archived", + "name" + ], + "variations": [ + "pet" + ] + }, + { + "name": "attachment", + "hierarchy": [ + "entity", + "attachment" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "archived" + ], + "attachment": [ + "attachable_id", + "name", + "kind" + ] + }, + "field_types": { + "attachable_id": "uuid", + "name": "text", + "kind": "text" + }, + "schemas": { + "attachment": { + "type": "entity", + "properties": { + "attachable_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "string" + } + } + } + }, + "fields": [ + "id", + "type", + "archived", + "attachable_id", + "name", + "kind" + ], + "variations": [ + "attachment" + ] + } + ] + }, + "tests": [ + { + "description": "Select query on pet with static properties constraints on attachments", + "action": "query", + "schema_id": "get_pet.response", + "expect": { + "success": true, + "sql": [ + [ + "((SELECT jsonb_strip_nulls((", + " SELECT jsonb_build_object(", + " 'name', pet_2.name,", + " 'cover_attachment', (", + " SELECT jsonb_build_object(", + " 'attachable_id', attachment_4.attachable_id,", + " 'name', attachment_4.name,", + " 'kind', attachment_4.kind", + " )", + " FROM agreego.entity entity_3", + " JOIN agreego.attachment attachment_4 ON attachment_4.id = entity_3.id", + " WHERE", + " NOT entity_3.archived", + " AND attachment_4.kind = 'cover'", + " ),", + " 'cover_attachments', (", + " SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'attachable_id', attachment_6.attachable_id,", + " 'name', attachment_6.name,", + " 'kind', attachment_6.kind", + " )), '[]'::jsonb)", + " FROM agreego.entity entity_5", + " JOIN agreego.attachment attachment_6 ON attachment_6.id = entity_5.id", + " WHERE", + " NOT entity_5.archived", + " AND attachment_6.kind = 'cover'", + " ),", + " 'media_attachments', (", + " SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'attachable_id', attachment_8.attachable_id,", + " 'name', attachment_8.name,", + " 'kind', attachment_8.kind", + " )), '[]'::jsonb)", + " FROM agreego.entity entity_7", + " JOIN agreego.attachment attachment_8 ON attachment_8.id = entity_7.id", + " WHERE", + " NOT entity_7.archived", + " AND attachment_8.kind IN ('cover', 'thumbnail')", + " )", + " )", + " FROM agreego.entity entity_1", + " JOIN agreego.pet pet_2 ON pet_2.id = entity_1.id", + " WHERE", + " NOT entity_1.archived", + "))))" + ] + ] + } + } + ] } ] \ No newline at end of file diff --git a/flow b/flow index 72e0a33..bf7c06a 100755 --- a/flow +++ b/flow @@ -36,7 +36,6 @@ pgrx-down() { info "Taking pgrx down..." } - build() { local version version=$(get-version) || return $? diff --git a/src/database/compile/edges.rs b/src/database/compile/edges.rs index 11073ac..4c1f4e7 100644 --- a/src/database/compile/edges.rs +++ b/src/database/compile/edges.rs @@ -29,10 +29,21 @@ impl Schema { 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()); + } else if let Some(t_or_arr) = &self.obj.type_ { + match t_or_arr { + crate::database::object::SchemaTypeOrArray::Single(t) => { + if !crate::database::object::is_primitive_type(t) { + parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string()); + } + } + crate::database::object::SchemaTypeOrArray::Multiple(types) => { + for t in types { + if !crate::database::object::is_primitive_type(t) { + parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string()); + break; + } + } + } } } @@ -46,13 +57,23 @@ impl Schema { 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(); + if let Some(t_or_arr) = &prop_schema.obj.type_ { + match t_or_arr { + crate::database::object::SchemaTypeOrArray::Single(t) => { + if t == "array" { + is_array = true; + if let Some(items) = &prop_schema.obj.items { + target_schema = items.clone(); + } + } + } + crate::database::object::SchemaTypeOrArray::Multiple(types) => { + if types.contains(&"array".to_string()) { + is_array = true; + if let Some(items) = &prop_schema.obj.items { + target_schema = items.clone(); + } + } } } } @@ -60,11 +81,21 @@ impl Schema { // 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(t_or_arr) = &target_schema.obj.type_ { + match t_or_arr { + crate::database::object::SchemaTypeOrArray::Single(t) => { + if !crate::database::object::is_primitive_type(t) { + child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string()); + } + } + crate::database::object::SchemaTypeOrArray::Multiple(types) => { + for t in types { + if !crate::database::object::is_primitive_type(t) { + child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string()); + break; + } + } + } } } else if let Some(arr) = &target_schema.obj.one_of { if let Some(first) = arr.first() { diff --git a/src/database/compile/mod.rs b/src/database/compile/mod.rs index 26a3218..5115ebc 100644 --- a/src/database/compile/mod.rs +++ b/src/database/compile/mod.rs @@ -77,6 +77,9 @@ impl Schema { if !crate::database::object::is_primitive_type(t) && !t.starts_with('$') { if let Some(parent) = db.schemas.get(t).cloned() { parent.as_ref().compile(db, t, t.clone(), errors); + if let Some(p_props) = parent.obj.compiled_properties.get() { + props.extend(p_props.clone()); + } } } } diff --git a/src/queryer/compiler.rs b/src/queryer/compiler.rs index 9108454..eef1d87 100644 --- a/src/queryer/compiler.rs +++ b/src/queryer/compiler.rs @@ -1,5 +1,6 @@ use crate::database::Database; use indexmap::IndexMap; +use serde_json::Value; use std::sync::Arc; pub struct Compiler<'a> { @@ -86,13 +87,29 @@ impl<'a> Compiler<'a> { .next_back() .unwrap_or(family_target); resolved_type = self.db.types.get(base_type_name); - } else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &items.obj.type_ - { - if !crate::database::object::is_primitive_type(t) { - resolved_type = self - .db - .types - .get(&t.split('.').next_back().unwrap_or(t).to_string()); + } else if let Some(t_or_arr) = &items.obj.type_ { + match t_or_arr { + crate::database::object::SchemaTypeOrArray::Single(t) => { + if !crate::database::object::is_primitive_type(t) { + resolved_type = self + .db + .types + .get(&t.split('.').next_back().unwrap_or(t).to_string()); + } + } + crate::database::object::SchemaTypeOrArray::Multiple(types) => { + for t in types { + if !crate::database::object::is_primitive_type(t) { + resolved_type = self + .db + .types + .get(&t.split('.').next_back().unwrap_or(t).to_string()); + if resolved_type.is_some() { + break; + } + } + } + } } } } @@ -135,12 +152,25 @@ impl<'a> Compiler<'a> { .next_back() .unwrap_or(family_target); resolved_type = self.db.types.get(base_type_name); - } else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = - &node.schema.obj.type_ - { - if !crate::database::object::is_primitive_type(t) { - let base_type_name = t.split('.').next_back().unwrap_or(t).to_string(); - resolved_type = self.db.types.get(&base_type_name); + } else if let Some(t_or_arr) = &node.schema.obj.type_ { + match t_or_arr { + crate::database::object::SchemaTypeOrArray::Single(t) => { + if !crate::database::object::is_primitive_type(t) { + let base_type_name = t.split('.').next_back().unwrap_or(t).to_string(); + resolved_type = self.db.types.get(&base_type_name); + } + } + crate::database::object::SchemaTypeOrArray::Multiple(types) => { + for t in types { + if !crate::database::object::is_primitive_type(t) { + let base_type_name = t.split('.').next_back().unwrap_or(t).to_string(); + resolved_type = self.db.types.get(&base_type_name); + if resolved_type.is_some() { + break; + } + } + } + } } } } @@ -439,6 +469,9 @@ impl<'a> Compiler<'a> { Some(crate::database::object::SchemaTypeOrArray::Single(s)) => { !crate::database::object::is_primitive_type(s) } + Some(crate::database::object::SchemaTypeOrArray::Multiple(v)) => { + v.iter().any(|s| !crate::database::object::is_primitive_type(s)) + } _ => false, }; @@ -551,6 +584,7 @@ impl<'a> Compiler<'a> { self.compile_filter_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses); self.compile_polymorphic_bounds(r#type, type_aliases, &node, &mut where_clauses); + self.compile_static_property_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses); let start_len = where_clauses.len(); self.compile_relation_conditions( @@ -865,4 +899,69 @@ impl<'a> Compiler<'a> { } Ok(()) } + + fn compile_static_property_conditions( + &self, + r#type: &crate::database::r#type::Type, + type_aliases: &std::collections::HashMap, + node: &Node, + base_alias: &str, + where_clauses: &mut Vec, + ) { + if let Some(props) = node.schema.obj.properties.as_ref() { + for (prop_name, prop_schema) in props { + let filter_alias = Self::resolve_filter_alias(r#type, type_aliases, base_alias, prop_name); + if let Some(const_val) = prop_schema.obj.const_.as_ref() { + let sql_val = Self::quote_literal(const_val); + where_clauses.push(format!("{}.{} = {}", filter_alias, prop_name, sql_val)); + } + if let Some(enum_vals) = prop_schema.obj.enum_.as_ref() { + if !enum_vals.is_empty() { + let sql_vals: Vec = enum_vals + .iter() + .map(|v| Self::quote_literal(v)) + .collect(); + where_clauses.push(format!( + "{}.{} IN ({})", + filter_alias, + prop_name, + sql_vals.join(", ") + )); + } + } + } + } + } + + fn quote_literal(val: &Value) -> String { + match val { + Value::Null => "NULL".to_string(), + Value::Bool(b) => { + if *b { + "true".to_string() + } else { + "false".to_string() + } + } + Value::Number(n) => { + if let Some(f) = n.as_f64() { + if f.fract() == 0.0 { + return f.trunc().to_string(); + } + } + n.to_string() + } + Value::String(s) => { + if s.is_empty() { + "NULL".to_string() + } else { + format!("'{}'", s.replace('\'', "''")) + } + } + _ => format!( + "'{}'", + serde_json::to_string(val).unwrap().replace('\'', "''") + ), + } + } } diff --git a/src/tests/fixtures.rs b/src/tests/fixtures.rs index b22d10c..ab10a57 100644 --- a/src/tests/fixtures.rs +++ b/src/tests/fixtures.rs @@ -1301,6 +1301,12 @@ fn test_queryer_0_15() { crate::tests::runner::run_test_case(&path, 0, 15).unwrap(); } +#[test] +fn test_queryer_1_0() { + let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 1, 0).unwrap(); +} + #[test] fn test_polymorphism_0_0() { let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR")); diff --git a/src/tests/formatter.rs b/src/tests/formatter.rs index f81c963..5b062a6 100644 --- a/src/tests/formatter.rs +++ b/src/tests/formatter.rs @@ -268,7 +268,8 @@ impl SqlFormatter { match &join.join_operator { JoinOperator::Inner(JoinConstraint::On(expr)) | JoinOperator::Left(JoinConstraint::On(expr)) - | JoinOperator::Right(JoinConstraint::On(expr)) => { + | JoinOperator::Right(JoinConstraint::On(expr)) + | JoinOperator::Join(JoinConstraint::On(expr)) => { self.push_str(" ON "); self.format_expr(expr); } diff --git a/src/tests/mod.rs b/src/tests/mod.rs index fcee8b2..1c43021 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -157,7 +157,10 @@ fn test_library_api() { } }, "name": { "type": ["string.condition", "null"] }, - "target": { "type": ["target_schema.filter", "null"] }, + "target": { + "type": ["target_schema.filter", "null"], + "compiledPropertyNames": ["value", "$and", "$or"] + }, "type": { "type": ["string.condition", "null"] } }, "type": "filter"