packages/sql-semantics/src/Core/Schema/TableConstraint.php

1<?php
2
3declare(strict_types=1);
4
5namespace SqlSemantics\Core\Schema;
6
7use InvalidArgumentException;
8use SqlSemantics\Statement\Element;
9
10/**
11 * A declared integrity condition, retaining its complete syntax for downstream consumers.
12 *
13 * @example Accept this semantic value in a database-independent consumer
14 *     $consume = static fn (\SqlSemantics\Core\Schema\TableConstraint $value): string => $value::class;
15 *     $consume instanceof \Closure // => true
16 *
17 * @visibility public
18 */
19final class TableConstraint
20{
21    /**
22     * @param ConstraintKind $kind Integrity condition
23     * @param list<string> $columns Local columns, in declaration order
24     * @param Element $source Typed constraint declaration, including actions and deferrability
25     * @param string|null $name Explicit constraint name
26     * @param list<string> $referencedTable Qualified foreign table name
27     * @param list<string> $referencedColumns Referenced key columns; empty means the primary key
28     * @param Element|null $expression Typed CHECK expression
29     * @throws InvalidArgumentException When supplied state violates its invariants
30     */
31    public function __construct(
32        public readonly ConstraintKind $kind,
33        public readonly array $columns,
34        public readonly Element $source,
35        public readonly ?string $name = null,
36        public readonly array $referencedTable = [],
37        public readonly array $referencedColumns = [],
38        public readonly ?Element $expression = null,
39        public readonly bool $inline = false,
40        public readonly bool $descending = false,
41    ) {
42        Invariant::names($columns);
43        Invariant::names($referencedTable);
44        Invariant::names($referencedColumns);
45        Invariant::elements($source, $expression);
46        Invariant::ensure(($kind === ConstraintKind::Check) === ($expression !== null), 'Only a CHECK constraint carries a predicate.');
47        Invariant::ensure($kind === ConstraintKind::ForeignKey || ($referencedTable === [] && $referencedColumns === []), 'Only foreign keys carry a reference.');
48        Invariant::ensure($kind !== ConstraintKind::ForeignKey || $referencedTable !== [], 'A foreign key needs a referenced table.');
49    }
50}
51