Brief: ограничения
Ограничения (CONSTRAINT)
Ограничение — свойство, значение которого всегда должно быть NULL. Проверяется оно на событии, заданном блоком описания события инструкции, а по умолчанию — на глобальном событии APPLY; если к этому моменту оно стало не NULL хотя бы на одном наборе объектов, платформа показывает сообщение с этими наборами и отменяет изменения. Создается инструкцией CONSTRAINT:
CONSTRAINT [eventClause] constraintExpr [CHECKED [BY propertyId1, ..., propertyIdN]] MESSAGE messageExpr
[PROPERTIES outExpr1, ..., outExprM];
Опция CHECKED BY заставляет диалог изменения перечисленных свойств отфильтровывать значения, нарушающие ограничение.
Простые ограничения задаются видом связи между свойствами, а не выражением, Реализованы два вида: следствие — инструкция => — и определенность — опция NONULL. Следствие умеет разрешать нарушение само: его клауза RESOLVE [LEFT] [RIGHT] указывает платформе, какую сторону исправлять. У NONULL такой клаузы нет — у него есть [DELETE], удаляющий нарушившие объекты.
Аналогия: CHECK и NOT NULL в SQL, но условием служит произвольное свойство над всей базой.
CONSTRAINT balance(Sku s, Stock st) < 0 MESSAGE 'Остаток не может быть отрицательным';
Агрегируемые объекты и ограничения
Агрегация создает агрегируемый объект, когда агрегируемое свойство становится не NULL на наборе значений параметров, и удаляет этот объект, когда свойство снова становится NULL. Создает и удаляет объект только оператор AGGR; GROUP AGGR строит обратное свойство — от значений параметров к объекту. Оба описаны в Brief: свойства.
Для целостности это еще один инвариант, который платформа поддерживает сама: на набор значений параметров приходится не более одного агрегируемого объекта. GROUP AGGR добавляет его как ограничение на свою группу, а AGGR держит в его рамках соответствие между объектами и значениями параметров. Как это ограничение поддерживается, описано в агрегациях и простых ограничениях.
// поставка по инвойсу создается, когда у инвойса ставится признак создания поставки, и удаляется, когда он снимается
shipment (Invoice i) = AGGR ShipmentInvoice WHERE createShipment(i);