Constraints are used to maintain data integrity within and across relvars, ensuring that only valid data is stored. Constraints are defined at the schema level within a special relvar, known as the $constraint relvar, and each constraint consists of a name and a rule.
The rule is a boolean expression that is evaluated each time an assignment is made to a relvar - before the changes are committed (i.e. before they become visible to other users or operations). If the rule evaluates to true, the constraint is satisfied, and the assignment proceeds. If the rule evaluates to false (or if the evaluation fails), the entire assignment is rolled back and no changes are made.
Error Handling
Any failed constraints will be logged in the error relvar. After each assignment, users should check the error relvar to ensure no violations occurred. If errors are present, the user will need to handle them - either by retrying the assignment or applying some other form of remediation.
Types of Constraint
Here are some common types of constraints, with examples:
Tuple-level constraints
A tuple-level constraint ensures that every tuple in a given relvar satisfies the constraint. For example:
empty($S[STATUS<1 or STATUS>100])
This rule ensures that every tuple in $S has a STATUS between 1 and 100 inclusive. If any tuple violates this, the constraint fails, and the assignment is rolled back.
Key constraints
A key constraint ensures that every tuple in a given relvar is unique, with respect to a specified set of attributes. For example:
key($S, S{SNO})
which expands to:
not assigned($S) or count($S) = count($S{SNO})
This rule ensures that, if $S exists, the number of tuples in $S remains equal to the number of unique SNO values in $S. If any assignment violates this uniqueness, the constraint fails, and the assignment is rolled back.
Foreign key constraints
A foreign key constraint ensures that every tuple in a given relvar (the relvar "left" of the ⊆ (subset) operator) has a matching tuple in another relvar (the one "right" of the ⊆ operator, i.e. the "referenced" relvar). The matching must occur with the same attribute(s) and data type(s). For example:
$SP{SNO} ⊆ $S{SNO}
This rule ensures that the set of SNO values from the $SP relvar is a subset of the SNO values from the $S relvar.
Note that a foreign key constraint restricts assignments to the relvars on both sides of the ⊆ operator: each tuple added to the left must have a matching entry in the right; and each tuple removed from the right must have no matching entries in the left.