diff --git a/Cargo.lock b/Cargo.lock index 4a7a297..6b676ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,15 @@ version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -874,6 +883,7 @@ dependencies = [ "regex-syntax", "serde", "serde_json", + "sqlparser", "url", "uuid", "xxhash-rust", @@ -1040,6 +1050,15 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1377,6 +1396,16 @@ dependencies = [ "unarray", ] +[[package]] +name = "psm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +dependencies = [ + "ar_archive_writer", + "cc", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1442,6 +1471,26 @@ dependencies = [ "rand_core", ] +[[package]] +name = "recursive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" +dependencies = [ + "recursive-proc-macro-impl", + "stacker", +] + +[[package]] +name = "recursive-proc-macro-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1669,12 +1718,35 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "sqlparser" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf5ea8d4d7c808e1af1cbabebca9a2abe603bcefc22294c5b95018d53200cb7" +dependencies = [ + "log", + "recursive", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stacker" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + [[package]] name = "stringprep" version = "0.1.5" @@ -2323,6 +2395,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.60.2" diff --git a/Cargo.toml b/Cargo.toml index 383cca2..c948a72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ indexmap = { version = "2.13.0", features = ["serde"] } moka = { version = "0.12.14", features = ["sync"] } xxhash-rust = { version = "0.8.15", features = ["xxh64"] } dashmap = "6.1.0" +sqlparser = "0.61.0" [dev-dependencies] pgrx-tests = "0.16.1" @@ -38,6 +39,10 @@ crate-type = ["cdylib", "lib"] name = "pgrx_embed_jspg" path = "src/bin/pgrx_embed.rs" +[[bin]] +name = "ast_explore" +path = "src/bin/ast_explore.rs" + [features] default = ["pg18"] pg18 = ["pgrx/pg18", "pgrx-tests/pg18" ] diff --git a/fix_queryer.py b/fix_queryer.py deleted file mode 100644 index c204bec..0000000 --- a/fix_queryer.py +++ /dev/null @@ -1,136 +0,0 @@ -import json - -file_path = "fixtures/queryer.json" -with open(file_path, "r") as f: - data = json.load(f) - -# Find the test case -test_case = next(tc for tc in data if tc.get("description") == "Base entity family select on polymorphic tree") - -# The sql is an array of strings -expected_sql = test_case["expect"]["sql"][0] - -# We need to extract the entity block and move it -# The blocks are delimited by " WHEN t1_obj_t1.type = " -# Actually, the easiest way is to re-build the expected_sql from the actual output logged, -# or simply swap the entity block in the array strings. -# But since we just want to reorder the WHEN clauses... - -# Let's just fix it by string manipulation or we can just replace the whole expect[sql][0] block -new_sql = [ - "(SELECT jsonb_build_object(", - " 'id', t1_obj_t1.id,", - " 'type', CASE WHEN t1_obj_t1.type = 'address' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'city', t1_obj_t1_obj_t1.city,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.address t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'contact' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t3.archived,", - " 'created_at', t1_obj_t1_obj_t3.created_at,", - " 'id', t1_obj_t1_obj_t3.id,", - " 'is_primary', t1_obj_t1_obj_t1.is_primary,", - " 'name', t1_obj_t1_obj_t3.name,", - " 'type', t1_obj_t1_obj_t3.type)", - " FROM agreego.contact t1_obj_t1_obj_t1", - " JOIN agreego.relationship t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " JOIN agreego.entity t1_obj_t1_obj_t3 ON t1_obj_t1_obj_t3.id = t1_obj_t1_obj_t2.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'email_address' THEN ((SELECT jsonb_build_object(", - " 'address', t1_obj_t1_obj_t1.address,", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.email_address t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'entity' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t1.archived,", - " 'created_at', t1_obj_t1_obj_t1.created_at,", - " 'id', t1_obj_t1_obj_t1.id,", - " 'name', t1_obj_t1_obj_t1.name,", - " 'type', t1_obj_t1_obj_t1.type)", - " FROM agreego.entity t1_obj_t1_obj_t1", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'order' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'customer_id', t1_obj_t1_obj_t1.customer_id,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'total', t1_obj_t1_obj_t1.total,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.order t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'order_line' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'order_id', t1_obj_t1_obj_t1.order_id,", - " 'price', t1_obj_t1_obj_t1.price,", - " 'product', t1_obj_t1_obj_t1.product,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.order_line t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'organization' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.organization t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'person' THEN ((SELECT jsonb_build_object(", - " 'age', t1_obj_t1_obj_t1.age,", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'first_name', t1_obj_t1_obj_t1.first_name,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'last_name', t1_obj_t1_obj_t1.last_name,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.person t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'phone_number' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'number', t1_obj_t1_obj_t1.number,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.phone_number t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'relationship' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.relationship t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " ELSE NULL END)", - "FROM agreego.entity t1_obj_t1", - "WHERE NOT t1_obj_t1.archived)" -] - -test_case["expect"]["sql"][0] = new_sql - -with open(file_path, "w") as f: - json.dump(data, f, indent=4) - -print("Fixed queryer.json expected sql array ordering.") - diff --git a/fixtures/queryer.json b/fixtures/queryer.json index df833fd..4cf1249 100644 --- a/fixtures/queryer.json +++ b/fixtures/queryer.json @@ -1,1630 +1,1630 @@ [ - { - "description": "Queryer Execution", - "database": { - "puncs": [ - { - "name": "get_entities", - "schemas": [ - { - "$id": "get_entities.response", - "$family": "entity" - } - ] - } - ], - "enums": [], - "relations": [ - { - "type": "relation", - "id": "00000000-0000-0000-0000-000000000001", - "constraint": "fk_relationship_source_entity", - "source_type": "relationship", - "source_columns": [ - "source_id", - "source_type" - ], - "destination_type": "entity", - "destination_columns": [ - "id", - "type" - ], - "prefix": "source" - }, - { - "type": "relation", - "id": "00000000-0000-0000-0000-000000000002", - "constraint": "fk_relationship_target_entity", - "source_type": "relationship", - "source_columns": [ - "target_id", - "target_type" - ], - "destination_type": "entity", - "destination_columns": [ - "id", - "type" - ], - "prefix": "target" - }, - { - "id": "22222222-2222-2222-2222-222222222222", - "type": "relation", - "constraint": "fk_order_customer", - "source_type": "order", - "source_columns": [ - "customer_id" - ], - "destination_type": "person", - "destination_columns": [ - "id" - ], - "prefix": "customer" - }, - { - "id": "33333333-3333-3333-3333-333333333333", - "type": "relation", - "constraint": "fk_order_line_order", - "source_type": "order_line", - "source_columns": [ - "order_id" - ], - "destination_type": "order", - "destination_columns": [ - "id" - ], - "prefix": "lines" - } - ], - "types": [ - { - "name": "entity", - "hierarchy": [ - "entity" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ] - }, - "field_types": { - "id": "uuid", - "name": "text", - "archived": "boolean", - "created_at": "timestamptz", - "type": "text" - }, - "schemas": [ - { - "$id": "entity", - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "archived": { - "type": "boolean" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "created": { - "type": "boolean" - } - } - } - ], - "fields": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "variations": [ - "address", - "contact", - "email_address", - "entity", - "order", - "order_line", - "organization", - "person", - "phone_number", - "relationship" - ] - }, - { - "name": "person", - "hierarchy": [ - "person", - "entity" - ], - "fields": [ - "first_name", - "last_name", - "id", - "type", - "name", - "age", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "person": [ - "first_name", - "last_name", - "age" - ] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "first_name": "text", - "last_name": "text", - "name": "text", - "age": "numeric", - "created_at": "timestamptz" - }, - "schemas": [ - { - "$id": "base.person", - "$ref": "entity", - "properties": { - "first_name": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "age": { - "type": "number" - } - } - }, - { - "$id": "full.person", - "$ref": "base.person", - "properties": { - "phone_numbers": { - "type": "array", - "items": { - "$ref": "contact", - "properties": { - "target": { - "$ref": "phone_number" - } - } - } - }, - "email_addresses": { - "type": "array", - "items": { - "$ref": "contact", - "properties": { - "target": { - "$ref": "email_address" - } - } - } - }, - "addresses": { - "type": "array", - "items": { - "$ref": "contact", - "properties": { - "target": { - "$ref": "address" - } - } - } - }, - "contacts": { - "type": "array", - "items": { - "$ref": "contact", - "properties": { - "target": { - "oneOf": [ - { - "$ref": "phone_number" - }, - { - "$ref": "email_address" - }, - { - "$ref": "address" - } - ] - } - } - } - } - } - } - ], - "variations": [ - "person" - ] - }, - { - "name": "relationship", - "relationship": true, - "hierarchy": [ - "relationship", - "entity" - ], - "fields": [ - "source_id", - "source_type", - "target_id", - "target_type", - "id", - "type", - "name", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "relationship": [ - "source_id", - "source_type", - "target_id", - "target_type" - ] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "source_id": "uuid", - "source_type": "text", - "target_id": "uuid", - "target_type": "text", - "name": "text", - "created_at": "timestamptz" - }, - "schemas": [ - { - "$id": "relationship", - "$ref": "entity", - "properties": {} - } - ], - "variations": [ - "contact", - "relationship" - ] - }, - { - "name": "contact", - "relationship": true, - "hierarchy": [ - "contact", - "relationship", - "entity" - ], - "fields": [ - "is_primary", - "source_id", - "source_type", - "target_id", - "target_type", - "id", - "type", - "name", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "relationship": [ - "source_id", - "source_type", - "target_id", - "target_type" - ], - "contact": [ - "is_primary" - ] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "source_id": "uuid", - "source_type": "text", - "target_id": "uuid", - "target_type": "text", - "is_primary": "boolean", - "name": "text", - "created_at": "timestamptz" - }, - "schemas": [ - { - "$id": "contact", - "$ref": "relationship", - "properties": { - "is_primary": { - "type": "boolean" - } - } - } - ], - "variations": [ - "contact" - ] - }, - { - "name": "phone_number", - "hierarchy": [ - "phone_number", - "entity" - ], - "fields": [ - "number", - "id", - "type", - "name", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "phone_number": [ - "number" - ] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "number": "text", - "name": "text", - "created_at": "timestamptz" - }, - "schemas": [ - { - "$id": "phone_number", - "$ref": "entity", - "properties": { - "number": { - "type": "string" - } - } - } - ], - "variations": [ - "phone_number" - ] - }, - { - "name": "email_address", - "hierarchy": [ - "email_address", - "entity" - ], - "fields": [ - "address", - "id", - "type", - "name", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "email_address": [ - "address" - ] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "address": "text", - "name": "text", - "created_at": "timestamptz" - }, - "schemas": [ - { - "$id": "email_address", - "$ref": "entity", - "properties": { - "address": { - "type": "string" - } - } - } - ], - "variations": [ - "email_address" - ] - }, - { - "name": "address", - "hierarchy": [ - "address", - "entity" - ], - "fields": [ - "city", - "id", - "type", - "name", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "address": [ - "city" - ] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "city": "text", - "name": "text", - "created_at": "timestamptz" - }, - "schemas": [ - { - "$id": "address", - "$ref": "entity", - "properties": { - "city": { - "type": "string" - } - } - } - ], - "variations": [ - "address" - ] - }, - { - "name": "order", - "schemas": [ - { - "$id": "order", - "$ref": "entity", - "properties": { - "total": { - "type": "number" - }, - "customer_id": { - "type": "string" - } - } - }, - { - "$id": "full.order", - "$ref": "order", - "properties": { - "customer": { - "$ref": "base.person" - }, - "lines": { - "type": "array", - "items": { - "$ref": "order_line" - } - } - } - } - ], - "hierarchy": [ - "order", - "entity" - ], - "fields": [ - "id", - "type", - "name", - "total", - "customer_id", - "created_at", - "created_by", - "modified_at", - "modified_by", - "archived" - ], - "grouped_fields": { - "order": [ - "id", - "type", - "total", - "customer_id" - ], - "entity": [ - "id", - "type", - "name", - "created_at", - "created_by", - "modified_at", - "modified_by", - "archived" - ] - }, - "lookup_fields": [ - "id" - ], - "historical": true, - "relationship": false, - "field_types": { - "id": "uuid", - "type": "text", - "name": "text", - "archived": "boolean", - "total": "numeric", - "customer_id": "uuid", - "created_at": "timestamptz", - "created_by": "uuid", - "modified_at": "timestamptz", - "modified_by": "uuid" - }, - "variations": [ - "order" - ] - }, - { - "name": "order_line", - "schemas": [ - { - "$id": "order_line", - "$ref": "entity", - "properties": { - "order_id": { - "type": "string" - }, - "product": { - "type": "string" - }, - "price": { - "type": "number" - } - } - } - ], - "hierarchy": [ - "order_line", - "entity" - ], - "fields": [ - "id", - "type", - "name", - "order_id", - "product", - "price", - "created_at", - "created_by", - "modified_at", - "modified_by", - "archived" - ], - "grouped_fields": { - "order_line": [ - "id", - "type", - "order_id", - "product", - "price" - ], - "entity": [ - "id", - "type", - "name", - "created_at", - "created_by", - "modified_at", - "modified_by", - "archived" - ] - }, - "lookup_fields": [], - "historical": true, - "relationship": false, - "field_types": { - "id": "uuid", - "type": "text", - "name": "text", - "archived": "boolean", - "order_id": "uuid", - "product": "text", - "price": "numeric", - "created_at": "timestamptz", - "created_by": "uuid", - "modified_at": "timestamptz", - "modified_by": "uuid" - }, - "variations": [ - "order_line" - ] - }, - { - "name": "organization", - "hierarchy": [ - "organization", - "entity" - ], - "fields": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "grouped_fields": { - "entity": [ - "id", - "type", - "name", - "archived", - "created_at" - ], - "organization": [] - }, - "field_types": { - "id": "uuid", - "type": "text", - "archived": "boolean", - "name": "text", - "created_at": "timestamptz" - }, - "lookup_fields": [ - "id" - ], - "null_fields": [], - "default_fields": [ - "id", - "type", - "created_at", - "archived" - ], - "variations": [ - "organization" - ] - } - ], - "schemas": [ - { - "$id": "entity", - "type": "object", - "properties": {} - }, - { - "$id": "organization", - "type": "object", - "$ref": "entity", - "properties": {} - }, - { - "$id": "person", - "type": "object", - "$ref": "base.person", - "properties": {} - } - ] - }, - "tests": [ + { + "description": "Queryer Execution", + "database": { + "puncs": [ + { + "name": "get_entities", + "schemas": [ { - "description": "Simple entity select", - "action": "query", - "schema_id": "entity", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'archived', t1_obj_t1.archived,", - " 'created_at', t1_obj_t1.created_at,", - " 'id', t1_obj_t1.id,", - " 'name', t1_obj_t1.name,", - " 'type', t1_obj_t1.type)", - "FROM agreego.entity t1_obj_t1", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Simple entity select on root stem", - "action": "query", - "schema_id": "entity", - "stem": "", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'archived', t1_obj_t1.archived,", - " 'created_at', t1_obj_t1.created_at,", - " 'id', t1_obj_t1.id,", - " 'name', t1_obj_t1.name,", - " 'type', t1_obj_t1.type)", - "FROM agreego.entity t1_obj_t1", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Simple entity select with multiple filters", - "action": "query", - "schema_id": "entity", - "filters": { - "id": { - "$eq": "123e4567-e89b-12d3-a456-426614174000", - "$ne": "123e4567-e89b-12d3-a456-426614174001", - "$in": [ - "123e4567-e89b-12d3-a456-426614174000" - ], - "$nin": [ - "123e4567-e89b-12d3-a456-426614174001" - ] - }, - "name": { - "$eq": "Jane%", - "$ne": "John%", - "$gt": "A", - "$gte": "B", - "$lt": "Z", - "$lte": "Y", - "$in": [ - "Jane", - "John" - ], - "$nin": [ - "Bob", - "Alice" - ] - }, - "created_at": { - "$eq": "2023-01-01T00:00:00Z", - "$ne": "2023-01-02T00:00:00Z", - "$gt": "2022-01-01T00:00:00Z", - "$gte": "2022-01-02T00:00:00Z", - "$lt": "2024-01-01T00:00:00Z", - "$lte": "2024-01-02T00:00:00Z" - }, - "archived": { - "$eq": false, - "$ne": true - } - }, - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'archived', t1_obj_t1.archived,", - " 'created_at', t1_obj_t1.created_at,", - " 'id', t1_obj_t1.id,", - " 'name', t1_obj_t1.name,", - " 'type', t1_obj_t1.type", - ")", - "FROM agreego.entity t1_obj_t1", - "WHERE", - " NOT t1_obj_t1.archived", - " AND t1_obj_t1.archived = ($1#>>'{}')::boolean", - " AND t1_obj_t1.archived != ($2#>>'{}')::boolean", - " AND t1_obj_t1.created_at = ($3#>>'{}')::timestamptz", - " AND t1_obj_t1.created_at > ($4#>>'{}')::timestamptz", - " AND t1_obj_t1.created_at >= ($5#>>'{}')::timestamptz", - " AND t1_obj_t1.created_at < ($6#>>'{}')::timestamptz", - " AND t1_obj_t1.created_at <= ($7#>>'{}')::timestamptz", - " AND t1_obj_t1.created_at != ($8#>>'{}')::timestamptz", - " AND t1_obj_t1.id = ($9#>>'{}')::uuid", - " AND t1_obj_t1.id IN (SELECT value::uuid FROM jsonb_array_elements_text(($10#>>'{}')::jsonb))", - " AND t1_obj_t1.id != ($11#>>'{}')::uuid", - " AND t1_obj_t1.id NOT IN (SELECT value::uuid FROM jsonb_array_elements_text(($12#>>'{}')::jsonb))", - " AND t1_obj_t1.name ILIKE $13#>>'{}'", - " AND t1_obj_t1.name > ($14#>>'{}')", - " AND t1_obj_t1.name >= ($15#>>'{}')", - " AND t1_obj_t1.name IN (SELECT value FROM jsonb_array_elements_text(($16#>>'{}')::jsonb))", - " AND t1_obj_t1.name < ($17#>>'{}')", - " AND t1_obj_t1.name <= ($18#>>'{}')", - " AND t1_obj_t1.name NOT ILIKE $19#>>'{}'", - " AND t1_obj_t1.name NOT IN (SELECT value FROM jsonb_array_elements_text(($20#>>'{}')::jsonb))", - ")" - ] - ] - } - }, - { - "description": "Person select on base schema", - "action": "query", - "schema_id": "base.person", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'age', t1_obj_t1.age,", - " 'archived', t1_obj_t2.archived,", - " 'created_at', t1_obj_t2.created_at,", - " 'first_name', t1_obj_t1.first_name,", - " 'id', t1_obj_t2.id,", - " 'last_name', t1_obj_t1.last_name,", - " 'name', t1_obj_t2.name,", - " 'type', t1_obj_t2.type)", - "FROM agreego.person t1_obj_t1", - "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Person select on full schema", - "action": "query", - "schema_id": "full.person", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'addresses',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_addresses_t3.archived,", - " 'created_at', t1_obj_t2_addresses_t3.created_at,", - " 'id', t1_obj_t2_addresses_t3.id,", - " 'is_primary', t1_obj_t2_addresses_t1.is_primary,", - " 'name', t1_obj_t2_addresses_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_addresses_t3_target_t2.archived,", - " 'city', t1_obj_t2_addresses_t3_target_t1.city,", - " 'created_at', t1_obj_t2_addresses_t3_target_t2.created_at,", - " 'id', t1_obj_t2_addresses_t3_target_t2.id,", - " 'name', t1_obj_t2_addresses_t3_target_t2.name,", - " 'type', t1_obj_t2_addresses_t3_target_t2.type", - " )", - " FROM agreego.address t1_obj_t2_addresses_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_addresses_t3_target_t2 ON t1_obj_t2_addresses_t3_target_t2.id = t1_obj_t2_addresses_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_addresses_t3_target_t1.archived", - " AND t1_obj_t2_addresses_t3_target_t1.id = t1_obj_t2_addresses_t3.target_id", - " ),", - " 'type', t1_obj_t2_addresses_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_addresses_t1", - " JOIN agreego.relationship t1_obj_t2_addresses_t2 ON t1_obj_t2_addresses_t2.id = t1_obj_t2_addresses_t1.id", - " JOIN agreego.entity t1_obj_t2_addresses_t3 ON t1_obj_t2_addresses_t3.id = t1_obj_t2_addresses_t2.id", - " WHERE", - " NOT t1_obj_t2_addresses_t1.archived", - " AND t1_obj_t2_addresses_t1.parent_id = t1_obj_t2.id),", - " 'age', t1_obj_t1.age,", - " 'archived', t1_obj_t2.archived,", - " 'contacts',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_contacts_t3.archived,", - " 'created_at', t1_obj_t2_contacts_t3.created_at,", - " 'id', t1_obj_t2_contacts_t3.id,", - " 'is_primary', t1_obj_t2_contacts_t1.is_primary,", - " 'name', t1_obj_t2_contacts_t3.name,", - " 'target', CASE", - " WHEN t1_obj_t2_contacts_t3.target_type = 'phone_number' THEN", - " ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", - " 'id', t1_obj_t2_contacts_t3_target_t2.id,", - " 'name', t1_obj_t2_contacts_t3_target_t2.name,", - " 'number', t1_obj_t2_contacts_t3_target_t1.number,", - " 'type', t1_obj_t2_contacts_t3_target_t2.type", - " )", - " FROM agreego.phone_number t1_obj_t2_contacts_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_contacts_t3_target_t1.archived", - " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", - " ))", - " WHEN t1_obj_t2_contacts_t3.target_type = 'email_address' THEN", - " ((SELECT jsonb_build_object(", - " 'address', t1_obj_t2_contacts_t3_target_t1.address,", - " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", - " 'id', t1_obj_t2_contacts_t3_target_t2.id,", - " 'name', t1_obj_t2_contacts_t3_target_t2.name,", - " 'type', t1_obj_t2_contacts_t3_target_t2.type", - " )", - " FROM agreego.email_address t1_obj_t2_contacts_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_contacts_t3_target_t1.archived", - " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", - " ))", - " WHEN t1_obj_t2_contacts_t3.target_type = 'address' THEN", - " ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", - " 'city', t1_obj_t2_contacts_t3_target_t1.city,", - " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", - " 'id', t1_obj_t2_contacts_t3_target_t2.id,", - " 'name', t1_obj_t2_contacts_t3_target_t2.name,", - " 'type', t1_obj_t2_contacts_t3_target_t2.type", - " )", - " FROM agreego.address t1_obj_t2_contacts_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_contacts_t3_target_t1.archived", - " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", - " ))", - " ELSE NULL", - " END,", - " 'type', t1_obj_t2_contacts_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_contacts_t1", - " JOIN agreego.relationship t1_obj_t2_contacts_t2 ON t1_obj_t2_contacts_t2.id = t1_obj_t2_contacts_t1.id", - " JOIN agreego.entity t1_obj_t2_contacts_t3 ON t1_obj_t2_contacts_t3.id = t1_obj_t2_contacts_t2.id", - " WHERE", - " NOT t1_obj_t2_contacts_t1.archived", - " AND t1_obj_t2_contacts_t1.parent_id = t1_obj_t2.id),", - " 'created_at', t1_obj_t2.created_at,", - " 'email_addresses',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_email_addresses_t3.archived,", - " 'created_at', t1_obj_t2_email_addresses_t3.created_at,", - " 'id', t1_obj_t2_email_addresses_t3.id,", - " 'is_primary', t1_obj_t2_email_addresses_t1.is_primary,", - " 'name', t1_obj_t2_email_addresses_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'address', t1_obj_t2_email_addresses_t3_target_t1.address,", - " 'archived', t1_obj_t2_email_addresses_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_email_addresses_t3_target_t2.created_at,", - " 'id', t1_obj_t2_email_addresses_t3_target_t2.id,", - " 'name', t1_obj_t2_email_addresses_t3_target_t2.name,", - " 'type', t1_obj_t2_email_addresses_t3_target_t2.type", - " )", - " FROM agreego.email_address t1_obj_t2_email_addresses_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_email_addresses_t3_target_t2 ON t1_obj_t2_email_addresses_t3_target_t2.id = t1_obj_t2_email_addresses_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_email_addresses_t3_target_t1.archived", - " AND t1_obj_t2_email_addresses_t3_target_t1.id = t1_obj_t2_email_addresses_t3.target_id", - " ),", - " 'type', t1_obj_t2_email_addresses_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_email_addresses_t1", - " JOIN agreego.relationship t1_obj_t2_email_addresses_t2 ON t1_obj_t2_email_addresses_t2.id = t1_obj_t2_email_addresses_t1.id", - " JOIN agreego.entity t1_obj_t2_email_addresses_t3 ON t1_obj_t2_email_addresses_t3.id = t1_obj_t2_email_addresses_t2.id", - " WHERE", - " NOT t1_obj_t2_email_addresses_t1.archived", - " AND t1_obj_t2_email_addresses_t1.parent_id = t1_obj_t2.id),", - " 'first_name', t1_obj_t1.first_name,", - " 'id', t1_obj_t2.id,", - " 'last_name', t1_obj_t1.last_name,", - " 'name', t1_obj_t2.name,", - " 'phone_numbers',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_phone_numbers_t3.archived,", - " 'created_at', t1_obj_t2_phone_numbers_t3.created_at,", - " 'id', t1_obj_t2_phone_numbers_t3.id,", - " 'is_primary', t1_obj_t2_phone_numbers_t1.is_primary,", - " 'name', t1_obj_t2_phone_numbers_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_phone_numbers_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_phone_numbers_t3_target_t2.created_at,", - " 'id', t1_obj_t2_phone_numbers_t3_target_t2.id,", - " 'name', t1_obj_t2_phone_numbers_t3_target_t2.name,", - " 'number', t1_obj_t2_phone_numbers_t3_target_t1.number,", - " 'type', t1_obj_t2_phone_numbers_t3_target_t2.type", - " )", - " FROM agreego.phone_number t1_obj_t2_phone_numbers_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_phone_numbers_t3_target_t2 ON t1_obj_t2_phone_numbers_t3_target_t2.id = t1_obj_t2_phone_numbers_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_phone_numbers_t3_target_t1.archived", - " AND t1_obj_t2_phone_numbers_t3_target_t1.id = t1_obj_t2_phone_numbers_t3.target_id", - " ),", - " 'type', t1_obj_t2_phone_numbers_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_phone_numbers_t1", - " JOIN agreego.relationship t1_obj_t2_phone_numbers_t2 ON t1_obj_t2_phone_numbers_t2.id = t1_obj_t2_phone_numbers_t1.id", - " JOIN agreego.entity t1_obj_t2_phone_numbers_t3 ON t1_obj_t2_phone_numbers_t3.id = t1_obj_t2_phone_numbers_t2.id", - " WHERE", - " NOT t1_obj_t2_phone_numbers_t1.archived", - " AND t1_obj_t2_phone_numbers_t1.parent_id = t1_obj_t2.id),", - " 'type', t1_obj_t2.type", - ")", - "FROM agreego.person t1_obj_t1", - "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Person select on full schema with filters", - "action": "query", - "schema_id": "full.person", - "filters": { - "age": { - "$eq": 30, - "$gt": 20, - "$gte": 20, - "$in": [ - 30, - 40 - ], - "$lt": 50, - "$lte": 50, - "$ne": 25, - "$nin": [ - 1, - 2 - ] - }, - "archived": { - "$eq": true, - "$ne": false - }, - "contacts.#.is_primary": { - "$eq": true - }, - "created_at": { - "$eq": "2020-01-01T00:00:00Z", - "$gt": "2019-01-01T00:00:00Z", - "$gte": "2019-01-01T00:00:00Z", - "$lt": "2021-01-01T00:00:00Z", - "$lte": "2021-01-01T00:00:00Z", - "$ne": "2022-01-01T00:00:00Z" - }, - "first_name": { - "$eq": "Jane%", - "$gt": "A", - "$gte": "A", - "$in": [ - "Jane", - "John" - ], - "$lt": "Z", - "$lte": "Z", - "$ne": "Doe", - "$nin": [ - "Bob" - ] - }, - "id": { - "$eq": "00000000-0000-0000-0000-000000000001", - "$in": [ - "00000000-0000-0000-0000-000000000001" - ], - "$ne": "00000000-0000-0000-0000-000000000002", - "$nin": [ - "00000000-0000-0000-0000-000000000002" - ] - }, - "last_name": { - "$eq": "%Doe%", - "$ne": "%Smith%" - }, - "phone_numbers.#.target.number": { - "$eq": "555-1234" - } - }, - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'addresses',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_addresses_t3.archived,", - " 'created_at', t1_obj_t2_addresses_t3.created_at,", - " 'id', t1_obj_t2_addresses_t3.id,", - " 'is_primary', t1_obj_t2_addresses_t1.is_primary,", - " 'name', t1_obj_t2_addresses_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_addresses_t3_target_t2.archived,", - " 'city', t1_obj_t2_addresses_t3_target_t1.city,", - " 'created_at', t1_obj_t2_addresses_t3_target_t2.created_at,", - " 'id', t1_obj_t2_addresses_t3_target_t2.id,", - " 'name', t1_obj_t2_addresses_t3_target_t2.name,", - " 'type', t1_obj_t2_addresses_t3_target_t2.type", - " )", - " FROM agreego.address t1_obj_t2_addresses_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_addresses_t3_target_t2 ON t1_obj_t2_addresses_t3_target_t2.id = t1_obj_t2_addresses_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_addresses_t3_target_t1.archived", - " AND t1_obj_t2_addresses_t3_target_t1.id = t1_obj_t2_addresses_t3.target_id", - " ),", - " 'type', t1_obj_t2_addresses_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_addresses_t1", - " JOIN agreego.relationship t1_obj_t2_addresses_t2 ON t1_obj_t2_addresses_t2.id = t1_obj_t2_addresses_t1.id", - " JOIN agreego.entity t1_obj_t2_addresses_t3 ON t1_obj_t2_addresses_t3.id = t1_obj_t2_addresses_t2.id", - " WHERE", - " NOT t1_obj_t2_addresses_t1.archived", - " AND t1_obj_t2_addresses_t1.parent_id = t1_obj_t2.id),", - " 'age', t1_obj_t1.age,", - " 'archived', t1_obj_t2.archived,", - " 'contacts',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_contacts_t3.archived,", - " 'created_at', t1_obj_t2_contacts_t3.created_at,", - " 'id', t1_obj_t2_contacts_t3.id,", - " 'is_primary', t1_obj_t2_contacts_t1.is_primary,", - " 'name', t1_obj_t2_contacts_t3.name,", - " 'target', CASE", - " WHEN t1_obj_t2_contacts_t3.target_type = 'phone_number' THEN", - " ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", - " 'id', t1_obj_t2_contacts_t3_target_t2.id,", - " 'name', t1_obj_t2_contacts_t3_target_t2.name,", - " 'number', t1_obj_t2_contacts_t3_target_t1.number,", - " 'type', t1_obj_t2_contacts_t3_target_t2.type", - " )", - " FROM agreego.phone_number t1_obj_t2_contacts_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_contacts_t3_target_t1.archived", - " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", - " ))", - " WHEN t1_obj_t2_contacts_t3.target_type = 'email_address' THEN", - " ((SELECT jsonb_build_object(", - " 'address', t1_obj_t2_contacts_t3_target_t1.address,", - " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", - " 'id', t1_obj_t2_contacts_t3_target_t2.id,", - " 'name', t1_obj_t2_contacts_t3_target_t2.name,", - " 'type', t1_obj_t2_contacts_t3_target_t2.type", - " )", - " FROM agreego.email_address t1_obj_t2_contacts_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_contacts_t3_target_t1.archived", - " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", - " ))", - " WHEN t1_obj_t2_contacts_t3.target_type = 'address' THEN", - " ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", - " 'city', t1_obj_t2_contacts_t3_target_t1.city,", - " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", - " 'id', t1_obj_t2_contacts_t3_target_t2.id,", - " 'name', t1_obj_t2_contacts_t3_target_t2.name,", - " 'type', t1_obj_t2_contacts_t3_target_t2.type", - " )", - " FROM agreego.address t1_obj_t2_contacts_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_contacts_t3_target_t1.archived", - " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", - " ))", - " ELSE NULL", - " END,", - " 'type', t1_obj_t2_contacts_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_contacts_t1", - " JOIN agreego.relationship t1_obj_t2_contacts_t2 ON t1_obj_t2_contacts_t2.id = t1_obj_t2_contacts_t1.id", - " JOIN agreego.entity t1_obj_t2_contacts_t3 ON t1_obj_t2_contacts_t3.id = t1_obj_t2_contacts_t2.id", - " WHERE", - " NOT t1_obj_t2_contacts_t1.archived", - " AND t1_obj_t2_contacts_t1.is_primary = ($11#>>'{}')::boolean", - " AND t1_obj_t2_contacts_t1.parent_id = t1_obj_t2.id),", - " 'created_at', t1_obj_t2.created_at,", - " 'email_addresses',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_email_addresses_t3.archived,", - " 'created_at', t1_obj_t2_email_addresses_t3.created_at,", - " 'id', t1_obj_t2_email_addresses_t3.id,", - " 'is_primary', t1_obj_t2_email_addresses_t1.is_primary,", - " 'name', t1_obj_t2_email_addresses_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'address', t1_obj_t2_email_addresses_t3_target_t1.address,", - " 'archived', t1_obj_t2_email_addresses_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_email_addresses_t3_target_t2.created_at,", - " 'id', t1_obj_t2_email_addresses_t3_target_t2.id,", - " 'name', t1_obj_t2_email_addresses_t3_target_t2.name,", - " 'type', t1_obj_t2_email_addresses_t3_target_t2.type", - " )", - " FROM agreego.email_address t1_obj_t2_email_addresses_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_email_addresses_t3_target_t2 ON t1_obj_t2_email_addresses_t3_target_t2.id = t1_obj_t2_email_addresses_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_email_addresses_t3_target_t1.archived", - " AND t1_obj_t2_email_addresses_t3_target_t1.id = t1_obj_t2_email_addresses_t3.target_id", - " ),", - " 'type', t1_obj_t2_email_addresses_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_email_addresses_t1", - " JOIN agreego.relationship t1_obj_t2_email_addresses_t2 ON t1_obj_t2_email_addresses_t2.id = t1_obj_t2_email_addresses_t1.id", - " JOIN agreego.entity t1_obj_t2_email_addresses_t3 ON t1_obj_t2_email_addresses_t3.id = t1_obj_t2_email_addresses_t2.id", - " WHERE", - " NOT t1_obj_t2_email_addresses_t1.archived", - " AND t1_obj_t2_email_addresses_t1.parent_id = t1_obj_t2.id),", - " 'first_name', t1_obj_t1.first_name,", - " 'id', t1_obj_t2.id,", - " 'last_name', t1_obj_t1.last_name,", - " 'name', t1_obj_t2.name,", - " 'phone_numbers',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_phone_numbers_t3.archived,", - " 'created_at', t1_obj_t2_phone_numbers_t3.created_at,", - " 'id', t1_obj_t2_phone_numbers_t3.id,", - " 'is_primary', t1_obj_t2_phone_numbers_t1.is_primary,", - " 'name', t1_obj_t2_phone_numbers_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'archived', t1_obj_t2_phone_numbers_t3_target_t2.archived,", - " 'created_at', t1_obj_t2_phone_numbers_t3_target_t2.created_at,", - " 'id', t1_obj_t2_phone_numbers_t3_target_t2.id,", - " 'name', t1_obj_t2_phone_numbers_t3_target_t2.name,", - " 'number', t1_obj_t2_phone_numbers_t3_target_t1.number,", - " 'type', t1_obj_t2_phone_numbers_t3_target_t2.type", - " )", - " FROM agreego.phone_number t1_obj_t2_phone_numbers_t3_target_t1", - " JOIN agreego.entity t1_obj_t2_phone_numbers_t3_target_t2 ON t1_obj_t2_phone_numbers_t3_target_t2.id = t1_obj_t2_phone_numbers_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t2_phone_numbers_t3_target_t1.archived", - " AND t1_obj_t2_phone_numbers_t3_target_t1.number ILIKE $32#>>'{}'", - " AND t1_obj_t2_phone_numbers_t3_target_t1.id = t1_obj_t2_phone_numbers_t3.target_id", - " ),", - " 'type', t1_obj_t2_phone_numbers_t3.type", - " )), '[]'::jsonb)", - " FROM agreego.contact t1_obj_t2_phone_numbers_t1", - " JOIN agreego.relationship t1_obj_t2_phone_numbers_t2 ON t1_obj_t2_phone_numbers_t2.id = t1_obj_t2_phone_numbers_t1.id", - " JOIN agreego.entity t1_obj_t2_phone_numbers_t3 ON t1_obj_t2_phone_numbers_t3.id = t1_obj_t2_phone_numbers_t2.id", - " WHERE", - " NOT t1_obj_t2_phone_numbers_t1.archived", - " AND t1_obj_t2_phone_numbers_t1.parent_id = t1_obj_t2.id),", - " 'type', t1_obj_t2.type", - ")", - "FROM agreego.person t1_obj_t1", - "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "WHERE", - " NOT t1_obj_t1.archived", - " AND t1_obj_t1.age = ($1#>>'{}')::numeric", - " AND t1_obj_t1.age > ($2#>>'{}')::numeric", - " AND t1_obj_t1.age >= ($3#>>'{}')::numeric", - " AND t1_obj_t1.age IN (SELECT value::numeric FROM jsonb_array_elements_text(($4#>>'{}')::jsonb))", - " AND t1_obj_t1.age < ($5#>>'{}')::numeric", - " AND t1_obj_t1.age <= ($6#>>'{}')::numeric", - " AND t1_obj_t1.age != ($7#>>'{}')::numeric", - " AND t1_obj_t1.age NOT IN (SELECT value::numeric FROM jsonb_array_elements_text(($8#>>'{}')::jsonb))", - " AND t1_obj_t2.archived = ($9#>>'{}')::boolean", - " AND t1_obj_t2.archived != ($10#>>'{}')::boolean", - " AND t1_obj_t2.created_at = ($12#>>'{}')::timestamptz", - " AND t1_obj_t2.created_at > ($13#>>'{}')::timestamptz", - " AND t1_obj_t2.created_at >= ($14#>>'{}')::timestamptz", - " AND t1_obj_t2.created_at < ($15#>>'{}')::timestamptz", - " AND t1_obj_t2.created_at <= ($16#>>'{}')::timestamptz", - " AND t1_obj_t2.created_at != ($17#>>'{}')::timestamptz", - " AND t1_obj_t1.first_name ILIKE $18#>>'{}'", - " AND t1_obj_t1.first_name > ($19#>>'{}')", - " AND t1_obj_t1.first_name >= ($20#>>'{}')", - " AND t1_obj_t1.first_name IN (SELECT value FROM jsonb_array_elements_text(($21#>>'{}')::jsonb))", - " AND t1_obj_t1.first_name < ($22#>>'{}')", - " AND t1_obj_t1.first_name <= ($23#>>'{}')", - " AND t1_obj_t1.first_name NOT ILIKE $24#>>'{}'", - " AND t1_obj_t1.first_name NOT IN (SELECT value FROM jsonb_array_elements_text(($25#>>'{}')::jsonb))", - " AND t1_obj_t2.id = ($26#>>'{}')::uuid", - " AND t1_obj_t2.id IN (SELECT value::uuid FROM jsonb_array_elements_text(($27#>>'{}')::jsonb))", - " AND t1_obj_t2.id != ($28#>>'{}')::uuid", - " AND t1_obj_t2.id NOT IN (SELECT value::uuid FROM jsonb_array_elements_text(($29#>>'{}')::jsonb))", - " AND t1_obj_t1.last_name ILIKE $30#>>'{}'", - " AND t1_obj_t1.last_name NOT ILIKE $31#>>'{}'", - ")" - ] - ] - } - }, - { - "description": "Full person stem query on phone number contact", - "action": "query", - "schema_id": "full.person", - "stem": "phone_numbers.#", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'archived', t1_obj_t3.archived,", - " 'created_at', t1_obj_t3.created_at,", - " 'id', t1_obj_t3.id,", - " 'is_primary', t1_obj_t1.is_primary,", - " 'name', t1_obj_t3.name,", - " 'target',", - " (SELECT jsonb_build_object(", - " 'archived', t1_obj_t3_target_t2.archived,", - " 'created_at', t1_obj_t3_target_t2.created_at,", - " 'id', t1_obj_t3_target_t2.id,", - " 'name', t1_obj_t3_target_t2.name,", - " 'number', t1_obj_t3_target_t1.number,", - " 'type', t1_obj_t3_target_t2.type", - " )", - " FROM agreego.phone_number t1_obj_t3_target_t1", - " JOIN agreego.entity t1_obj_t3_target_t2 ON t1_obj_t3_target_t2.id = t1_obj_t3_target_t1.id", - " WHERE", - " NOT t1_obj_t3_target_t1.archived", - " AND t1_obj_t3_target_t1.id = t1_obj_t3.target_id),", - " 'type', t1_obj_t3.type", - ")", - "FROM agreego.contact t1_obj_t1", - "JOIN agreego.relationship t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "JOIN agreego.entity t1_obj_t3 ON t1_obj_t3.id = t1_obj_t2.id", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Full person stem query on phone number contact on phone number", - "action": "query", - "schema_id": "full.person", - "stem": "phone_numbers.#.target", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'archived', t1_obj_t2.archived,", - " 'created_at', t1_obj_t2.created_at,", - " 'id', t1_obj_t2.id,", - " 'name', t1_obj_t2.name,", - " 'number', t1_obj_t1.number,", - " 'type', t1_obj_t2.type)", - "FROM agreego.phone_number t1_obj_t1", - "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Full person stem query on contact email address", - "action": "query", - "schema_id": "full.person", - "stem": "contacts.#.target#(type==\"email_address\")", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'address', t1_obj_t1.address,", - " 'archived', t1_obj_t2.archived,", - " 'created_at', t1_obj_t2.created_at,", - " 'id', t1_obj_t2.id,", - " 'name', t1_obj_t2.name,", - " 'type', t1_obj_t2.type)", - "FROM agreego.email_address t1_obj_t1", - "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Order select with customer and lines", - "action": "query", - "schema_id": "full.order", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'archived', t1_obj_t2.archived,", - " 'created_at', t1_obj_t2.created_at,", - " 'customer',", - " (SELECT jsonb_build_object(", - " 'age', t1_obj_t2_customer_t1.age,", - " 'archived', t1_obj_t2_customer_t2.archived,", - " 'created_at', t1_obj_t2_customer_t2.created_at,", - " 'first_name', t1_obj_t2_customer_t1.first_name,", - " 'id', t1_obj_t2_customer_t2.id,", - " 'last_name', t1_obj_t2_customer_t1.last_name,", - " 'name', t1_obj_t2_customer_t2.name,", - " 'type', t1_obj_t2_customer_t2.type", - " )", - " FROM agreego.person t1_obj_t2_customer_t1", - " JOIN agreego.entity t1_obj_t2_customer_t2 ON t1_obj_t2_customer_t2.id = t1_obj_t2_customer_t1.id", - " WHERE", - " NOT t1_obj_t2_customer_t1.archived", - " AND t1_obj_t2_customer_t1.parent_id = t1_obj_t2.id),", - " 'customer_id', t1_obj_t1.customer_id,", - " 'id', t1_obj_t2.id,", - " 'lines',", - " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", - " 'archived', t1_obj_t2_lines_t2.archived,", - " 'created_at', t1_obj_t2_lines_t2.created_at,", - " 'id', t1_obj_t2_lines_t2.id,", - " 'name', t1_obj_t2_lines_t2.name,", - " 'order_id', t1_obj_t2_lines_t1.order_id,", - " 'price', t1_obj_t2_lines_t1.price,", - " 'product', t1_obj_t2_lines_t1.product,", - " 'type', t1_obj_t2_lines_t2.type", - " )), '[]'::jsonb)", - " FROM agreego.order_line t1_obj_t2_lines_t1", - " JOIN agreego.entity t1_obj_t2_lines_t2 ON t1_obj_t2_lines_t2.id = t1_obj_t2_lines_t1.id", - " WHERE", - " NOT t1_obj_t2_lines_t1.archived", - " AND t1_obj_t2_lines_t1.parent_id = t1_obj_t2.id),", - " 'name', t1_obj_t2.name,", - " 'total', t1_obj_t1.total,", - " 'type', t1_obj_t2.type", - ")", - "FROM agreego.order t1_obj_t1", - "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } - }, - { - "description": "Base entity family select on polymorphic tree", - "action": "query", - "schema_id": "get_entities.response", - "expect": { - "success": true, - "sql": [ - [ - "(SELECT jsonb_build_object(", - " 'id', t1_obj_t1.id,", - " 'type', CASE WHEN t1_obj_t1.type = 'address' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'city', t1_obj_t1_obj_t1.city,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.address t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'contact' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t3.archived,", - " 'created_at', t1_obj_t1_obj_t3.created_at,", - " 'id', t1_obj_t1_obj_t3.id,", - " 'is_primary', t1_obj_t1_obj_t1.is_primary,", - " 'name', t1_obj_t1_obj_t3.name,", - " 'type', t1_obj_t1_obj_t3.type)", - " FROM agreego.contact t1_obj_t1_obj_t1", - " JOIN agreego.relationship t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " JOIN agreego.entity t1_obj_t1_obj_t3 ON t1_obj_t1_obj_t3.id = t1_obj_t1_obj_t2.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'email_address' THEN ((SELECT jsonb_build_object(", - " 'address', t1_obj_t1_obj_t1.address,", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.email_address t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'entity' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t1.archived,", - " 'created_at', t1_obj_t1_obj_t1.created_at,", - " 'id', t1_obj_t1_obj_t1.id,", - " 'name', t1_obj_t1_obj_t1.name,", - " 'type', t1_obj_t1_obj_t1.type)", - " FROM agreego.entity t1_obj_t1_obj_t1", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'order' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'customer_id', t1_obj_t1_obj_t1.customer_id,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'total', t1_obj_t1_obj_t1.total,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.order t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'order_line' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'order_id', t1_obj_t1_obj_t1.order_id,", - " 'price', t1_obj_t1_obj_t1.price,", - " 'product', t1_obj_t1_obj_t1.product,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.order_line t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'organization' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.organization t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'person' THEN ((SELECT jsonb_build_object(", - " 'age', t1_obj_t1_obj_t1.age,", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'first_name', t1_obj_t1_obj_t1.first_name,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'last_name', t1_obj_t1_obj_t1.last_name,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.person t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'phone_number' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'number', t1_obj_t1_obj_t1.number,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.phone_number t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " WHEN t1_obj_t1.type = 'relationship' THEN ((SELECT jsonb_build_object(", - " 'archived', t1_obj_t1_obj_t2.archived,", - " 'created_at', t1_obj_t1_obj_t2.created_at,", - " 'id', t1_obj_t1_obj_t2.id,", - " 'name', t1_obj_t1_obj_t2.name,", - " 'type', t1_obj_t1_obj_t2.type)", - " FROM agreego.relationship t1_obj_t1_obj_t1", - " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", - " WHERE NOT t1_obj_t1_obj_t1.archived))", - " ELSE NULL END)", - "FROM agreego.entity t1_obj_t1", - "WHERE NOT t1_obj_t1.archived)" - ] - ] - } + "$id": "get_entities.response", + "$family": "entity" } - ] - } + ] + } + ], + "enums": [], + "relations": [ + { + "type": "relation", + "id": "00000000-0000-0000-0000-000000000001", + "constraint": "fk_relationship_source_entity", + "source_type": "relationship", + "source_columns": [ + "source_id", + "source_type" + ], + "destination_type": "entity", + "destination_columns": [ + "id", + "type" + ], + "prefix": "source" + }, + { + "type": "relation", + "id": "00000000-0000-0000-0000-000000000002", + "constraint": "fk_relationship_target_entity", + "source_type": "relationship", + "source_columns": [ + "target_id", + "target_type" + ], + "destination_type": "entity", + "destination_columns": [ + "id", + "type" + ], + "prefix": "target" + }, + { + "id": "22222222-2222-2222-2222-222222222222", + "type": "relation", + "constraint": "fk_order_customer", + "source_type": "order", + "source_columns": [ + "customer_id" + ], + "destination_type": "person", + "destination_columns": [ + "id" + ], + "prefix": "customer" + }, + { + "id": "33333333-3333-3333-3333-333333333333", + "type": "relation", + "constraint": "fk_order_line_order", + "source_type": "order_line", + "source_columns": [ + "order_id" + ], + "destination_type": "order", + "destination_columns": [ + "id" + ], + "prefix": "lines" + } + ], + "types": [ + { + "name": "entity", + "hierarchy": [ + "entity" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ] + }, + "field_types": { + "id": "uuid", + "name": "text", + "archived": "boolean", + "created_at": "timestamptz", + "type": "text" + }, + "schemas": [ + { + "$id": "entity", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string" + }, + "name": { + "type": "string" + }, + "archived": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "created": { + "type": "boolean" + } + } + } + ], + "fields": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "variations": [ + "address", + "contact", + "email_address", + "entity", + "order", + "order_line", + "organization", + "person", + "phone_number", + "relationship" + ] + }, + { + "name": "person", + "hierarchy": [ + "person", + "entity" + ], + "fields": [ + "first_name", + "last_name", + "id", + "type", + "name", + "age", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "person": [ + "first_name", + "last_name", + "age" + ] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "first_name": "text", + "last_name": "text", + "name": "text", + "age": "numeric", + "created_at": "timestamptz" + }, + "schemas": [ + { + "$id": "base.person", + "$ref": "entity", + "properties": { + "first_name": { + "type": "string" + }, + "last_name": { + "type": "string" + }, + "age": { + "type": "number" + } + } + }, + { + "$id": "full.person", + "$ref": "base.person", + "properties": { + "phone_numbers": { + "type": "array", + "items": { + "$ref": "contact", + "properties": { + "target": { + "$ref": "phone_number" + } + } + } + }, + "email_addresses": { + "type": "array", + "items": { + "$ref": "contact", + "properties": { + "target": { + "$ref": "email_address" + } + } + } + }, + "addresses": { + "type": "array", + "items": { + "$ref": "contact", + "properties": { + "target": { + "$ref": "address" + } + } + } + }, + "contacts": { + "type": "array", + "items": { + "$ref": "contact", + "properties": { + "target": { + "oneOf": [ + { + "$ref": "phone_number" + }, + { + "$ref": "email_address" + }, + { + "$ref": "address" + } + ] + } + } + } + } + } + } + ], + "variations": [ + "person" + ] + }, + { + "name": "relationship", + "relationship": true, + "hierarchy": [ + "relationship", + "entity" + ], + "fields": [ + "source_id", + "source_type", + "target_id", + "target_type", + "id", + "type", + "name", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "relationship": [ + "source_id", + "source_type", + "target_id", + "target_type" + ] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "source_id": "uuid", + "source_type": "text", + "target_id": "uuid", + "target_type": "text", + "name": "text", + "created_at": "timestamptz" + }, + "schemas": [ + { + "$id": "relationship", + "$ref": "entity", + "properties": {} + } + ], + "variations": [ + "contact", + "relationship" + ] + }, + { + "name": "contact", + "relationship": true, + "hierarchy": [ + "contact", + "relationship", + "entity" + ], + "fields": [ + "is_primary", + "source_id", + "source_type", + "target_id", + "target_type", + "id", + "type", + "name", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "relationship": [ + "source_id", + "source_type", + "target_id", + "target_type" + ], + "contact": [ + "is_primary" + ] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "source_id": "uuid", + "source_type": "text", + "target_id": "uuid", + "target_type": "text", + "is_primary": "boolean", + "name": "text", + "created_at": "timestamptz" + }, + "schemas": [ + { + "$id": "contact", + "$ref": "relationship", + "properties": { + "is_primary": { + "type": "boolean" + } + } + } + ], + "variations": [ + "contact" + ] + }, + { + "name": "phone_number", + "hierarchy": [ + "phone_number", + "entity" + ], + "fields": [ + "number", + "id", + "type", + "name", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "phone_number": [ + "number" + ] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "number": "text", + "name": "text", + "created_at": "timestamptz" + }, + "schemas": [ + { + "$id": "phone_number", + "$ref": "entity", + "properties": { + "number": { + "type": "string" + } + } + } + ], + "variations": [ + "phone_number" + ] + }, + { + "name": "email_address", + "hierarchy": [ + "email_address", + "entity" + ], + "fields": [ + "address", + "id", + "type", + "name", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "email_address": [ + "address" + ] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "address": "text", + "name": "text", + "created_at": "timestamptz" + }, + "schemas": [ + { + "$id": "email_address", + "$ref": "entity", + "properties": { + "address": { + "type": "string" + } + } + } + ], + "variations": [ + "email_address" + ] + }, + { + "name": "address", + "hierarchy": [ + "address", + "entity" + ], + "fields": [ + "city", + "id", + "type", + "name", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "address": [ + "city" + ] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "city": "text", + "name": "text", + "created_at": "timestamptz" + }, + "schemas": [ + { + "$id": "address", + "$ref": "entity", + "properties": { + "city": { + "type": "string" + } + } + } + ], + "variations": [ + "address" + ] + }, + { + "name": "order", + "schemas": [ + { + "$id": "order", + "$ref": "entity", + "properties": { + "total": { + "type": "number" + }, + "customer_id": { + "type": "string" + } + } + }, + { + "$id": "full.order", + "$ref": "order", + "properties": { + "customer": { + "$ref": "base.person" + }, + "lines": { + "type": "array", + "items": { + "$ref": "order_line" + } + } + } + } + ], + "hierarchy": [ + "order", + "entity" + ], + "fields": [ + "id", + "type", + "name", + "total", + "customer_id", + "created_at", + "created_by", + "modified_at", + "modified_by", + "archived" + ], + "grouped_fields": { + "order": [ + "id", + "type", + "total", + "customer_id" + ], + "entity": [ + "id", + "type", + "name", + "created_at", + "created_by", + "modified_at", + "modified_by", + "archived" + ] + }, + "lookup_fields": [ + "id" + ], + "historical": true, + "relationship": false, + "field_types": { + "id": "uuid", + "type": "text", + "name": "text", + "archived": "boolean", + "total": "numeric", + "customer_id": "uuid", + "created_at": "timestamptz", + "created_by": "uuid", + "modified_at": "timestamptz", + "modified_by": "uuid" + }, + "variations": [ + "order" + ] + }, + { + "name": "order_line", + "schemas": [ + { + "$id": "order_line", + "$ref": "entity", + "properties": { + "order_id": { + "type": "string" + }, + "product": { + "type": "string" + }, + "price": { + "type": "number" + } + } + } + ], + "hierarchy": [ + "order_line", + "entity" + ], + "fields": [ + "id", + "type", + "name", + "order_id", + "product", + "price", + "created_at", + "created_by", + "modified_at", + "modified_by", + "archived" + ], + "grouped_fields": { + "order_line": [ + "id", + "type", + "order_id", + "product", + "price" + ], + "entity": [ + "id", + "type", + "name", + "created_at", + "created_by", + "modified_at", + "modified_by", + "archived" + ] + }, + "lookup_fields": [], + "historical": true, + "relationship": false, + "field_types": { + "id": "uuid", + "type": "text", + "name": "text", + "archived": "boolean", + "order_id": "uuid", + "product": "text", + "price": "numeric", + "created_at": "timestamptz", + "created_by": "uuid", + "modified_at": "timestamptz", + "modified_by": "uuid" + }, + "variations": [ + "order_line" + ] + }, + { + "name": "organization", + "hierarchy": [ + "organization", + "entity" + ], + "fields": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "grouped_fields": { + "entity": [ + "id", + "type", + "name", + "archived", + "created_at" + ], + "organization": [] + }, + "field_types": { + "id": "uuid", + "type": "text", + "archived": "boolean", + "name": "text", + "created_at": "timestamptz" + }, + "lookup_fields": [ + "id" + ], + "null_fields": [], + "default_fields": [ + "id", + "type", + "created_at", + "archived" + ], + "variations": [ + "organization" + ] + } + ], + "schemas": [ + { + "$id": "entity", + "type": "object", + "properties": {} + }, + { + "$id": "organization", + "type": "object", + "$ref": "entity", + "properties": {} + }, + { + "$id": "person", + "type": "object", + "$ref": "base.person", + "properties": {} + } + ] + }, + "tests": [ + { + "description": "Simple entity select", + "action": "query", + "schema_id": "entity", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'archived', t1_obj_t1.archived,", + " 'created_at', t1_obj_t1.created_at,", + " 'id', t1_obj_t1.id,", + " 'name', t1_obj_t1.name,", + " 'type', t1_obj_t1.type)", + "FROM agreego.entity t1_obj_t1", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Simple entity select on root stem", + "action": "query", + "schema_id": "entity", + "stem": "", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'archived', t1_obj_t1.archived,", + " 'created_at', t1_obj_t1.created_at,", + " 'id', t1_obj_t1.id,", + " 'name', t1_obj_t1.name,", + " 'type', t1_obj_t1.type)", + "FROM agreego.entity t1_obj_t1", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Simple entity select with multiple filters", + "action": "query", + "schema_id": "entity", + "filters": { + "id": { + "$eq": "123e4567-e89b-12d3-a456-426614174000", + "$ne": "123e4567-e89b-12d3-a456-426614174001", + "$in": [ + "123e4567-e89b-12d3-a456-426614174000" + ], + "$nin": [ + "123e4567-e89b-12d3-a456-426614174001" + ] + }, + "name": { + "$eq": "Jane%", + "$ne": "John%", + "$gt": "A", + "$gte": "B", + "$lt": "Z", + "$lte": "Y", + "$in": [ + "Jane", + "John" + ], + "$nin": [ + "Bob", + "Alice" + ] + }, + "created_at": { + "$eq": "2023-01-01T00:00:00Z", + "$ne": "2023-01-02T00:00:00Z", + "$gt": "2022-01-01T00:00:00Z", + "$gte": "2022-01-02T00:00:00Z", + "$lt": "2024-01-01T00:00:00Z", + "$lte": "2024-01-02T00:00:00Z" + }, + "archived": { + "$eq": false, + "$ne": true + } + }, + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'archived', t1_obj_t1.archived,", + " 'created_at', t1_obj_t1.created_at,", + " 'id', t1_obj_t1.id,", + " 'name', t1_obj_t1.name,", + " 'type', t1_obj_t1.type", + ")", + "FROM agreego.entity t1_obj_t1", + "WHERE", + " NOT t1_obj_t1.archived", + " AND t1_obj_t1.archived = ($1#>>'{}')::boolean", + " AND t1_obj_t1.archived != ($2#>>'{}')::boolean", + " AND t1_obj_t1.created_at = ($3#>>'{}')::timestamptz", + " AND t1_obj_t1.created_at > ($4#>>'{}')::timestamptz", + " AND t1_obj_t1.created_at >= ($5#>>'{}')::timestamptz", + " AND t1_obj_t1.created_at < ($6#>>'{}')::timestamptz", + " AND t1_obj_t1.created_at <= ($7#>>'{}')::timestamptz", + " AND t1_obj_t1.created_at != ($8#>>'{}')::timestamptz", + " AND t1_obj_t1.id = ($9#>>'{}')::uuid", + " AND t1_obj_t1.id IN (SELECT value::uuid FROM jsonb_array_elements_text(($10#>>'{}')::jsonb))", + " AND t1_obj_t1.id != ($11#>>'{}')::uuid", + " AND t1_obj_t1.id NOT IN (SELECT value::uuid FROM jsonb_array_elements_text(($12#>>'{}')::jsonb))", + " AND t1_obj_t1.name ILIKE $13#>>'{}'", + " AND t1_obj_t1.name > ($14#>>'{}')", + " AND t1_obj_t1.name >= ($15#>>'{}')", + " AND t1_obj_t1.name IN (SELECT value FROM jsonb_array_elements_text(($16#>>'{}')::jsonb))", + " AND t1_obj_t1.name < ($17#>>'{}')", + " AND t1_obj_t1.name <= ($18#>>'{}')", + " AND t1_obj_t1.name NOT ILIKE $19#>>'{}'", + " AND t1_obj_t1.name NOT IN (SELECT value FROM jsonb_array_elements_text(($20#>>'{}')::jsonb))", + ")" + ] + ] + } + }, + { + "description": "Person select on base schema", + "action": "query", + "schema_id": "base.person", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'age', t1_obj_t1.age,", + " 'archived', t1_obj_t2.archived,", + " 'created_at', t1_obj_t2.created_at,", + " 'first_name', t1_obj_t1.first_name,", + " 'id', t1_obj_t2.id,", + " 'last_name', t1_obj_t1.last_name,", + " 'name', t1_obj_t2.name,", + " 'type', t1_obj_t2.type)", + "FROM agreego.person t1_obj_t1", + "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Person select on full schema", + "action": "query", + "schema_id": "full.person", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'addresses',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_addresses_t3.archived,", + " 'created_at', t1_obj_t2_addresses_t3.created_at,", + " 'id', t1_obj_t2_addresses_t3.id,", + " 'is_primary', t1_obj_t2_addresses_t1.is_primary,", + " 'name', t1_obj_t2_addresses_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_addresses_t3_target_t2.archived,", + " 'city', t1_obj_t2_addresses_t3_target_t1.city,", + " 'created_at', t1_obj_t2_addresses_t3_target_t2.created_at,", + " 'id', t1_obj_t2_addresses_t3_target_t2.id,", + " 'name', t1_obj_t2_addresses_t3_target_t2.name,", + " 'type', t1_obj_t2_addresses_t3_target_t2.type", + " )", + " FROM agreego.address t1_obj_t2_addresses_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_addresses_t3_target_t2 ON t1_obj_t2_addresses_t3_target_t2.id = t1_obj_t2_addresses_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_addresses_t3_target_t1.archived", + " AND t1_obj_t2_addresses_t3_target_t1.id = t1_obj_t2_addresses_t3.target_id", + " ),", + " 'type', t1_obj_t2_addresses_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_addresses_t1", + " JOIN agreego.relationship t1_obj_t2_addresses_t2 ON t1_obj_t2_addresses_t2.id = t1_obj_t2_addresses_t1.id", + " JOIN agreego.entity t1_obj_t2_addresses_t3 ON t1_obj_t2_addresses_t3.id = t1_obj_t2_addresses_t2.id", + " WHERE", + " NOT t1_obj_t2_addresses_t1.archived", + " AND t1_obj_t2_addresses_t1.parent_id = t1_obj_t2.id),", + " 'age', t1_obj_t1.age,", + " 'archived', t1_obj_t2.archived,", + " 'contacts',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_contacts_t3.archived,", + " 'created_at', t1_obj_t2_contacts_t3.created_at,", + " 'id', t1_obj_t2_contacts_t3.id,", + " 'is_primary', t1_obj_t2_contacts_t1.is_primary,", + " 'name', t1_obj_t2_contacts_t3.name,", + " 'target', CASE", + " WHEN t1_obj_t2_contacts_t3.target_type = 'phone_number' THEN", + " ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", + " 'id', t1_obj_t2_contacts_t3_target_t2.id,", + " 'name', t1_obj_t2_contacts_t3_target_t2.name,", + " 'number', t1_obj_t2_contacts_t3_target_t1.number,", + " 'type', t1_obj_t2_contacts_t3_target_t2.type", + " )", + " FROM agreego.phone_number t1_obj_t2_contacts_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_contacts_t3_target_t1.archived", + " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", + " ))", + " WHEN t1_obj_t2_contacts_t3.target_type = 'email_address' THEN", + " ((SELECT jsonb_build_object(", + " 'address', t1_obj_t2_contacts_t3_target_t1.address,", + " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", + " 'id', t1_obj_t2_contacts_t3_target_t2.id,", + " 'name', t1_obj_t2_contacts_t3_target_t2.name,", + " 'type', t1_obj_t2_contacts_t3_target_t2.type", + " )", + " FROM agreego.email_address t1_obj_t2_contacts_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_contacts_t3_target_t1.archived", + " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", + " ))", + " WHEN t1_obj_t2_contacts_t3.target_type = 'address' THEN", + " ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", + " 'city', t1_obj_t2_contacts_t3_target_t1.city,", + " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", + " 'id', t1_obj_t2_contacts_t3_target_t2.id,", + " 'name', t1_obj_t2_contacts_t3_target_t2.name,", + " 'type', t1_obj_t2_contacts_t3_target_t2.type", + " )", + " FROM agreego.address t1_obj_t2_contacts_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_contacts_t3_target_t1.archived", + " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", + " ))", + " ELSE NULL", + " END,", + " 'type', t1_obj_t2_contacts_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_contacts_t1", + " JOIN agreego.relationship t1_obj_t2_contacts_t2 ON t1_obj_t2_contacts_t2.id = t1_obj_t2_contacts_t1.id", + " JOIN agreego.entity t1_obj_t2_contacts_t3 ON t1_obj_t2_contacts_t3.id = t1_obj_t2_contacts_t2.id", + " WHERE", + " NOT t1_obj_t2_contacts_t1.archived", + " AND t1_obj_t2_contacts_t1.parent_id = t1_obj_t2.id),", + " 'created_at', t1_obj_t2.created_at,", + " 'email_addresses',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_email_addresses_t3.archived,", + " 'created_at', t1_obj_t2_email_addresses_t3.created_at,", + " 'id', t1_obj_t2_email_addresses_t3.id,", + " 'is_primary', t1_obj_t2_email_addresses_t1.is_primary,", + " 'name', t1_obj_t2_email_addresses_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'address', t1_obj_t2_email_addresses_t3_target_t1.address,", + " 'archived', t1_obj_t2_email_addresses_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_email_addresses_t3_target_t2.created_at,", + " 'id', t1_obj_t2_email_addresses_t3_target_t2.id,", + " 'name', t1_obj_t2_email_addresses_t3_target_t2.name,", + " 'type', t1_obj_t2_email_addresses_t3_target_t2.type", + " )", + " FROM agreego.email_address t1_obj_t2_email_addresses_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_email_addresses_t3_target_t2 ON t1_obj_t2_email_addresses_t3_target_t2.id = t1_obj_t2_email_addresses_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_email_addresses_t3_target_t1.archived", + " AND t1_obj_t2_email_addresses_t3_target_t1.id = t1_obj_t2_email_addresses_t3.target_id", + " ),", + " 'type', t1_obj_t2_email_addresses_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_email_addresses_t1", + " JOIN agreego.relationship t1_obj_t2_email_addresses_t2 ON t1_obj_t2_email_addresses_t2.id = t1_obj_t2_email_addresses_t1.id", + " JOIN agreego.entity t1_obj_t2_email_addresses_t3 ON t1_obj_t2_email_addresses_t3.id = t1_obj_t2_email_addresses_t2.id", + " WHERE", + " NOT t1_obj_t2_email_addresses_t1.archived", + " AND t1_obj_t2_email_addresses_t1.parent_id = t1_obj_t2.id),", + " 'first_name', t1_obj_t1.first_name,", + " 'id', t1_obj_t2.id,", + " 'last_name', t1_obj_t1.last_name,", + " 'name', t1_obj_t2.name,", + " 'phone_numbers',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_phone_numbers_t3.archived,", + " 'created_at', t1_obj_t2_phone_numbers_t3.created_at,", + " 'id', t1_obj_t2_phone_numbers_t3.id,", + " 'is_primary', t1_obj_t2_phone_numbers_t1.is_primary,", + " 'name', t1_obj_t2_phone_numbers_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_phone_numbers_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_phone_numbers_t3_target_t2.created_at,", + " 'id', t1_obj_t2_phone_numbers_t3_target_t2.id,", + " 'name', t1_obj_t2_phone_numbers_t3_target_t2.name,", + " 'number', t1_obj_t2_phone_numbers_t3_target_t1.number,", + " 'type', t1_obj_t2_phone_numbers_t3_target_t2.type", + " )", + " FROM agreego.phone_number t1_obj_t2_phone_numbers_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_phone_numbers_t3_target_t2 ON t1_obj_t2_phone_numbers_t3_target_t2.id = t1_obj_t2_phone_numbers_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_phone_numbers_t3_target_t1.archived", + " AND t1_obj_t2_phone_numbers_t3_target_t1.id = t1_obj_t2_phone_numbers_t3.target_id", + " ),", + " 'type', t1_obj_t2_phone_numbers_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_phone_numbers_t1", + " JOIN agreego.relationship t1_obj_t2_phone_numbers_t2 ON t1_obj_t2_phone_numbers_t2.id = t1_obj_t2_phone_numbers_t1.id", + " JOIN agreego.entity t1_obj_t2_phone_numbers_t3 ON t1_obj_t2_phone_numbers_t3.id = t1_obj_t2_phone_numbers_t2.id", + " WHERE", + " NOT t1_obj_t2_phone_numbers_t1.archived", + " AND t1_obj_t2_phone_numbers_t1.parent_id = t1_obj_t2.id),", + " 'type', t1_obj_t2.type", + ")", + "FROM agreego.person t1_obj_t1", + "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Person select on full schema with filters", + "action": "query", + "schema_id": "full.person", + "filters": { + "age": { + "$eq": 30, + "$gt": 20, + "$gte": 20, + "$in": [ + 30, + 40 + ], + "$lt": 50, + "$lte": 50, + "$ne": 25, + "$nin": [ + 1, + 2 + ] + }, + "archived": { + "$eq": true, + "$ne": false + }, + "contacts.#.is_primary": { + "$eq": true + }, + "created_at": { + "$eq": "2020-01-01T00:00:00Z", + "$gt": "2019-01-01T00:00:00Z", + "$gte": "2019-01-01T00:00:00Z", + "$lt": "2021-01-01T00:00:00Z", + "$lte": "2021-01-01T00:00:00Z", + "$ne": "2022-01-01T00:00:00Z" + }, + "first_name": { + "$eq": "Jane%", + "$gt": "A", + "$gte": "A", + "$in": [ + "Jane", + "John" + ], + "$lt": "Z", + "$lte": "Z", + "$ne": "Doe", + "$nin": [ + "Bob" + ] + }, + "id": { + "$eq": "00000000-0000-0000-0000-000000000001", + "$in": [ + "00000000-0000-0000-0000-000000000001" + ], + "$ne": "00000000-0000-0000-0000-000000000002", + "$nin": [ + "00000000-0000-0000-0000-000000000002" + ] + }, + "last_name": { + "$eq": "%Doe%", + "$ne": "%Smith%" + }, + "phone_numbers.#.target.number": { + "$eq": "555-1234" + } + }, + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'addresses',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_addresses_t3.archived,", + " 'created_at', t1_obj_t2_addresses_t3.created_at,", + " 'id', t1_obj_t2_addresses_t3.id,", + " 'is_primary', t1_obj_t2_addresses_t1.is_primary,", + " 'name', t1_obj_t2_addresses_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_addresses_t3_target_t2.archived,", + " 'city', t1_obj_t2_addresses_t3_target_t1.city,", + " 'created_at', t1_obj_t2_addresses_t3_target_t2.created_at,", + " 'id', t1_obj_t2_addresses_t3_target_t2.id,", + " 'name', t1_obj_t2_addresses_t3_target_t2.name,", + " 'type', t1_obj_t2_addresses_t3_target_t2.type", + " )", + " FROM agreego.address t1_obj_t2_addresses_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_addresses_t3_target_t2 ON t1_obj_t2_addresses_t3_target_t2.id = t1_obj_t2_addresses_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_addresses_t3_target_t1.archived", + " AND t1_obj_t2_addresses_t3_target_t1.id = t1_obj_t2_addresses_t3.target_id", + " ),", + " 'type', t1_obj_t2_addresses_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_addresses_t1", + " JOIN agreego.relationship t1_obj_t2_addresses_t2 ON t1_obj_t2_addresses_t2.id = t1_obj_t2_addresses_t1.id", + " JOIN agreego.entity t1_obj_t2_addresses_t3 ON t1_obj_t2_addresses_t3.id = t1_obj_t2_addresses_t2.id", + " WHERE", + " NOT t1_obj_t2_addresses_t1.archived", + " AND t1_obj_t2_addresses_t1.parent_id = t1_obj_t2.id),", + " 'age', t1_obj_t1.age,", + " 'archived', t1_obj_t2.archived,", + " 'contacts',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_contacts_t3.archived,", + " 'created_at', t1_obj_t2_contacts_t3.created_at,", + " 'id', t1_obj_t2_contacts_t3.id,", + " 'is_primary', t1_obj_t2_contacts_t1.is_primary,", + " 'name', t1_obj_t2_contacts_t3.name,", + " 'target', CASE", + " WHEN t1_obj_t2_contacts_t3.target_type = 'phone_number' THEN", + " ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", + " 'id', t1_obj_t2_contacts_t3_target_t2.id,", + " 'name', t1_obj_t2_contacts_t3_target_t2.name,", + " 'number', t1_obj_t2_contacts_t3_target_t1.number,", + " 'type', t1_obj_t2_contacts_t3_target_t2.type", + " )", + " FROM agreego.phone_number t1_obj_t2_contacts_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_contacts_t3_target_t1.archived", + " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", + " ))", + " WHEN t1_obj_t2_contacts_t3.target_type = 'email_address' THEN", + " ((SELECT jsonb_build_object(", + " 'address', t1_obj_t2_contacts_t3_target_t1.address,", + " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", + " 'id', t1_obj_t2_contacts_t3_target_t2.id,", + " 'name', t1_obj_t2_contacts_t3_target_t2.name,", + " 'type', t1_obj_t2_contacts_t3_target_t2.type", + " )", + " FROM agreego.email_address t1_obj_t2_contacts_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_contacts_t3_target_t1.archived", + " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", + " ))", + " WHEN t1_obj_t2_contacts_t3.target_type = 'address' THEN", + " ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_contacts_t3_target_t2.archived,", + " 'city', t1_obj_t2_contacts_t3_target_t1.city,", + " 'created_at', t1_obj_t2_contacts_t3_target_t2.created_at,", + " 'id', t1_obj_t2_contacts_t3_target_t2.id,", + " 'name', t1_obj_t2_contacts_t3_target_t2.name,", + " 'type', t1_obj_t2_contacts_t3_target_t2.type", + " )", + " FROM agreego.address t1_obj_t2_contacts_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_contacts_t3_target_t2 ON t1_obj_t2_contacts_t3_target_t2.id = t1_obj_t2_contacts_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_contacts_t3_target_t1.archived", + " AND t1_obj_t2_contacts_t3_target_t1.id = t1_obj_t2_contacts_t3.target_id", + " ))", + " ELSE NULL", + " END,", + " 'type', t1_obj_t2_contacts_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_contacts_t1", + " JOIN agreego.relationship t1_obj_t2_contacts_t2 ON t1_obj_t2_contacts_t2.id = t1_obj_t2_contacts_t1.id", + " JOIN agreego.entity t1_obj_t2_contacts_t3 ON t1_obj_t2_contacts_t3.id = t1_obj_t2_contacts_t2.id", + " WHERE", + " NOT t1_obj_t2_contacts_t1.archived", + " AND t1_obj_t2_contacts_t1.is_primary = ($11#>>'{}')::boolean", + " AND t1_obj_t2_contacts_t1.parent_id = t1_obj_t2.id),", + " 'created_at', t1_obj_t2.created_at,", + " 'email_addresses',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_email_addresses_t3.archived,", + " 'created_at', t1_obj_t2_email_addresses_t3.created_at,", + " 'id', t1_obj_t2_email_addresses_t3.id,", + " 'is_primary', t1_obj_t2_email_addresses_t1.is_primary,", + " 'name', t1_obj_t2_email_addresses_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'address', t1_obj_t2_email_addresses_t3_target_t1.address,", + " 'archived', t1_obj_t2_email_addresses_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_email_addresses_t3_target_t2.created_at,", + " 'id', t1_obj_t2_email_addresses_t3_target_t2.id,", + " 'name', t1_obj_t2_email_addresses_t3_target_t2.name,", + " 'type', t1_obj_t2_email_addresses_t3_target_t2.type", + " )", + " FROM agreego.email_address t1_obj_t2_email_addresses_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_email_addresses_t3_target_t2 ON t1_obj_t2_email_addresses_t3_target_t2.id = t1_obj_t2_email_addresses_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_email_addresses_t3_target_t1.archived", + " AND t1_obj_t2_email_addresses_t3_target_t1.id = t1_obj_t2_email_addresses_t3.target_id", + " ),", + " 'type', t1_obj_t2_email_addresses_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_email_addresses_t1", + " JOIN agreego.relationship t1_obj_t2_email_addresses_t2 ON t1_obj_t2_email_addresses_t2.id = t1_obj_t2_email_addresses_t1.id", + " JOIN agreego.entity t1_obj_t2_email_addresses_t3 ON t1_obj_t2_email_addresses_t3.id = t1_obj_t2_email_addresses_t2.id", + " WHERE", + " NOT t1_obj_t2_email_addresses_t1.archived", + " AND t1_obj_t2_email_addresses_t1.parent_id = t1_obj_t2.id),", + " 'first_name', t1_obj_t1.first_name,", + " 'id', t1_obj_t2.id,", + " 'last_name', t1_obj_t1.last_name,", + " 'name', t1_obj_t2.name,", + " 'phone_numbers',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_phone_numbers_t3.archived,", + " 'created_at', t1_obj_t2_phone_numbers_t3.created_at,", + " 'id', t1_obj_t2_phone_numbers_t3.id,", + " 'is_primary', t1_obj_t2_phone_numbers_t1.is_primary,", + " 'name', t1_obj_t2_phone_numbers_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'archived', t1_obj_t2_phone_numbers_t3_target_t2.archived,", + " 'created_at', t1_obj_t2_phone_numbers_t3_target_t2.created_at,", + " 'id', t1_obj_t2_phone_numbers_t3_target_t2.id,", + " 'name', t1_obj_t2_phone_numbers_t3_target_t2.name,", + " 'number', t1_obj_t2_phone_numbers_t3_target_t1.number,", + " 'type', t1_obj_t2_phone_numbers_t3_target_t2.type", + " )", + " FROM agreego.phone_number t1_obj_t2_phone_numbers_t3_target_t1", + " JOIN agreego.entity t1_obj_t2_phone_numbers_t3_target_t2 ON t1_obj_t2_phone_numbers_t3_target_t2.id = t1_obj_t2_phone_numbers_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t2_phone_numbers_t3_target_t1.archived", + " AND t1_obj_t2_phone_numbers_t3_target_t1.number ILIKE $32#>>'{}'", + " AND t1_obj_t2_phone_numbers_t3_target_t1.id = t1_obj_t2_phone_numbers_t3.target_id", + " ),", + " 'type', t1_obj_t2_phone_numbers_t3.type", + " )), '[]'::jsonb)", + " FROM agreego.contact t1_obj_t2_phone_numbers_t1", + " JOIN agreego.relationship t1_obj_t2_phone_numbers_t2 ON t1_obj_t2_phone_numbers_t2.id = t1_obj_t2_phone_numbers_t1.id", + " JOIN agreego.entity t1_obj_t2_phone_numbers_t3 ON t1_obj_t2_phone_numbers_t3.id = t1_obj_t2_phone_numbers_t2.id", + " WHERE", + " NOT t1_obj_t2_phone_numbers_t1.archived", + " AND t1_obj_t2_phone_numbers_t1.parent_id = t1_obj_t2.id),", + " 'type', t1_obj_t2.type", + ")", + "FROM agreego.person t1_obj_t1", + "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "WHERE", + " NOT t1_obj_t1.archived", + " AND t1_obj_t1.age = ($1#>>'{}')::numeric", + " AND t1_obj_t1.age > ($2#>>'{}')::numeric", + " AND t1_obj_t1.age >= ($3#>>'{}')::numeric", + " AND t1_obj_t1.age IN (SELECT value::numeric FROM jsonb_array_elements_text(($4#>>'{}')::jsonb))", + " AND t1_obj_t1.age < ($5#>>'{}')::numeric", + " AND t1_obj_t1.age <= ($6#>>'{}')::numeric", + " AND t1_obj_t1.age != ($7#>>'{}')::numeric", + " AND t1_obj_t1.age NOT IN (SELECT value::numeric FROM jsonb_array_elements_text(($8#>>'{}')::jsonb))", + " AND t1_obj_t2.archived = ($9#>>'{}')::boolean", + " AND t1_obj_t2.archived != ($10#>>'{}')::boolean", + " AND t1_obj_t2.created_at = ($12#>>'{}')::timestamptz", + " AND t1_obj_t2.created_at > ($13#>>'{}')::timestamptz", + " AND t1_obj_t2.created_at >= ($14#>>'{}')::timestamptz", + " AND t1_obj_t2.created_at < ($15#>>'{}')::timestamptz", + " AND t1_obj_t2.created_at <= ($16#>>'{}')::timestamptz", + " AND t1_obj_t2.created_at != ($17#>>'{}')::timestamptz", + " AND t1_obj_t1.first_name ILIKE $18#>>'{}'", + " AND t1_obj_t1.first_name > ($19#>>'{}')", + " AND t1_obj_t1.first_name >= ($20#>>'{}')", + " AND t1_obj_t1.first_name IN (SELECT value FROM jsonb_array_elements_text(($21#>>'{}')::jsonb))", + " AND t1_obj_t1.first_name < ($22#>>'{}')", + " AND t1_obj_t1.first_name <= ($23#>>'{}')", + " AND t1_obj_t1.first_name NOT ILIKE $24#>>'{}'", + " AND t1_obj_t1.first_name NOT IN (SELECT value FROM jsonb_array_elements_text(($25#>>'{}')::jsonb))", + " AND t1_obj_t2.id = ($26#>>'{}')::uuid", + " AND t1_obj_t2.id IN (SELECT value::uuid FROM jsonb_array_elements_text(($27#>>'{}')::jsonb))", + " AND t1_obj_t2.id != ($28#>>'{}')::uuid", + " AND t1_obj_t2.id NOT IN (SELECT value::uuid FROM jsonb_array_elements_text(($29#>>'{}')::jsonb))", + " AND t1_obj_t1.last_name ILIKE $30#>>'{}'", + " AND t1_obj_t1.last_name NOT ILIKE $31#>>'{}'", + ")" + ] + ] + } + }, + { + "description": "Full person stem query on phone number contact", + "action": "query", + "schema_id": "full.person", + "stem": "phone_numbers.#", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'archived', t1_obj_t3.archived,", + " 'created_at', t1_obj_t3.created_at,", + " 'id', t1_obj_t3.id,", + " 'is_primary', t1_obj_t1.is_primary,", + " 'name', t1_obj_t3.name,", + " 'target',", + " (SELECT jsonb_build_object(", + " 'archived', t1_obj_t3_target_t2.archived,", + " 'created_at', t1_obj_t3_target_t2.created_at,", + " 'id', t1_obj_t3_target_t2.id,", + " 'name', t1_obj_t3_target_t2.name,", + " 'number', t1_obj_t3_target_t1.number,", + " 'type', t1_obj_t3_target_t2.type", + " )", + " FROM agreego.phone_number t1_obj_t3_target_t1", + " JOIN agreego.entity t1_obj_t3_target_t2 ON t1_obj_t3_target_t2.id = t1_obj_t3_target_t1.id", + " WHERE", + " NOT t1_obj_t3_target_t1.archived", + " AND t1_obj_t3_target_t1.id = t1_obj_t3.target_id),", + " 'type', t1_obj_t3.type", + ")", + "FROM agreego.contact t1_obj_t1", + "JOIN agreego.relationship t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "JOIN agreego.entity t1_obj_t3 ON t1_obj_t3.id = t1_obj_t2.id", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Full person stem query on phone number contact on phone number", + "action": "query", + "schema_id": "full.person", + "stem": "phone_numbers.#.target", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'archived', t1_obj_t2.archived,", + " 'created_at', t1_obj_t2.created_at,", + " 'id', t1_obj_t2.id,", + " 'name', t1_obj_t2.name,", + " 'number', t1_obj_t1.number,", + " 'type', t1_obj_t2.type)", + "FROM agreego.phone_number t1_obj_t1", + "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Full person stem query on contact email address", + "action": "query", + "schema_id": "full.person", + "stem": "contacts.#.target#(type==\"email_address\")", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'address', t1_obj_t1.address,", + " 'archived', t1_obj_t2.archived,", + " 'created_at', t1_obj_t2.created_at,", + " 'id', t1_obj_t2.id,", + " 'name', t1_obj_t2.name,", + " 'type', t1_obj_t2.type)", + "FROM agreego.email_address t1_obj_t1", + "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Order select with customer and lines", + "action": "query", + "schema_id": "full.order", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'archived', t1_obj_t2.archived,", + " 'created_at', t1_obj_t2.created_at,", + " 'customer',", + " (SELECT jsonb_build_object(", + " 'age', t1_obj_t2_customer_t1.age,", + " 'archived', t1_obj_t2_customer_t2.archived,", + " 'created_at', t1_obj_t2_customer_t2.created_at,", + " 'first_name', t1_obj_t2_customer_t1.first_name,", + " 'id', t1_obj_t2_customer_t2.id,", + " 'last_name', t1_obj_t2_customer_t1.last_name,", + " 'name', t1_obj_t2_customer_t2.name,", + " 'type', t1_obj_t2_customer_t2.type", + " )", + " FROM agreego.person t1_obj_t2_customer_t1", + " JOIN agreego.entity t1_obj_t2_customer_t2 ON t1_obj_t2_customer_t2.id = t1_obj_t2_customer_t1.id", + " WHERE", + " NOT t1_obj_t2_customer_t1.archived", + " AND t1_obj_t2_customer_t1.parent_id = t1_obj_t2.id),", + " 'customer_id', t1_obj_t1.customer_id,", + " 'id', t1_obj_t2.id,", + " 'lines',", + " (SELECT COALESCE(jsonb_agg(jsonb_build_object(", + " 'archived', t1_obj_t2_lines_t2.archived,", + " 'created_at', t1_obj_t2_lines_t2.created_at,", + " 'id', t1_obj_t2_lines_t2.id,", + " 'name', t1_obj_t2_lines_t2.name,", + " 'order_id', t1_obj_t2_lines_t1.order_id,", + " 'price', t1_obj_t2_lines_t1.price,", + " 'product', t1_obj_t2_lines_t1.product,", + " 'type', t1_obj_t2_lines_t2.type", + " )), '[]'::jsonb)", + " FROM agreego.order_line t1_obj_t2_lines_t1", + " JOIN agreego.entity t1_obj_t2_lines_t2 ON t1_obj_t2_lines_t2.id = t1_obj_t2_lines_t1.id", + " WHERE", + " NOT t1_obj_t2_lines_t1.archived", + " AND t1_obj_t2_lines_t1.parent_id = t1_obj_t2.id),", + " 'name', t1_obj_t2.name,", + " 'total', t1_obj_t1.total,", + " 'type', t1_obj_t2.type", + ")", + "FROM agreego.order t1_obj_t1", + "JOIN agreego.entity t1_obj_t2 ON t1_obj_t2.id = t1_obj_t1.id", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + }, + { + "description": "Base entity family select on polymorphic tree", + "action": "query", + "schema_id": "get_entities.response", + "expect": { + "success": true, + "sql": [ + [ + "(SELECT jsonb_build_object(", + " 'id', t1_obj_t1.id,", + " 'type', CASE WHEN t1_obj_t1.type = 'address' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'city', t1_obj_t1_obj_t1.city,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.address t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'contact' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t3.archived,", + " 'created_at', t1_obj_t1_obj_t3.created_at,", + " 'id', t1_obj_t1_obj_t3.id,", + " 'is_primary', t1_obj_t1_obj_t1.is_primary,", + " 'name', t1_obj_t1_obj_t3.name,", + " 'type', t1_obj_t1_obj_t3.type)", + " FROM agreego.contact t1_obj_t1_obj_t1", + " JOIN agreego.relationship t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " JOIN agreego.entity t1_obj_t1_obj_t3 ON t1_obj_t1_obj_t3.id = t1_obj_t1_obj_t2.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'email_address' THEN ((SELECT jsonb_build_object(", + " 'address', t1_obj_t1_obj_t1.address,", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.email_address t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'entity' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t1.archived,", + " 'created_at', t1_obj_t1_obj_t1.created_at,", + " 'id', t1_obj_t1_obj_t1.id,", + " 'name', t1_obj_t1_obj_t1.name,", + " 'type', t1_obj_t1_obj_t1.type)", + " FROM agreego.entity t1_obj_t1_obj_t1", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'order' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'customer_id', t1_obj_t1_obj_t1.customer_id,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'total', t1_obj_t1_obj_t1.total,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.order t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'order_line' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'order_id', t1_obj_t1_obj_t1.order_id,", + " 'price', t1_obj_t1_obj_t1.price,", + " 'product', t1_obj_t1_obj_t1.product,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.order_line t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'organization' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.organization t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'person' THEN ((SELECT jsonb_build_object(", + " 'age', t1_obj_t1_obj_t1.age,", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'first_name', t1_obj_t1_obj_t1.first_name,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'last_name', t1_obj_t1_obj_t1.last_name,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.person t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'phone_number' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'number', t1_obj_t1_obj_t1.number,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.phone_number t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " WHEN t1_obj_t1.type = 'relationship' THEN ((SELECT jsonb_build_object(", + " 'archived', t1_obj_t1_obj_t2.archived,", + " 'created_at', t1_obj_t1_obj_t2.created_at,", + " 'id', t1_obj_t1_obj_t2.id,", + " 'name', t1_obj_t1_obj_t2.name,", + " 'type', t1_obj_t1_obj_t2.type)", + " FROM agreego.relationship t1_obj_t1_obj_t1", + " JOIN agreego.entity t1_obj_t1_obj_t2 ON t1_obj_t1_obj_t2.id = t1_obj_t1_obj_t1.id", + " WHERE NOT t1_obj_t1_obj_t1.archived))", + " ELSE NULL END)", + "FROM agreego.entity t1_obj_t1", + "WHERE NOT t1_obj_t1.archived)" + ] + ] + } + } + ] + } ] \ No newline at end of file diff --git a/src/bin/ast_explore.rs b/src/bin/ast_explore.rs new file mode 100644 index 0000000..1ecefba --- /dev/null +++ b/src/bin/ast_explore.rs @@ -0,0 +1,17 @@ +use sqlparser::dialect::PostgreSqlDialect; +use sqlparser::parser::Parser; +use std::env; + +fn main() { + let sql = "SELECT t1_obj_t1_addresses_t1_target_t2.archived, t1.id FROM person t1 JOIN address t1_obj_t1_addresses ON true"; + let dialect = PostgreSqlDialect {}; + + match Parser::parse_sql(&dialect, sql) { + Ok(ast) => { + println!("{:#?}", ast); + } + Err(e) => { + println!("Error: {:?}", e); + } + } +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 9cfc000..649fa83 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -2,6 +2,7 @@ use crate::*; pub mod runner; pub mod types; use serde_json::json; +pub mod sql_validator; // Database module tests moved to src/database/executors/mock.rs diff --git a/src/tests/sql_validator.rs b/src/tests/sql_validator.rs new file mode 100644 index 0000000..f5b04e9 --- /dev/null +++ b/src/tests/sql_validator.rs @@ -0,0 +1,156 @@ +use sqlparser::ast::{ + Expr, Join, JoinConstraint, JoinOperator, Query, Select, SelectItem, SetExpr, Statement, + TableFactor, TableWithJoins, Ident, +}; +use sqlparser::dialect::PostgreSqlDialect; +use sqlparser::parser::Parser; +use std::collections::HashSet; + +pub fn validate_semantic_sql(sql: &str) -> Result<(), String> { + let dialect = PostgreSqlDialect {}; + let statements = match Parser::parse_sql(&dialect, sql) { + Ok(s) => s, + Err(e) => return Err(format!("SQL Syntax Error: {}\nSQL: {}", e, sql)), + }; + + for statement in statements { + validate_statement(&statement, sql)?; + } + + Ok(()) +} + +fn validate_statement(stmt: &Statement, original_sql: &str) -> Result<(), String> { + match stmt { + Statement::Query(query) => validate_query(query, original_sql)?, + Statement::Insert(insert) => { + if let Some(query) = &insert.source { + validate_query(query, original_sql)? + } + } + Statement::Update(update) => { + if let Some(expr) = &update.selection { + validate_expr(expr, &HashSet::new(), original_sql)?; + } + } + Statement::Delete(delete) => { + if let Some(expr) = &delete.selection { + validate_expr(expr, &HashSet::new(), original_sql)?; + } + } + _ => {} + } + Ok(()) +} + +fn validate_query(query: &Query, original_sql: &str) -> Result<(), String> { + if let SetExpr::Select(select) = &*query.body { + validate_select(select, original_sql)?; + } + Ok(()) +} + +fn validate_select(select: &Select, original_sql: &str) -> Result<(), String> { + let mut available_aliases = HashSet::new(); + + // 1. Collect all declared table aliases in the FROM clause and JOINs + for table_with_joins in &select.from { + collect_aliases_from_table_factor(&table_with_joins.relation, &mut available_aliases); + for join in &table_with_joins.joins { + collect_aliases_from_table_factor(&join.relation, &mut available_aliases); + } + } + + // 2. Validate all SELECT projection fields + for projection in &select.projection { + if let SelectItem::UnnamedExpr(expr) | SelectItem::ExprWithAlias { expr, .. } = projection { + validate_expr(expr, &available_aliases, original_sql)?; + } + } + + // 3. Validate ON conditions in joins + for table_with_joins in &select.from { + for join in &table_with_joins.joins { + if let JoinOperator::Inner(JoinConstraint::On(expr)) + | JoinOperator::LeftOuter(JoinConstraint::On(expr)) + | JoinOperator::RightOuter(JoinConstraint::On(expr)) + | JoinOperator::FullOuter(JoinConstraint::On(expr)) + | JoinOperator::Join(JoinConstraint::On(expr)) = &join.join_operator + { + validate_expr(expr, &available_aliases, original_sql)?; + } + } + } + + // 4. Validate WHERE conditions + if let Some(selection) = &select.selection { + validate_expr(selection, &available_aliases, original_sql)?; + } + + Ok(()) +} + +fn collect_aliases_from_table_factor(tf: &TableFactor, aliases: &mut HashSet) { + match tf { + TableFactor::Table { name, alias, .. } => { + if let Some(table_alias) = alias { + aliases.insert(table_alias.name.value.clone()); + } else if let Some(last) = name.0.last() { + match last { + sqlparser::ast::ObjectNamePart::Identifier(i) => { + aliases.insert(i.value.clone()); + } + _ => {} + } + } + } + TableFactor::Derived { alias: Some(table_alias), .. } => { + aliases.insert(table_alias.name.value.clone()); + } + _ => {} + } +} + +fn validate_expr(expr: &Expr, available_aliases: &HashSet, sql: &str) -> Result<(), String> { + match expr { + Expr::CompoundIdentifier(idents) => { + if idents.len() == 2 { + let alias = &idents[0].value; + if !available_aliases.is_empty() && !available_aliases.contains(alias) { + return Err(format!( + "Semantic Error: Orchestrated query referenced table alias '{}' but it was not declared in the query's FROM/JOIN clauses.\nAvailable aliases: {:?}\nSQL: {}", + alias, available_aliases, sql + )); + } + } else if idents.len() > 2 { + let alias = &idents[1].value; // In form schema.table.column, 'table' is idents[1] + if !available_aliases.is_empty() && !available_aliases.contains(alias) { + return Err(format!( + "Semantic Error: Orchestrated query referenced table '{}' but it was not mapped.\nAvailable aliases: {:?}\nSQL: {}", + alias, available_aliases, sql + )); + } + } + } + Expr::BinaryOp { left, right, .. } => { + validate_expr(left, available_aliases, sql)?; + validate_expr(right, available_aliases, sql)?; + } + Expr::IsFalse(e) | Expr::IsNotFalse(e) | Expr::IsTrue(e) | Expr::IsNotTrue(e) + | Expr::IsNull(e) | Expr::IsNotNull(e) | Expr::InList { expr: e, .. } + | Expr::Nested(e) | Expr::UnaryOp { expr: e, .. } | Expr::Cast { expr: e, .. } + | Expr::Like { expr: e, .. } | Expr::ILike { expr: e, .. } | Expr::AnyOp { left: e, .. } + | Expr::AllOp { left: e, .. } => { + validate_expr(e, available_aliases, sql)?; + } + Expr::Function(func) => { + if let sqlparser::ast::FunctionArguments::List(args) = &func.args { + if let Some(sqlparser::ast::FunctionArg::Unnamed(sqlparser::ast::FunctionArgExpr::Expr(e))) = args.args.get(0) { + validate_expr(e, available_aliases, sql)?; + } + } + } + _ => {} + } + Ok(()) +} diff --git a/src/tests/types/expect.rs b/src/tests/types/expect.rs index 68e7b38..a9d9f84 100644 --- a/src/tests/types/expect.rs +++ b/src/tests/types/expect.rs @@ -39,6 +39,12 @@ impl ExpectBlock { )); } + for query in actual { + if let Err(e) = crate::tests::sql_validator::validate_semantic_sql(query) { + return Err(e); + } + } + let ws_re = Regex::new(r"\s+").unwrap(); let types = HashMap::from([