111 Path Expression Updates
Location Denotations
Some path expressions have a location denotation as well as a value denotation. In an expression context, the path denotes the value obtained by reading that location. In an update target context, the path denotes the selected locations.
read(location(E)) = value(E)
This equation is the consistency requirement for update targets. The ordinary value denotation describes what a query observes. The location denotation keeps the address witnesses needed for a write, such as the selected receiver and attribute slot.
An update target is valid only when it denotes locations. A single path expression may select zero, one, or many locations, so a single update statement may perform zero, one, or many writes. Assignment replaces the value at each selected location, and compound update operators read the current value at each location and write the updated value back to the same location.
Not every readable path denotes locations. Program variables and base
attributes are updateable. Derived attributes, including attributes defined with
inverse, are read-only unless an explicit view-update rule gives them a
location denotation. Ordinary path-expression updates therefore write base
locations, not derived views.
The target location denotation and the right-hand value denotation are evaluated in the statement pre-state. For a local program-state update, this is the current program state. For a database-state update, this is the current transaction state before the statement. The resulting writes are then applied to produce the next state of the same kind.
s.supplier.[city = "London"].status := 5;
This statement assigns status 5 to every supplier in London.
Attributes With Cardinality One
s.shipment.[supplier.name = "Smith"].qty := 100;
s.shipment.[supplier.name = "Smith"].qty++;
s.shipment.[supplier.name = "Smith"].qty += 10;
The same update operators can be used when the target is selected by a path
expression. These updates start at the Supplier/Parts system s, range over
its shipments, filter to shipments whose supplier is named "Smith", and then
select their required qty attribute. A cardinality-one attribute location is
the selected receiver together with the required attribute slot; reading it
yields exactly one value, and assignment replaces that value.
Attributes With Cardinality Zero Or One
s.supplier.[name = "Smith"].status := 25;
s.supplier.[name = "Smith"].status := none;
For an attribute with cardinality zero or one, the selected target is an
optional location. The location exists for each selected receiver even when the
current extension is empty. Assigning a value of the attribute's element type
sets the selected extension to that single value. Assigning none clears the
selected extension.
The expression none denotes an empty result for an optional location. It is not a value and is
not inserted as a stored marker. For an optional attribute, clearing the attribute removes the
corresponding predicate fact from the selected receiver. Setting the attribute inserts or replaces
that fact so that the cardinality remains zero or one.
Attributes With Cardinality Zero Or More
s.supplier.[name = "Smith"].city ∪= {"London"};
s.supplier.[name = "Smith"].city ∩= {"London", "Athens"};
s.supplier.[name = "Smith"].city ∖= {"Paris"};
s.supplier.[name = "Smith"].city ∩= {};
s.supplier.[name = "Smith"].city := {"London", "Athens"};
For an attribute with cardinality zero or more, the selected target is a
set-valued attribute slot whose current value is an extension of values. If
city is such an attribute, ∪= adds city values,
∩= keeps only city values in the right-hand set, ∖= removes city values,
and := replaces the selected city extension with the given set of values.
The update applies to each supplier selected by the path.
For any attribute whose cardinality permits zero, intersecting with the empty set clears the selected extension.
These examples are intentionally written with the same navigation and filtering notation used for queries. An update target is not a separate addressing language: it is a path expression with a location denotation.
Zero-Location Updates
s.supplier.[name = "No such supplier"].status := 25;
shape.circle.radius += 1;
value.as<Circle>.radius += 1;
If a path expression selects no locations, the update succeeds and performs no writes. This is the ordinary empty result case for location denotations, not an exception. Variant attribute navigation and union casts can therefore be used as conditional update targets: if the current variant attribute or union member does not match, the selected location set is empty.
Partition Validity
// Invalid when the open transaction owns only one supplier partition.
network.supplier.status := 5;
For a database-state update, every selected database location must belong to the single partition owned by the open transaction. An implementation may reject an update statically when the target is known to cross partition boundaries, or dynamically when evaluating the target discovers an out-of-partition location. It must not silently turn a cross-partition update into a filtered partition-local update.
Local program-state locations do not belong to database partitions and are not subject to this partition rule. A single update statement must not mix local program-state locations and database-state locations in the same target.