94 Supplier/Parts Translation

A historical table comparing many Supplier/Parts queries in Tutorial D and Predica is retained under this part's redundant notes. This chapter does not repeat that catalogue. Instead it takes a few representative cases and reads them through the relation view developed in this part.

The recurring pattern is:

Predica path expression  --logic frame-->  corresponding s-relation

The familiar RM operations appear on the corresponding relation. The logic frame remains the primary denotation, as in the correspondence chapter.

Base Relvars as Results

The RM relvars S, P, and SP can be recovered by choosing RM-style result headings for Predica frames. For suppliers:

s.supplier.(id as s_id, name as sname, status, city)

corresponds to:

s_id=SI:Integer, sname=SN:String, status=ST:Integer, city=C:String
where ∃S:Supplier (
    s.supplier(S)
    ∧ S.id(SI)
    ∧ S.name(SN)
    ∧ S.status(ST)
    ∧ S.city(C)
)

For parts:

s.part.(id as p_id, name as pname, color, weight, city)

and for shipments:

s.shipment.(
    supplier.id as s_id,
    part.id as p_id,
    qty)

The shipment frame keeps the shipment value as a witness, while exposing the supplier id, part id, and quantity:

s_id=SI:Integer, p_id=PI:Integer, qty=Q:Integer
where ∃X:Shipment ∃S:Supplier ∃P:Part (
    s.shipment(X)
    ∧ X.supplier(S)
    ∧ X.part(P)
    ∧ S.id(SI)
    ∧ P.id(PI)
    ∧ X.qty(Q)
)

Under these result headings, the s-relation extensions have the same row shape as the RM relations S, P, and SP, modulo label spelling.

Projection and Restriction

The RM query:

(S WHERE CITY = 'London'){SNAME}

is recovered by:

s.supplier.[city = "London"].name

The corresponding relation has heading {name: String}:

name=N:String
where ∃S:Supplier ∃C:String (
    s.supplier(S)
    ∧ S.city(C)
    ∧ C = "London"
    ∧ S.name(N)
)

Restriction strengthens the frame condition. Projection exposes fewer result labels. The hidden supplier and city remain witnesses for the resulting supplier name.

Join Through Witnesses

The RM query:

(SP JOIN S JOIN P){SNAME, PNAME, QTY}

is recovered by:

s.shipment.(
    supplier.name as sname,
    part.name as pname,
    qty)

The frame has one shipment witness and navigates to its supplier and part witnesses:

sname=SN:String, pname=PN:String, qty=Q:Integer
where ∃X:Shipment ∃S:Supplier ∃P:Part (
    s.shipment(X)
    ∧ X.supplier(S)
    ∧ X.part(P)
    ∧ S.name(SN)
    ∧ P.name(PN)
    ∧ X.qty(Q)
)

In the RM view, this appears as a join because SP, S, and P are separate unnested relations. In the Predica frame, the shared witnesses are already present in the navigation path. The corresponding s-relation has the joined row shape.

Difference and Anti-Join

The RM query for suppliers who ship nothing can be written:

S MINUS (SP {S#} JOIN S)

The corresponding Predica expression is:

s.supplier -- s.shipment.supplier

The first operand returns all supplier values. The second returns supplier values that are witnessed by some shipment. Difference is then ordinary set difference on the common heading:

{ supplier: Supplier }

If the result is later exposed as supplier rows, the surviving supplier witnesses can be expanded with:

(...).(id as s_id, name as sname, status, city)

The important point is that anti-join is not a primary Predica denotation here. It is the relation view of subtracting one extension from another under the same heading.

Universal Navigation

The query "suppliers who supply all red parts" is a standard RM division-style query:

S WHERE NOT EXISTS (
    (P WHERE COLOR = 'Red')
    WHERE NOT EXISTS (
        SP WHERE SP.S# = S.S#
           AND SP.P# = P.P#
    )
)

In Predica:

s.part.[
    color = Color.red
]⇒(shipment.supplier)

The ordinary navigation . is existential path composition. The universal navigation ⇒ is universal path composition: the left-hand witnesses are protected while the right-hand path is translated, then the left-hand witnesses are universally closed. In this query, the selected red parts are the protected witnesses, and the result is the suppliers for which the shipment.supplier path succeeds for every selected red part.

In the relation view, the corresponding extension is an s-relation under a unary heading of supplier values. If an RM-shaped result is required, that extension can then be expanded through supplier attributes.

Quantified Comparisons

The RM query:

S WHERE STATUS >= ALL S{STATUS}

is recovered by:

s.supplier.[
    status ≥ s.supplier⇒status
]

The right-hand side uses universal path composition to express the ALL side of the comparison. In frame terms, the supplier witnesses from s.supplier are universally closed in the comparison condition. The frame keeps the candidate supplier on the left as a witness and adds the condition that its status is at least every resulting supplier status.

The ANY-style query:

P WHERE WEIGHT > ANY (
    (SP WHERE S# = 'S1' JOIN P){WEIGHT}
)

is recovered by:

s.part.[
    weight > s.shipment.[
        supplier.id = 1
    ].part.weight
]

Here ordinary navigation on the right gives the existential, ANY-style side of the comparison: weights reachable from S1's shipments. The filter keeps each candidate part whose weight satisfies the comparison against that resulting set of weights.

Where M-Relations Help

Most Supplier/Parts examples use single-valued attributes, so the corresponding s-relation rows are close to the familiar RM rows. M-relations become more important when attributes have mixed cardinalities, or when intermediate results would otherwise multiply out large Cartesian products.

For example, if suppliers can have many cities and optional statuses, then an implementation may keep an intermediate supplier result as rectangles:

{
    < id: {1}, city: {"London"}, status: {20} >,
    < id: {5}, city: {"Athens"}, status: {30} >
}

Such a representation is trusted only through heading-relative expansion. It is useful when it avoids unnecessary multiplication, but every externally visible result must agree with the s-relation extension for the chosen heading.