Compare commits

..

18 Commits

Author SHA1 Message Date
a006d3f00a version: 1.0.178 2026-07-09 14:48:03 -04:00
942520a36a added static property constraints using const and enums and fixed formatter ON rendering 2026-07-09 14:47:39 -04:00
1bb9586b9b Merge branch 'main' of gitea-ssh.thoughtpatterns.ai:cellular/jspg 2026-07-09 14:46:03 -04:00
b4882bcb27 generator checkpoint 2026-07-09 14:27:50 -04:00
3bb7eb312a generator checkpoint 2026-07-09 14:26:45 -04:00
73ab6d4ce7 version: 1.0.177 2026-07-09 12:44:55 -04:00
cef65958cb reversed jsonb subquerying for now 2026-07-07 23:10:43 -04:00
95600551a7 version: 1.0.172 2026-07-07 12:45:00 -04:00
c18a8c1561 remove icon from sidebar 2026-07-07 12:44:49 -04:00
8ed588ca61 version: 1.0.171 2026-07-07 12:42:56 -04:00
edd0dd4763 jspg cleanup 2026-07-07 12:42:05 -04:00
abf1d12e43 test: mock matcher understands UNION existence reads; revive dead update-path mocks
parse_and_match_mocks matched mock.type against only the first FROM table —
always 'entity' for hierarchy existence reads — so every merger update-case
mock was silently inert and those cases snapshotted the create path. Now each
UNION arm is matched independently (a mock matching any arm returns once,
mirroring UNION dedup) and the type check accepts any FROM/JOIN table.

Regenerated the five revived cases (their snapshots now show real update /
no-op replace / archive semantics) and added the two previously untestable
probe-disagreement cases: same-row dedup (updates, no error) and id-vs-lookup
conflict (TOO_MANY_LOOKUP_ROWS).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:05:21 -04:00
a885713563 version: 1.0.170 2026-07-06 13:05:16 -04:00
d9b4f417f6 merger existence read: UNION the id and lk-lookup probes instead of OR-ing them across the hierarchy join — the OR is un-indexable and full-scans the subtype table on every child merge (one scan per relationship-edge write; surfaced by Castleberry onboarding); each UNION arm uses its own index (pk / lk_) and dedup preserves TOO_MANY_LOOKUP_ROWS semantics
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 20:52:29 -04:00
eae39f92d6 version: 1.0.169 2026-07-04 18:27:57 -04:00
2895f06074 page.personal is an opaque filter template (the 'mine' predicate), not a punc name — same read, scope-derived filter
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 18:27:51 -04:00
c1c16bc814 version: 1.0.168 2026-07-04 18:21:36 -04:00
5885552192 database: page carries 'personal' (the personal-scope sibling punc) and sidebar carries 'icon' — previously stripped by the whitelist
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 18:21:30 -04:00
39 changed files with 1159 additions and 732 deletions

View File

