Constraints
Constraints are just queries ending in one of the constraint statements like :unique
,
:fk
and :check
. These operators throw exceptions when the constraints are not met.
You can use constraints to get confidence that databases are always in valid states, and you didn't accidentally screw something up (perhaps at dev time).
To constrain a database such that you get errors when putting the db into invalid states, you materialize
constraint queries (and they can be removed with demat
).
Constraints can apply to any query, so you can apply constraints to aggregates and joins, here
is the obligatory order can have at most 10 items if its associated customer is called bob and its tuesday
constraint.
[[:from Order]
[:join Customer {:customer-id :customer-id}]
[:where [= "bob" :firstname] [tuesday? [rel/env :now]]]
[:check {:pred [<= [count :items] 10],
:error [str "order can have at most 10 items if its associated customer is called bob and its tuesday, found: " [count :items]]}]]
As it is convenient to specify multiple constraints on a query in one form, a special
:constrain
operator is provided.
e.g
[[:from Customer]
[:constrain
[:check [string? :firstname] [string? :lastname] [nat-int? :age]]
[:unique :customer-id]
[:fk Address {:address-id :address-id}]]]
Constraint operators
:check
ensure certain predicates hold:req
ensure cols exist:fk
ensure a referenced row exists in some other query/table:constrain
combine multiple constraints on a query/table:unique
unsure only one row exists for some set of expressions