classForeignKeyCascade
final class ForeignKeyCascade
Applies to one child table what happened to its parent.
A parent row that moved only reaches its children where nothing else holds the key up, so each candidate is checked against the parent table first. An update carries new values to write; a delete carries none, and the action decides whether the child goes with it, is set to null, or refuses the statement outright.
What comes back is what happened to the child table, in the same shape as what happened to the parent, which is what lets the caller keep following the constraints outward without knowing how deep it is.
Type Aliases§
Row = import from TableDefinitionRowValue = import from TableDefinitionMethods§
public function __construct(
private ForeignKeyEnds $ends,
private ParentKeyLookup $parents = new \ZtdQuery\Shadow\ParentKeyLookup(),
private RowMatch $rows = new \ZtdQuery\Shadow\Row\RowMatch(),
)Parameters
$ends | ForeignKeyEnds | Answers which parent columns a key points at |
$parents | ParentKeyLookup | Reports whether a referenced row is still there |
$rows | RowMatch | Finds a row among rows |
Test cases 12
ForeignKeyCascadeTest::testApplyActionCarriesTheNewValueWhereTheActionSaysToCascadecoversForeignKeyCascadeTest::testApplyActionRefusesTheStatementWhereTheActionForbidsItcoversForeignKeyCascadeTest::testApplyActionSetsTheKeyToNullWhereTheActionSaysTocoversForeignKeyCascadeTest::testCarryDeleteDropsEveryChildThatWasHoldingTheKeyWhereTheActionCascadescoversForeignKeyCascadeTest::testCarryDeleteReachesNothingWhereTheParentTableStillHoldsTheKeycoversForeignKeyCascadeTest::testCarryDeleteSetsTheKeyToNullWhereTheActionSaysToRatherThanDroppingTheRowcoversForeignKeyCascadeTest::testCarryUpdateReachesNothingWhereTheParentTableStillHoldsTheOldKeycoversForeignKeyCascadeTest::testCarryUpdateWritesTheNewKeyIntoEveryChildThatWasHoldingTheOldOnecoversForeignKeyCascadeTest::testOfCarriesTheNewKeyToTheChildrenOfAnUpdatedParentcoversForeignKeyCascadeTest::testOfDeletesTheChildrenOfADeletedParentWhereTheActionSaysToCascadecoversForeignKeyCascadeTest::testOfFollowsNothingThroughAKeyWhoseEndsDisagreecoversForeignKeyCascadeTest::testOfLeavesTheChildrenAloneWhileAnotherParentRowHoldsTheKeyUpcovers
Calls 2
- new ParentKeyLookup line 40
- new RowMatch line 41
public function of(
ShadowStore $store,
string $childTable,
string $constraintName,
ForeignKeyDefinition $foreignKey,
TableTransition $parent,
string $sql,
): TableTransition|nullAnswers what one constraint makes of what happened to the parent.
Parameters
$store | ShadowStore | Shadow the child rows live in, written back in place |
$childTable | string | Table holding the key |
$constraintName | string | Constraint being followed, for the refusal |
$foreignKey | ForeignKeyDefinition | Constraint being followed |
$parent | TableTransition | What happened to the parent table |
$sql | string | Statement being simulated, for the refusal |
Returns
TableTransition|null What happened to the child, or null when nothing didThrows
ForeignKeyViolationException When the action forbids the statementTest cases 4
ForeignKeyCascadeTest::testOfCarriesTheNewKeyToTheChildrenOfAnUpdatedParentcovers and callsForeignKeyCascadeTest::testOfDeletesTheChildrenOfADeletedParentWhereTheActionSaysToCascadecovers and callsForeignKeyCascadeTest::testOfFollowsNothingThroughAKeyWhoseEndsDisagreecovers and callsForeignKeyCascadeTest::testOfLeavesTheChildrenAloneWhileAnotherParentRowHoldsTheKeyUpcovers and calls
Called from 1
Calls 14
- method-call ForeignKeyEnds::areBalanced() line 67
- new FollowedConstraint line 71
- method-call ForeignKeyEnds::referencedColumns() line 72
- new CascadedChildren line 73
- function-call
array_valuesline 73 - method-call ShadowStore::get() line 73
- method-call ForeignKeyCascade::carryUpdate() line 75
- method-call ForeignKeyCascade::carryDelete() line 78
- method-call CascadedChildren::areUnchanged() line 80
- method-call ShadowStore::set() line 84
- method-call CascadedChildren::rows() line 84
- new TableTransition line 86
- method-call CascadedChildren::deleted() line 86
- method-call CascadedChildren::updated() line 86
public function carryUpdate(
CascadedChildren $children,
ShadowStore $store,
FollowedConstraint $constraint,
list<string> $referencedColumns,
RowChange $parentChange,
): voidCarries a parent row that moved to the children that were holding it.
A key the parent table still holds up somewhere else has not moved as far as the child is concerned, so nothing is carried for it.
Parameters
$children | CascadedChildren | Child rows as the cascade has left them |
$store | ShadowStore | Shadow the parent rows live in |
$constraint | FollowedConstraint | Constraint being followed |
$referencedColumns | list<string> | Parent columns the key points at |
$parentChange | RowChange | What happened to the one parent row |
Throws
ForeignKeyViolationException When the action forbids the statementTest cases 3
Called from 1
Calls 6
- method-call RowMatch::valuesOf() line 111
- method-call ParentKeyLookup::exists() line 115
- method-call CascadedChildren::rows() line 120
- method-call RowMatch::carries() line 121
- method-call CascadedChildren::replace() line 124
- method-call ForeignKeyCascade::applyAction() line 124
public function carryDelete(
CascadedChildren $children,
ShadowStore $store,
FollowedConstraint $constraint,
list<string> $referencedColumns,
Row $parentRow,
): voidCarries a parent row that went to the children that were holding it.
Parameters
$children | CascadedChildren | Child rows as the cascade has left them |
$store | ShadowStore | Shadow the parent rows live in |
$constraint | FollowedConstraint | Constraint being followed |
$referencedColumns | list<string> | Parent columns the key points at |
$parentRow | Row | The parent row that went |
Throws
ForeignKeyViolationException When the action forbids the statementTest cases 5
ForeignKeyCascadeTest::testCarryDeleteDropsEveryChildThatWasHoldingTheKeyWhereTheActionCascadescovers and callsForeignKeyCascadeTest::testCarryDeleteReachesNothingWhereTheParentTableStillHoldsTheKeycovers and callsForeignKeyCascadeTest::testCarryDeleteSetsTheKeyToNullWhereTheActionSaysToRatherThanDroppingTheRowcovers and callsForeignKeyCascadeTest::testOfDeletesTheChildrenOfADeletedParentWhereTheActionSaysToCascadecoversForeignKeyCascadeTest::testOfLeavesTheChildrenAloneWhileAnotherParentRowHoldsTheKeyUpcovers
Called from 1
Calls 8
- method-call RowMatch::valuesOf() line 147
- method-call ParentKeyLookup::exists() line 149
- method-call CascadedChildren::rows() line 155
- method-call RowMatch::carries() line 156
- class-const ReferentialAction::Cascade() line 159
- method-call CascadedChildren::replace() line 163
- method-call ForeignKeyCascade::applyAction() line 163
- method-call CascadedChildren::remove() line 165
public function applyAction(
Row $row,
list<RowValue> $values,
ReferentialAction $action,
FollowedConstraint $constraint,
): RowWrites into a child row what the declared action says should be there.
Only CASCADE and SET NULL let the statement stand. Every other action — RESTRICT, NO ACTION, SET DEFAULT — means the parent was not free to move, so the statement is refused rather than quietly rewritten.
Parameters
$row | Row | Child row to rewrite |
$values | list<RowValue> | Values to carry over, empty when the parent went |
$action | ReferentialAction | Action the constraint declares |
$constraint | FollowedConstraint | Constraint being followed, for the refusal |
Returns
Row The child row as it should now beThrows
ForeignKeyViolationException When the action forbids the statementTest cases 6
ForeignKeyCascadeTest::testApplyActionCarriesTheNewValueWhereTheActionSaysToCascadecovers and callsForeignKeyCascadeTest::testApplyActionRefusesTheStatementWhereTheActionForbidsItcovers and callsForeignKeyCascadeTest::testApplyActionSetsTheKeyToNullWhereTheActionSaysTocovers and callsForeignKeyCascadeTest::testCarryDeleteSetsTheKeyToNullWhereTheActionSaysToRatherThanDroppingTheRowcoversForeignKeyCascadeTest::testCarryUpdateWritesTheNewKeyIntoEveryChildThatWasHoldingTheOldOnecoversForeignKeyCascadeTest::testOfCarriesTheNewKeyToTheChildrenOfAnUpdatedParentcovers
Called from 2
Calls 4
- class-const ReferentialAction::Cascade() line 190
- class-const ReferentialAction::SetNull() line 190
- static-call ForeignKeyViolationException::of() line 191
- method-call ForeignKeyEnds::referencedColumns() line 196
Private surface 3§
Implementation details, listed for orientation only.
private ForeignKeyEnds $endsprivate ParentKeyLookup $parents = new \ZtdQuery\Shadow\ParentKeyLookup()private RowMatch $rows = new \ZtdQuery\Shadow\Row\RowMatch()Test cases 16§
Test cases that cover or call this symbol, from the coverage report and from the analyzed test sources.
Dedicated tests 12
ForeignKeyCascadeTest::testApplyActionCarriesTheNewValueWhereTheActionSaysToCascadecovers and callsForeignKeyCascadeTest::testApplyActionRefusesTheStatementWhereTheActionForbidsItcovers and callsForeignKeyCascadeTest::testApplyActionSetsTheKeyToNullWhereTheActionSaysTocovers and callsForeignKeyCascadeTest::testCarryDeleteDropsEveryChildThatWasHoldingTheKeyWhereTheActionCascadescovers and callsForeignKeyCascadeTest::testCarryDeleteReachesNothingWhereTheParentTableStillHoldsTheKeycovers and callsForeignKeyCascadeTest::testCarryDeleteSetsTheKeyToNullWhereTheActionSaysToRatherThanDroppingTheRowcovers and callsForeignKeyCascadeTest::testCarryUpdateReachesNothingWhereTheParentTableStillHoldsTheOldKeycovers and callsForeignKeyCascadeTest::testCarryUpdateWritesTheNewKeyIntoEveryChildThatWasHoldingTheOldOnecovers and callsForeignKeyCascadeTest::testOfCarriesTheNewKeyToTheChildrenOfAnUpdatedParentcovers and callsForeignKeyCascadeTest::testOfDeletesTheChildrenOfADeletedParentWhereTheActionSaysToCascadecovers and callsForeignKeyCascadeTest::testOfFollowsNothingThroughAKeyWhoseEndsDisagreecovers and callsForeignKeyCascadeTest::testOfLeavesTheChildrenAloneWhileAnotherParentRowHoldsTheKeyUpcovers and calls
Other tests reaching this symbol 4
SessionTestcallsReferentialIntegrityEnforcerTestcallsShadowApplicationTestcallsStatementSimulatorTestcalls