@ -72,8 +72,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "1",
"count": "0"
"count": "0",
"limit": "1"
},
"details": {
"path": "",
@ -177,8 +177,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "1",
"count": "0"
"count": "0",
"limit": "1"
},
"details": {
"path": "",
@ -227,8 +227,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "0",
"limit": "1"
"limit": "1",
"count": "0"
},
"details": {
"path": "",
@ -268,8 +268,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "0",
"limit": "1"
"limit": "1",
"count": "0"
},
"details": {
"path": "",
@ -384,8 +384,8 @@
{
"code": "MULTIPLE_OF_VIOLATED",
"values": {
"value": "9",
"multiple_of": "2"
"multiple_of": "2",
"value": "9"
},
"details": {
"path": "2",
@ -443,8 +443,8 @@
{
"code": "MULTIPLE_OF_VIOLATED",
"values": {
"multiple_of": "2",
"value": "5"
"value": "5",
"multiple_of": "2"
},
"details": {
"path": "1",

View File

@ -62,8 +62,8 @@
"code": "EDGE_MISSING",
"values": {
"parent_type": "org",
"child_type": "user",
"property_name": "missing_users"
"property_name": "missing_users",
"child_type": "user"
},
"details": {
"path": "full.org/missing_users",
@ -151,9 +151,9 @@
{
"code": "EDGE_MISSING",
"values": {
"property_name": "children",
"child_type": "child",
"parent_type": "parent",
"child_type": "child"
"property_name": "children"
},
"details": {
"path": "full.parent/children",
@ -254,9 +254,9 @@
{
"code": "AMBIGUOUS_TYPE_RELATIONS",
"values": {
"parent_type": "invoice",
"child_type": "activity",
"property_name": "activities",
"parent_type": "invoice"
"property_name": "activities"
},
"details": {
"path": "full.invoice/activities",
@ -390,9 +390,9 @@
{
"code": "AMBIGUOUS_TYPE_RELATIONS",
"values": {
"property_name": "ambiguous_edge",
"parent_type": "actor",
"child_type": "junction",
"parent_type": "actor"
"property_name": "ambiguous_edge"
},
"details": {
"path": "full.actor/ambiguous_edge",
@ -470,8 +470,8 @@
{
"code": "DATABASE_TYPE_PARSE_FAILED",
"values": {
"type": "failure",
"reason": "invalid type: sequence, expected a string"
"reason": "invalid type: sequence, expected a string",
"type": "failure"
},
"details": {
"context": "failure"

View File

@ -65,8 +65,8 @@
{
"code": "DEPENDENCY_MISSING",
"values": {
"required_property": "foo",
"property_name": "bar"
"property_name": "bar",
"required_property": "foo"
},
"details": {
"path": "",
@ -276,8 +276,8 @@
{
"code": "DEPENDENCY_MISSING",
"values": {
"required_property": "foo",
"property_name": "quux"
"property_name": "quux",
"required_property": "foo"
},
"details": {
"path": "",
@ -287,8 +287,8 @@
{
"code": "DEPENDENCY_MISSING",
"values": {
"required_property": "bar",
"property_name": "quux"
"property_name": "quux",
"required_property": "bar"
},
"details": {
"path": "",
@ -362,8 +362,8 @@
{
"code": "DEPENDENCY_MISSING",
"values": {
"required_property": "foo\rbar",
"property_name": "foo\nbar"
"property_name": "foo\nbar",
"required_property": "foo\rbar"
},
"details": {
"path": "",
@ -829,8 +829,8 @@
{
"code": "MIN_PROPERTIES_VIOLATED",
"values": {
"count": "2",
"limit": "4"
"limit": "4",
"count": "2"
},
"details": {
"path": "",

View File

@ -128,8 +128,8 @@
{
"code": "PATTERN_VIOLATED",
"values": {
"value": "abcd",
"pattern": "^[a-z]{3}$"
"pattern": "^[a-z]{3}$",
"value": "abcd"
},
"details": {
"path": "keys/abcd",

View File

@ -56,8 +56,8 @@
{
"code": "EXCLUSIVE_MAXIMUM_VIOLATED",
"values": {
"limit": "3",
"value": "3.5"
"value": "3.5",
"limit": "3"
},
"details": {
"path": "",

View File

@ -34,8 +34,8 @@
{
"code": "EXCLUSIVE_MINIMUM_VIOLATED",
"values": {
"value": "1.1",
"limit": "1.1"
"limit": "1.1",
"value": "1.1"
},
"details": {
"path": "",

View File

@ -87,34 +87,6 @@
"type": "string"
}
}
},
"schedule": {
"type": [
"opening_hours",
"null"
]
}
}
},
"season": {
"type": "object",
"properties": {
"label": {
"type": "string"
}
}
},
"opening_hours": {
"type": "object",
"properties": {
"open": {
"type": "string"
},
"seasons": {
"type": "array",
"items": {
"type": "season"
}
}
}
}
@ -239,13 +211,6 @@
"gender": {},
"gender.condition": {
"type": "condition",
"compiledPropertyNames": [
"kind",
"$eq",
"$ne",
"$of",
"$nof"
],
"properties": {
"$eq": {
"type": [
@ -259,15 +224,6 @@
"null"
]
},
"$nof": {
"type": [
"array",
"null"
],
"items": {
"type": "gender"
}
},
"$of": {
"type": [
"array",
@ -276,11 +232,167 @@
"items": {
"type": "gender"
}
},
"$nof": {
"type": [
"array",
"null"
],
"items": {
"type": "gender"
}
}
}
},
"compiledPropertyNames": [
"kind",
"$eq",
"$ne",
"$of",
"$nof"
]
},
"person": {},
"person.filter": {
"type": "filter",
"properties": {
"first_name": {
"type": [
"string.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"age": {
"type": [
"integer.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"billing_address": {
"type": [
"address.filter",
"null"
],
"compiledPropertyNames": [
"city",
"$and",
"$or"
]
},
"gender": {
"type": [
"gender.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq",
"$ne",
"$of",
"$nof"
]
},
"birth_date": {
"type": [
"date.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"uuid_field": {
"type": [
"uuid.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"tags": {
"type": [
"string.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"ad_hoc": {
"type": [
"filter",
"null"
],
"properties": {
"foo": {
"type": [
"string.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
}
},
"compiledPropertyNames": [
"foo"
]
},
"$and": {
"type": [
"array",
"null"
],
"items": {
"type": "person.filter",
"compiledPropertyNames": [
"first_name",
"age",
"billing_address",
"gender",
"birth_date",
"uuid_field",
"tags",
"ad_hoc",
"$and",
"$or"
]
}
},
"$or": {
"type": [
"array",
"null"
],
"items": {
"type": "person.filter",
"compiledPropertyNames": [
"first_name",
"age",
"billing_address",
"gender",
"birth_date",
"uuid_field",
"tags",
"ad_hoc",
"$and",
"$or"
]
}
}
},
"compiledPropertyNames": [
"first_name",
"age",
@ -290,144 +402,36 @@
"uuid_field",
"tags",
"ad_hoc",
"schedule",
"$and",
"$or"
],
"properties": {
"$and": {
"items": {
"compiledPropertyNames": [
"first_name",
"age",
"billing_address",
"gender",
"birth_date",
"uuid_field",
"tags",
"ad_hoc",
"schedule",
"$and",
"$or"
],
"type": "person.filter"
},
"type": [
"array",
"null"
]
},
"$or": {
"items": {
"compiledPropertyNames": [
"first_name",
"age",
"billing_address",
"gender",
"birth_date",
"uuid_field",
"tags",
"ad_hoc",
"schedule",
"$and",
"$or"
],
"type": "person.filter"
},
"type": [
"array",
"null"
]
},
"ad_hoc": {
"compiledPropertyNames": [
"foo"
],
"properties": {
"foo": {
"type": [
"string.condition",
"null"
]
}
},
"type": [
"filter",
"null"
]
},
"age": {
"type": [
"integer.condition",
"null"
]
},
"billing_address": {
"type": [
"address.filter",
"null"
]
},
"birth_date": {
"type": [
"date.condition",
"null"
]
},
"uuid_field": {
"type": [
"uuid.condition",
"null"
]
},
"first_name": {
"type": [
"string.condition",
"null"
]
},
"gender": {
"type": [
"gender.condition",
"null"
]
},
"tags": {
"type": [
"string.condition",
"null"
]
},
"schedule": {
"type": [
"opening_hours.filter",
"null"
]
}
},
"type": "filter"
]
},
"address": {},
"address.filter": {
"type": "filter",
"compiledPropertyNames": [
"city",
"$and",
"$or"
],
"properties": {
"city": {
"type": [
"string.condition",
"null"
],
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"$and": {
"type": [
"array",
"null"
],
"items": {
"type": "address.filter",
"compiledPropertyNames": [
"city",
"$and",
"$or"
],
"type": "address.filter"
]
}
},
"$or": {
@ -436,21 +440,20 @@
"null"
],
"items": {
"type": "address.filter",
"compiledPropertyNames": [
"city",
"$and",
"$or"
],
"type": "address.filter"
]
}
},
"city": {
"type": [
"string.condition",
"null"
]
}
}
},
"compiledPropertyNames": [
"city",
"$and",
"$or"
]
},
"condition": {},
"filter": {},
@ -461,45 +464,26 @@
"search": {},
"search.filter": {
"type": "filter",
"compiledPropertyNames": [
"kind",
"name",
"filter",
"$and",
"$or"
],
"properties": {
"$and": {
"kind": {
"type": [
"array",
"string.condition",
"null"
],
"items": {
"compiledPropertyNames": [
"kind",
"name",
"filter",
"$and",
"$or"
],
"type": "search.filter"
}
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"$or": {
"name": {
"type": [
"array",
"string.condition",
"null"
],
"items": {
"compiledPropertyNames": [
"kind",
"name",
"filter",
"$and",
"$or"
],
"type": "search.filter"
}
"compiledPropertyNames": [
"kind",
"$eq"
]
},
"filter": {
"type": [
@ -507,53 +491,17 @@
"null"
]
},
"kind": {
"type": [
"string.condition",
"null"
]
},
"name": {
"type": [
"string.condition",
"null"
]
}
}
},
"opening_hours": {},
"season": {},
"opening_hours.filter": {
"type": "filter",
"compiledPropertyNames": [
"open",
"seasons",
"$and",
"$or"
],
"properties": {
"open": {
"type": [
"string.condition",
"null"
]
},
"seasons": {
"type": [
"season.filter",
"null"
]
},
"$and": {
"type": [
"array",
"null"
],
"items": {
"type": "opening_hours.filter",
"type": "search.filter",
"compiledPropertyNames": [
"open",
"seasons",
"kind",
"name",
"filter",
"$and",
"$or"
]
@ -565,21 +513,28 @@
"null"
],
"items": {
"type": "opening_hours.filter",
"type": "search.filter",
"compiledPropertyNames": [
"open",
"seasons",
"kind",
"name",
"filter",
"$and",
"$or"
]
}
}
}
},
"season.filter": {}
},
"compiledPropertyNames": [
"kind",
"name",
"filter",
"$and",
"$or"
]
}
}
}
}
]
}
]
]

File diff suppressed because it is too large Load Diff

View File

@ -70,8 +70,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "1",
"count": "0"
"count": "0",
"limit": "1"
},
"details": {
"path": "",
@ -144,8 +144,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "2",
"limit": "1"
"limit": "1",
"count": "2"
},
"details": {
"path": "",

View File

@ -43,8 +43,8 @@
{
"code": "MAX_LENGTH_VIOLATED",
"values": {
"count": "3",
"limit": "2"
"limit": "2",
"count": "3"
},
"details": {
"path": "",
@ -109,8 +109,8 @@
{
"code": "MAX_LENGTH_VIOLATED",
"values": {
"count": "3",
"limit": "2"
"limit": "2",
"count": "3"
},
"details": {
"path": "",

View File

@ -139,8 +139,8 @@
{
"code": "MAX_PROPERTIES_VIOLATED",
"values": {
"count": "3",
"limit": "2"
"limit": "2",
"count": "3"
},
"details": {
"path": "",
@ -190,8 +190,8 @@
{
"code": "MAX_PROPERTIES_VIOLATED",
"values": {
"count": "1",
"limit": "0"
"limit": "0",
"count": "1"
},
"details": {
"path": "",
@ -234,8 +234,8 @@
{
"code": "MAX_PROPERTIES_VIOLATED",
"values": {
"limit": "2",
"count": "3"
"count": "3",
"limit": "2"
},
"details": {
"path": "",

View File

@ -43,8 +43,8 @@
{
"code": "MAXIMUM_VIOLATED",
"values": {
"limit": "3",
"value": "3.5"
"value": "3.5",
"limit": "3"
},
"details": {
"path": "",
@ -118,8 +118,8 @@
{
"code": "MAXIMUM_VIOLATED",
"values": {
"value": "300.5",
"limit": "300"
"limit": "300",
"value": "300.5"
},
"details": {
"path": "",

View File

@ -264,8 +264,8 @@
{
"code": "DEPENDENCY_MISSING",
"values": {
"required_property": "child_dep",
"property_name": "trigger"
"property_name": "trigger",
"required_property": "child_dep"
},
"details": {
"path": "",

View File

@ -1310,9 +1310,9 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"person\" t4 ON ",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" (\"first_name\" = 'LookupFirst'",
" AND \"last_name\" = 'LookupLast'",
@ -1463,12 +1463,18 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"person\" t4 ON ",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}'",
" OR (\"first_name\" = 'LookupFirst'",
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" (\"first_name\" = 'LookupFirst'",
" AND \"last_name\" = 'LookupLast'",
" AND \"date_of_birth\" = '{{timestamp}}'",
" AND \"pronouns\" = 'they/them'))"
@ -1616,12 +1622,18 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"person\" t4 ON ",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}'",
" OR (\"first_name\" = 'LookupFirst'",
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" (\"first_name\" = 'LookupFirst'",
" AND \"last_name\" = 'LookupLast'",
" AND \"date_of_birth\" = '{{timestamp}}'",
" AND \"pronouns\" = 'they/them'))"
@ -1759,9 +1771,9 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"person\" t4 ON ",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '{{uuid:mocks.0.id}}')"
],
@ -1882,9 +1894,9 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"person\" t4 ON ",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = '123')"
],
@ -2221,10 +2233,14 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"order\" t2 ON ",
"LEFT JOIN agreego.\"order\" t2 ON t2.id = t1.id",
"WHERE",
" t1.id = 'abc'",
" OR (\"id\" = 'abc'))"
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"order\" t2 ON t2.id = t1.id",
"WHERE",
" (\"id\" = 'abc'))"
],
[
"INSERT INTO agreego.\"entity\" (",
@ -3024,9 +3040,9 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"organization\" t2 ON ",
"JOIN agreego.\"user\" t3 ON ",
"JOIN agreego.\"person\" t4 ON ",
"LEFT JOIN agreego.\"organization\" t2 ON t2.id = t1.id",
"LEFT JOIN agreego.\"user\" t3 ON t3.id = t1.id",
"LEFT JOIN agreego.\"person\" t4 ON t4.id = t1.id",
"WHERE",
" t1.id = 'abc-archived')"
],
@ -3377,7 +3393,7 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"order_line\" t2 ON ",
"LEFT JOIN agreego.\"order_line\" t2 ON t2.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.lines.0.id}}')"
],
@ -3501,10 +3517,14 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"invoice\" t2 ON ",
"LEFT JOIN agreego.\"invoice\" t2 ON t2.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}'",
" OR (\"id\" = '{{uuid:data.id}}'))"
"UNION SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"entity\" t1",
"LEFT JOIN agreego.\"invoice\" t2 ON t2.id = t1.id",
"WHERE",
" (\"id\" = '{{uuid:data.id}}'))"
],
[
"INSERT INTO agreego.\"entity\" (",
@ -3618,7 +3638,7 @@
[
"(SELECT to_jsonb(t1.*) || to_jsonb(t2.*)",
"FROM agreego.\"entity\" t1",
"JOIN agreego.\"account\" t2 ON ",
"LEFT JOIN agreego.\"account\" t2 ON t2.id = t1.id",
"WHERE",
" t1.id = '{{uuid:data.id}}')"
],

View File

@ -67,8 +67,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "1",
"count": "0"
"count": "0",
"limit": "1"
},
"details": {
"path": "",
@ -91,8 +91,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "1",
"count": "0"
"count": "0",
"limit": "1"
},
"details": {
"path": "",
@ -218,8 +218,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "2",
"count": "1"
"count": "1",
"limit": "2"
},
"details": {
"path": "",
@ -357,8 +357,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "0",
"limit": "2"
"limit": "2",
"count": "0"
},
"details": {
"path": "",
@ -381,8 +381,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "2",
"count": "1"
"count": "1",
"limit": "2"
},
"details": {
"path": "",
@ -407,8 +407,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "2",
"count": "3"
"count": "3",
"limit": "2"
},
"details": {
"path": "",
@ -463,8 +463,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "0",
"limit": "3"
"limit": "3",
"count": "0"
},
"details": {
"path": "",
@ -487,8 +487,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "1",
"limit": "3"
"limit": "3",
"count": "1"
},
"details": {
"path": "",
@ -513,8 +513,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "3",
"limit": "1"
"limit": "1",
"count": "3"
},
"details": {
"path": "",
@ -538,8 +538,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "3",
"count": "2"
"count": "2",
"limit": "3"
},
"details": {
"path": "",
@ -549,8 +549,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"limit": "1",
"count": "2"
"count": "2",
"limit": "1"
},
"details": {
"path": "",
@ -657,8 +657,8 @@
{
"code": "CONTAINS_VIOLATED",
"values": {
"count": "2",
"limit": "1"
"limit": "1",
"count": "2"
},
"details": {
"path": "",

View File

@ -48,8 +48,8 @@
{
"code": "MIN_ITEMS_VIOLATED",
"values": {
"limit": "1",
"count": "0"
"count": "0",
"limit": "1"
},
"details": {
"path": "",
@ -108,8 +108,8 @@
{
"code": "MIN_ITEMS_VIOLATED",
"values": {
"count": "0",
"limit": "1"
"limit": "1",
"count": "0"
},
"details": {
"path": "",

View File

@ -43,8 +43,8 @@
{
"code": "MINIMUM_VIOLATED",
"values": {
"value": "0.6",
"limit": "1.1"
"limit": "1.1",
"value": "0.6"
},
"details": {
"path": "",
@ -127,8 +127,8 @@
{
"code": "MINIMUM_VIOLATED",
"values": {
"value": "-2.0001",
"limit": "-2"
"limit": "-2",
"value": "-2.0001"
},
"details": {
"path": "",

View File

@ -100,8 +100,8 @@
{
"code": "MULTIPLE_OF_VIOLATED",
"values": {
"multiple_of": "1.5",
"value": "35"
"value": "35",
"multiple_of": "1.5"
},
"details": {
"path": "",
@ -148,8 +148,8 @@
{
"code": "MULTIPLE_OF_VIOLATED",
"values": {
"value": "0.00751",
"multiple_of": "0.0001"
"multiple_of": "0.0001",
"value": "0.00751"
},
"details": {
"path": "",

View File

@ -156,8 +156,8 @@
{
"code": "MAXIMUM_VIOLATED",
"values": {
"value": "60",
"limit": "50"
"limit": "50",
"value": "60"
},
"details": {
"path": "max",
@ -191,6 +191,9 @@
},
{
"name": "request",
"field_types": {
"inv": "jsonb"
},
"schemas": {
"request": {
"type": "object",

View File

@ -34,8 +34,8 @@
{
"code": "PATTERN_VIOLATED",
"values": {
"pattern": "^a*$",
"value": "abc"
"value": "abc",
"pattern": "^a*$"
},
"details": {
"path": "",

View File

@ -148,8 +148,8 @@
{
"code": "NO_FAMILY_MATCH",
"values": {
"discriminator": "type",
"value": "alien"
"value": "alien",
"discriminator": "type"
},
"details": {
"path": "",
@ -284,8 +284,8 @@
{
"code": "NO_FAMILY_MATCH",
"values": {
"discriminator": "type",
"value": "bot"
"value": "bot",
"discriminator": "type"
},
"details": {
"path": "",

View File

@ -43,8 +43,8 @@
{
"code": "MAX_LENGTH_VIOLATED",
"values": {
"limit": "3",
"count": "6"
"count": "6",
"limit": "3"
},
"details": {
"path": "",
@ -431,8 +431,8 @@
{
"code": "MAX_LENGTH_VIOLATED",
"values": {
"count": "6",
"limit": "3"
"limit": "3",
"count": "6"
},
"details": {
"path": "",

View File

@ -1,6 +1,6 @@
[
{
"description": "Queryer Execution",
"description": "Queryer Suite 1",
"database": {
"puncs": [
{
@ -2439,5 +2439,276 @@
}
}
]
},
{
"description": "Queryer Suite 2",
"database": {
"puncs": [
{
"name": "get_pet",
"schemas": {
"get_pet.response": {
"type": "full.pet"
}
}
}
],
"enums": [],
"relations": [
{
"type": "relation",
"id": "00000000-0000-0000-0000-000000000001",
"constraint": "fk_attachment_attachable_entity",
"source_type": "attachment",
"source_columns": [
"attachable_id"
],
"destination_type": "entity",
"destination_columns": [
"id"
],
"prefix": "attachable"
}
],
"types": [
{
"name": "entity",
"hierarchy": [
"entity"
],
"grouped_fields": {
"entity": [
"id",
"type",
"archived"
]
},
"field_types": {
"id": "uuid",
"archived": "boolean",
"type": "text"
},
"schemas": {
"entity": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"archived": {
"type": "boolean"
}
}
}
},
"fields": [
"id",
"type",
"archived"
],
"variations": [
"entity",
"pet",
"attachment"
]
},
{
"name": "pet",
"hierarchy": [
"entity",
"pet"
],
"grouped_fields": {
"entity": [
"id",
"type",
"archived"
],
"pet": [
"name"
]
},
"field_types": {
"name": "text"
},
"schemas": {
"pet": {
"type": "entity",
"properties": {
"name": {
"type": "string"
}
}
},
"full.pet": {
"type": "pet",
"properties": {
"cover_attachment": {
"type": [
"attachment",
"null"
],
"properties": {
"kind": {
"const": "cover"
}
}
},
"cover_attachments": {
"type": "array",
"items": {
"type": "attachment",
"properties": {
"kind": {
"const": "cover"
}
}
}
},
"media_attachments": {
"type": "array",
"items": {
"type": "attachment",
"properties": {
"kind": {
"enum": [
"cover",
"thumbnail"
]
}
}
}
}
}
}
},
"fields": [
"id",
"type",
"archived",
"name"
],
"variations": [
"pet"
]
},
{
"name": "attachment",
"hierarchy": [
"entity",
"attachment"
],
"grouped_fields": {
"entity": [
"id",
"type",
"archived"
],
"attachment": [
"attachable_id",
"name",
"kind"
]
},
"field_types": {
"attachable_id": "uuid",
"name": "text",
"kind": "text"
},
"schemas": {
"attachment": {
"type": "entity",
"properties": {
"attachable_id": {
"type": "string"
},
"name": {
"type": "string"
},
"kind": {
"type": "string"
}
}
}
},
"fields": [
"id",
"type",
"archived",
"attachable_id",
"name",
"kind"
],
"variations": [
"attachment"
]
}
]
},
"tests": [
{
"description": "Select query on pet with static properties constraints on attachments",
"action": "query",
"schema_id": "get_pet.response",
"expect": {
"success": true,
"sql": [
[
"((SELECT jsonb_strip_nulls((",
" SELECT jsonb_build_object(",
" 'name', pet_2.name,",
" 'cover_attachment', (",
" SELECT jsonb_build_object(",
" 'attachable_id', attachment_4.attachable_id,",
" 'name', attachment_4.name,",
" 'kind', attachment_4.kind",
" )",
" FROM agreego.entity entity_3",
" JOIN agreego.attachment attachment_4 ON attachment_4.id = entity_3.id",
" WHERE",
" NOT entity_3.archived",
" AND attachment_4.kind = 'cover'",
" AND attachment_4.attachable_id = entity_1.id",
" ),",
" 'cover_attachments', (",
" SELECT COALESCE(jsonb_agg(jsonb_build_object(",
" 'attachable_id', attachment_6.attachable_id,",
" 'name', attachment_6.name,",
" 'kind', attachment_6.kind",
" )), '[]'::jsonb)",
" FROM agreego.entity entity_5",
" JOIN agreego.attachment attachment_6 ON attachment_6.id = entity_5.id",
" WHERE",
" NOT entity_5.archived",
" AND attachment_6.kind = 'cover'",
" AND attachment_6.attachable_id = entity_1.id",
" ),",
" 'media_attachments', (",
" SELECT COALESCE(jsonb_agg(jsonb_build_object(",
" 'attachable_id', attachment_8.attachable_id,",
" 'name', attachment_8.name,",
" 'kind', attachment_8.kind",
" )), '[]'::jsonb)",
" FROM agreego.entity entity_7",
" JOIN agreego.attachment attachment_8 ON attachment_8.id = entity_7.id",
" WHERE",
" NOT entity_7.archived",
" AND attachment_8.kind IN ('cover', 'thumbnail')",
" AND attachment_8.attachable_id = entity_1.id",
" )",
" )",
" FROM agreego.entity entity_1",
" JOIN agreego.pet pet_2 ON pet_2.id = entity_1.id",
" WHERE",
" NOT entity_1.archived",
"))))"
]
]
}
}
]
}
]

View File

@ -139,8 +139,8 @@
{
"code": "MAX_LENGTH_VIOLATED",
"values": {
"count": "26",
"limit": "5"
"limit": "5",
"count": "26"
},
"details": {
"path": "email",

1
flow
View File

@ -36,7 +36,6 @@ pgrx-down() {
info "Taking pgrx down..."
}
build() {
local version
version=$(get-version) || return $?

2
flows

Submodule flows updated: 0d9bd8644e...89748a246e

View File

@ -29,10 +29,21 @@ impl Schema {
if db.types.contains_key(&base_type_name) {
parent_type_name = Some(base_type_name);
}
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
// 3. Nested graphs trust their explicit struct pointer reference
if !crate::database::object::is_primitive_type(t) {
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
} else if let Some(t_or_arr) = &self.obj.type_ {
match t_or_arr {
crate::database::object::SchemaTypeOrArray::Single(t) => {
if !crate::database::object::is_primitive_type(t) {
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
}
}
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
for t in types {
if !crate::database::object::is_primitive_type(t) {
parent_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
break;
}
}
}
}
}
@ -46,13 +57,23 @@ impl Schema {
let mut is_array = false;
// Structurally unpack the inner target entity if the object maps to an array list
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
&prop_schema.obj.type_
{
if t == "array" {
is_array = true;
if let Some(items) = &prop_schema.obj.items {
target_schema = items.clone();
if let Some(t_or_arr) = &prop_schema.obj.type_ {
match t_or_arr {
crate::database::object::SchemaTypeOrArray::Single(t) => {
if t == "array" {
is_array = true;
if let Some(items) = &prop_schema.obj.items {
target_schema = items.clone();
}
}
}
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
if types.contains(&"array".to_string()) {
is_array = true;
if let Some(items) = &prop_schema.obj.items {
target_schema = items.clone();
}
}
}
}
}
@ -60,11 +81,21 @@ impl Schema {
// Determine the physical Postgres table backing the nested child schema recursively
if let Some(family) = &target_schema.obj.family {
child_type_name = Some(family.split('.').next_back().unwrap_or(family).to_string());
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
&target_schema.obj.type_
{
if !crate::database::object::is_primitive_type(t) {
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
} else if let Some(t_or_arr) = &target_schema.obj.type_ {
match t_or_arr {
crate::database::object::SchemaTypeOrArray::Single(t) => {
if !crate::database::object::is_primitive_type(t) {
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
}
}
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
for t in types {
if !crate::database::object::is_primitive_type(t) {
child_type_name = Some(t.split('.').next_back().unwrap_or(t).to_string());
break;
}
}
}
}
} else if let Some(arr) = &target_schema.obj.one_of {
if let Some(first) = arr.first() {

View File

@ -165,20 +165,8 @@ impl Schema {
} else if db.enums.contains_key(custom) {
Some(vec![format!("{}.condition", custom)])
} else {
// A named type gets a reference to its dynamically built .filter — either a
// Relational cross-boundary (table-backed) or a named value type, whose filter
// is likewise synthesized (see Database::compile_filters). A named type with no
// compilable structure gets no filter — omit it rather than dangle a reference.
let base = custom.split('.').next_back().unwrap_or(custom);
let has_value_filter = db
.schemas
.get(custom)
.map_or(false, |s| Database::is_value_filter_candidate(custom, s));
if db.types.contains_key(base) || has_value_filter {
Some(vec![format!("{}.filter", custom)])
} else {
None
}
// Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built
Some(vec![format!("{}.filter", custom)])
}
}
}

View File

@ -40,6 +40,7 @@ impl Schema {
}
let mut props = IndexMap::new();
let mut inherited_edges = IndexMap::new();
// 1. Resolve INHERITANCE dependencies first
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &self.obj.type_ {
@ -49,6 +50,9 @@ impl Schema {
if let Some(p_props) = parent.obj.compiled_properties.get() {
props.extend(p_props.clone());
}
if let Some(p_edges) = parent.obj.compiled_edges.get() {
inherited_edges.extend(p_edges.clone());
}
}
}
}
@ -77,6 +81,12 @@ impl Schema {
if !crate::database::object::is_primitive_type(t) && !t.starts_with('$') {
if let Some(parent) = db.schemas.get(t).cloned() {
parent.as_ref().compile(db, t, t.clone(), errors);
if let Some(p_props) = parent.obj.compiled_properties.get() {
props.extend(p_props.clone());
}
if let Some(p_edges) = parent.obj.compiled_edges.get() {
inherited_edges.extend(p_edges.clone());
}
}
}
}
@ -116,7 +126,10 @@ impl Schema {
let _ = self.obj.compiled_property_names.set(names);
// 5. Compute Edges natively
let schema_edges = self.compile_edges(db, root_id, &path, &props, errors);
let mut schema_edges = self.compile_edges(db, root_id, &path, &props, errors);
for (k, v) in inherited_edges {
schema_edges.entry(k).or_insert(v);
}
let _ = self.obj.compiled_edges.set(schema_edges);
// 5. Build our inline children properties recursively NOW! (Depth-first search)

View File

@ -262,23 +262,15 @@ impl Database {
}
}
/// Synthesizes Composed Filter References for all table-backed boundaries — and for
/// named non-table value types (schema-only objects, e.g. an operating-hours config),
/// so a property reference resolves to ONE named filter instead of inlining anonymous
/// per-path copies (which duplicate identical leaf type names for downstream codegen).
/// Synthesizes Composed Filter References for all table-backed boundaries.
fn compile_filters(&mut self, errors: &mut Vec<crate::drop::Error>) -> Vec<(String, String)> {
let mut filter_schemas = Vec::new();
let mut seen_value_ids = std::collections::HashSet::new();
for (type_name, type_def) in &self.types {
for (id, schema_arc) in &type_def.schemas {
// Run synthesis on structured table-backed boundaries and named value types.
// Exclude subschemas!
// Only run synthesis on actual structured, table-backed boundaries. Exclude subschemas!
let base_name = id.split('.').last().unwrap_or(id);
let is_table_backed = base_name == type_def.name;
let is_value_type = !is_table_backed
&& Self::is_value_filter_candidate(id, schema_arc)
&& seen_value_ids.insert(id.clone());
if (is_table_backed || is_value_type) && !id.contains('/') {
if is_table_backed && !id.contains('/') {
if let Some(filter_schema) = schema_arc.compile_filter(self, id, errors) {
filter_schemas.push((
type_name.clone(),
@ -301,20 +293,6 @@ impl Database {
filter_ids
}
/// A named non-table value type that earns its own synthesized filter: a bare-named
/// (dotless) schema-only object with compiled properties. The base `filter`/`condition`
/// schemas are infrastructure, not value types.
pub fn is_value_filter_candidate(id: &str, schema: &Arc<Schema>) -> bool {
!id.contains('.')
&& id != "filter"
&& id != "condition"
&& schema
.obj
.compiled_properties
.get()
.map_or(false, |props| !props.is_empty())
}
/// Synthesizes strong Enum Conditions mirroring the string.condition capabilities.
fn compile_conditions(&mut self) -> Vec<(String, String)> {
let mut enum_conditions = Vec::new();

View File

@ -1,6 +1,7 @@
use crate::database::action::Action;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(default)]
@ -13,6 +14,11 @@ pub struct Page {
pub sidebar: Option<Sidebar>,
#[serde(skip_serializing_if = "Option::is_none")]
pub actions: Option<IndexMap<String, Action>>,
/// The personal-scope filter template ("one surface, two scopes"): the
/// "mine" predicate the page merges into its filter when the app is in
/// personal scope, e.g. {"reserved_by": "$me"}. Opaque to the engine.
#[serde(skip_serializing_if = "Option::is_none")]
pub personal: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]

View File

@ -744,9 +744,15 @@ impl Merger {
return Ok(None);
}
let where_clause = format!("WHERE {}", where_parts.join(" OR "));
let final_sql = format!("{} {}", fetch_sql_template, where_clause);
let final_sql = if where_parts.len() == 1 {
format!("{} WHERE {}", fetch_sql_template, where_parts[0])
} else {
where_parts
.iter()
.map(|p| format!("{} WHERE {}", fetch_sql_template, p))
.collect::<Vec<_>>()
.join(" UNION ")
};
let fetched = match self.db.query(&final_sql, None) {
Ok(Value::Array(table)) => {

View File

@ -1,5 +1,6 @@
use crate::database::Database;
use indexmap::IndexMap;
use serde_json::Value;
use std::sync::Arc;
pub struct Compiler<'a> {
@ -86,13 +87,29 @@ impl<'a> Compiler<'a> {
.next_back()
.unwrap_or(family_target);
resolved_type = self.db.types.get(base_type_name);
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &items.obj.type_
{
if !crate::database::object::is_primitive_type(t) {
resolved_type = self
.db
.types
.get(&t.split('.').next_back().unwrap_or(t).to_string());
} else if let Some(t_or_arr) = &items.obj.type_ {
match t_or_arr {
crate::database::object::SchemaTypeOrArray::Single(t) => {
if !crate::database::object::is_primitive_type(t) {
resolved_type = self
.db
.types
.get(&t.split('.').next_back().unwrap_or(t).to_string());
}
}
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
for t in types {
if !crate::database::object::is_primitive_type(t) {
resolved_type = self
.db
.types
.get(&t.split('.').next_back().unwrap_or(t).to_string());
if resolved_type.is_some() {
break;
}
}
}
}
}
}
}
@ -135,12 +152,25 @@ impl<'a> Compiler<'a> {
.next_back()
.unwrap_or(family_target);
resolved_type = self.db.types.get(base_type_name);
} else if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) =
&node.schema.obj.type_
{
if !crate::database::object::is_primitive_type(t) {
let base_type_name = t.split('.').next_back().unwrap_or(t).to_string();
resolved_type = self.db.types.get(&base_type_name);
} else if let Some(t_or_arr) = &node.schema.obj.type_ {
match t_or_arr {
crate::database::object::SchemaTypeOrArray::Single(t) => {
if !crate::database::object::is_primitive_type(t) {
let base_type_name = t.split('.').next_back().unwrap_or(t).to_string();
resolved_type = self.db.types.get(&base_type_name);
}
}
crate::database::object::SchemaTypeOrArray::Multiple(types) => {
for t in types {
if !crate::database::object::is_primitive_type(t) {
let base_type_name = t.split('.').next_back().unwrap_or(t).to_string();
resolved_type = self.db.types.get(&base_type_name);
if resolved_type.is_some() {
break;
}
}
}
}
}
}
}
@ -439,6 +469,9 @@ impl<'a> Compiler<'a> {
Some(crate::database::object::SchemaTypeOrArray::Single(s)) => {
!crate::database::object::is_primitive_type(s)
}
Some(crate::database::object::SchemaTypeOrArray::Multiple(v)) => {
v.iter().any(|s| !crate::database::object::is_primitive_type(s))
}
_ => false,
};
@ -551,6 +584,7 @@ impl<'a> Compiler<'a> {
self.compile_filter_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses);
self.compile_polymorphic_bounds(r#type, type_aliases, &node, &mut where_clauses);
self.compile_static_property_conditions(r#type, type_aliases, &node, &base_alias, &mut where_clauses);
let start_len = where_clauses.len();
self.compile_relation_conditions(
@ -865,4 +899,69 @@ impl<'a> Compiler<'a> {
}
Ok(())
}
fn compile_static_property_conditions(
&self,
r#type: &crate::database::r#type::Type,
type_aliases: &std::collections::HashMap<String, String>,
node: &Node,
base_alias: &str,
where_clauses: &mut Vec<String>,
) {
if let Some(props) = node.schema.obj.properties.as_ref() {
for (prop_name, prop_schema) in props {
let filter_alias = Self::resolve_filter_alias(r#type, type_aliases, base_alias, prop_name);
if let Some(const_val) = prop_schema.obj.const_.as_ref() {
let sql_val = Self::quote_literal(const_val);
where_clauses.push(format!("{}.{} = {}", filter_alias, prop_name, sql_val));
}
if let Some(enum_vals) = prop_schema.obj.enum_.as_ref() {
if !enum_vals.is_empty() {
let sql_vals: Vec<String> = enum_vals
.iter()
.map(|v| Self::quote_literal(v))
.collect();
where_clauses.push(format!(
"{}.{} IN ({})",
filter_alias,
prop_name,
sql_vals.join(", ")
));
}
}
}
}
}
fn quote_literal(val: &Value) -> String {
match val {
Value::Null => "NULL".to_string(),
Value::Bool(b) => {
if *b {
"true".to_string()
} else {
"false".to_string()
}
}
Value::Number(n) => {
if let Some(f) = n.as_f64() {
if f.fract() == 0.0 {
return f.trunc().to_string();
}
}
n.to_string()
}
Value::String(s) => {
if s.is_empty() {
"NULL".to_string()
} else {
format!("'{}'", s.replace('\'', "''"))
}
}
_ => format!(
"'{}'",
serde_json::to_string(val).unwrap().replace('\'', "''")
),
}
}
}

View File

@ -1301,6 +1301,12 @@ fn test_queryer_0_15() {
crate::tests::runner::run_test_case(&path, 0, 15).unwrap();
}
#[test]
fn test_queryer_1_0() {
let path = format!("{}/fixtures/queryer.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_case(&path, 1, 0).unwrap();
}
#[test]
fn test_polymorphism_0_0() {
let path = format!("{}/fixtures/polymorphism.json", env!("CARGO_MANIFEST_DIR"));

View File

@ -179,14 +179,35 @@ impl SqlFormatter {
}
fn format_query(&mut self, query: &Query) {
match &*query.body {
self.format_set_expr(&query.body);
}
fn format_set_expr(&mut self, set_expr: &SetExpr) {
match set_expr {
SetExpr::Select(select) => self.format_select(select),
SetExpr::Query(inner_query) => {
self.push_str("(");
self.format_query(inner_query);
self.push_str(")");
}
_ => self.push_str(&query.to_string()),
SetExpr::SetOperation { op, left, right, set_quantifier } => {
self.format_set_expr(left);
let op_str = match op {
sqlparser::ast::SetOperator::Union => "UNION",
sqlparser::ast::SetOperator::Intersect => "INTERSECT",
sqlparser::ast::SetOperator::Except => "EXCEPT",
_ => "UNION",
};
let quant_str = match set_quantifier {
sqlparser::ast::SetQuantifier::All => " ALL",
sqlparser::ast::SetQuantifier::Distinct => " DISTINCT",
sqlparser::ast::SetQuantifier::None => "",
_ => "",
};
self.push_line(&format!("{}{} ", op_str, quant_str));
self.format_set_expr(right);
}
_ => self.push_str(&set_expr.to_string()),
}
}
@ -236,20 +257,23 @@ impl SqlFormatter {
}
fn format_join(&mut self, join: &Join) {
let op = match &join.join_operator {
let op_str = match &join.join_operator {
JoinOperator::Inner(_) => "JOIN",
JoinOperator::LeftOuter(_) => "LEFT JOIN",
JoinOperator::Left(_) => "LEFT JOIN",
JoinOperator::Right(_) => "RIGHT JOIN",
_ => "JOIN",
};
self.push_str(&format!("{} {} ON ", op, join.relation));
self.push_str(&format!("{} {}", op_str, join.relation));
match &join.join_operator {
JoinOperator::Inner(JoinConstraint::On(expr)) => self.format_expr(expr),
JoinOperator::LeftOuter(JoinConstraint::On(expr)) => self.format_expr(expr),
JoinOperator::Join(JoinConstraint::On(expr)) => self.format_expr(expr),
_ => {
println!("FALLBACK JOIN OP: {:?}", join.join_operator);
JoinOperator::Inner(JoinConstraint::On(expr))
| JoinOperator::Left(JoinConstraint::On(expr))
| JoinOperator::Right(JoinConstraint::On(expr))
| JoinOperator::Join(JoinConstraint::On(expr)) => {
self.push_str(" ON ");
self.format_expr(expr);
}
_ => {}
}
}

View File

@ -157,7 +157,10 @@ fn test_library_api() {
}
},
"name": { "type": ["string.condition", "null"] },
"target": { "type": ["target_schema.filter", "null"] },
"target": {
"type": ["target_schema.filter", "null"],
"compiledPropertyNames": ["value", "$and", "$or"]
},
"type": { "type": ["string.condition", "null"] }
},
"type": "filter"

View File

@ -259,3 +259,25 @@ pub fn update_sql_fixture(path: &str, suite_idx: usize, case_idx: usize, queries
let formatted_json = serde_json::to_string_pretty(&file_data).unwrap();
fs::write(path, formatted_json).unwrap();
}
pub fn update_schemas_fixture(path: &str, suite_idx: usize, case_idx: usize, db: &crate::database::Database) {
let content = fs::read_to_string(path).unwrap();
let mut file_data: Value = serde_json::from_str(&content).unwrap();
if let Some(expect) = file_data[suite_idx]["tests"][case_idx].get_mut("expect") {
if let Some(schemas_map) = expect.get_mut("schemas").and_then(|v| v.as_object_mut()) {
for (key, expected_val) in schemas_map {
if expected_val.is_object() && expected_val.as_object().unwrap().is_empty() {
continue;
}
if let Some(actual_ast) = db.schemas.get(key) {
let actual_val = serde_json::to_value(actual_ast).unwrap();
*expected_val = actual_val;
}
}
}
}
let formatted_json = serde_json::to_string_pretty(&file_data).unwrap();
fs::write(path, formatted_json).unwrap();
}

View File

@ -57,6 +57,9 @@ impl Case {
if env::var("UPDATE_EXPECT").is_ok() {
update_validation_fixture(path, suite_idx, case_idx, &result.errors);
if let Ok(db) = db_res {
crate::tests::runner::update_schemas_fixture(path, suite_idx, case_idx, db);
}
}
expect.assert_drop(&result)?;

View File

@ -1 +1 @@
1.0.167
1.0.178