A SINGLE reified-relationship property (e.g. invoice.counterparty,
person.primary_contact — a property whose type is a relationship
subtype) is hydrated by a correlated subquery that joins the
relationship table and correlates source_id/target_id = parent.id; its
discriminators (the relationship subtype and the target's type CASE)
are constrained INSIDE that subquery.
For such a property the resolved edge is a REVERSE traversal
(forward = false) over a generic relationship FK
(fk_relationship_source_entity, destination = entity). compile_
polymorphic_bounds then took the `!edge.forward` branch with
poly_col = destination_columns[1] = "type" and
table_to_alias = "entity", which resolves to the PARENT's entity alias
— emitting a bound like `entity_1.type = 'counterparty'` into the
parent WHERE. No parent row has that type, so EVERY parent was dropped
(e.g. get_invoice returned null for an existing invoice whose
counterparty edge was absent).
Array reified traversals (contacts, occupancies) never hit this: an
array property has no bound_type_name. Only the single form did, and it
was previously unexercised.
Fix: in compile_polymorphic_bounds, skip the bound when the property's
resolved type is itself a relationship (type_def.relationship) — there
is no parent-row discriminator column to bound.
Test: new queryer case (person.primary_contact, type=contact) asserts
the parent WHERE has no spurious entity.type bound while the subquery
keeps its discriminator + source_id correlation. Full suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An OPTIONAL forward polymorphic relation declared via `family` (e.g.
order.counterparty / invoice.counterparty) had its auto-generated type
bound `{alias}.{disc} IN (variations)` emitted into the PARENT row's
WHERE. When the relation is absent (NULL discriminator) the parent row
was wrongly excluded — a counterparty-less order/invoice returned
nothing instead of the row with `counterparty: null`.
Fix: in `compile_polymorphic_bounds`, make the forward-FK bound
NULL-tolerant (`(… IN (…) OR …_type IS NULL)`), gated on
`!r#type.relationship`:
- real entities get NULL-tolerance (the relation is an optional
attribute; an absent one must not drop the row — the inner CASE
already resolves it to NULL);
- edge entities (`relationship == true`, e.g. `contact`) keep the
bound EXACT, because there source_type/target_type *partition*
typed sub-collections (phone_numbers vs email_addresses) and a NULL
endpoint belongs to no partition.
Note: the `oneOf` path was already correct — it emits no parent bound
(resolves via CASE … ELSE NULL), so cross-family optional relations
already hydrate NULL-safely. Added a fixture case documenting that.
Tests (fixtures/queryer.json): case 15 (entity → NULL-tolerant),
cases 3/5/10 (contact edges → exact, unchanged), new case 16
(oneOf cross-family → no bound). Full suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch the two UUID generation sites in the merger from v4 to time-ordered
v7, and swap the uuid crate feature flag from v4 to v7 accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>