Skip to content

Commit

Permalink
Document auto-validation of foreign keys
Browse files Browse the repository at this point in the history
Fixes #4891.
Fixes #5187.
  • Loading branch information
jseldess authored and RaduBerinde committed Dec 11, 2019
1 parent e4ada32 commit 40c3ae0
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 98 deletions.
48 changes: 22 additions & 26 deletions _includes/v19.2/sql/diagrams/add_constraint.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div><svg width="684" height="134">
<div><svg width="695" height="135">
<polygon points="9 17 1 13 1 21"></polygon>
<polygon points="17 17 9 13 9 21"></polygon>
<rect x="31" y="3" width="62" height="32" rx="10"></rect>
Expand All @@ -10,29 +10,25 @@
<rect x="215" y="35" width="34" height="32" rx="10"></rect>
<rect x="213" y="33" width="34" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="223" y="53">IF</text>
<rect x="269" y="35" width="68" height="32" rx="10"></rect>
<rect x="267" y="33" width="68" height="32" class="terminal" rx="10"></rect>
<rect x="269" y="35" width="70" height="32" rx="10"></rect>
<rect x="267" y="33" width="70" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="277" y="53">EXISTS</text>
<rect x="377" y="3" width="90" height="32"></rect>
<rect x="375" y="1" width="90" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="385" y="21">table_name</text>
<rect x="487" y="3" width="48" height="32" rx="10"></rect>
<rect x="485" y="1" width="48" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="495" y="21">ADD</text>
<rect x="555" y="3" width="108" height="32" rx="10"></rect>
<rect x="553" y="1" width="108" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="563" y="21">CONSTRAINT</text>
<a xlink:href="sql-grammar.html#constraint_name" xlink:title="constraint_name">
<rect x="401" y="101" width="120" height="32"></rect>
<rect x="399" y="99" width="120" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="409" y="119">constraint_name</text>
</a>
<a xlink:href="sql-grammar.html#constraint_elem" xlink:title="constraint_elem">
<rect x="541" y="101" width="116" height="32"></rect>
<rect x="539" y="99" width="116" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="549" y="119">constraint_elem</text>
</a>
<path class="line" d="m17 17 h2 m0 0 h10 m62 0 h10 m0 0 h10 m62 0 h10 m20 0 h10 m0 0 h132 m-162 0 h20 m142 0 h20 m-182 0 q10 0 10 10 m162 0 q0 -10 10 -10 m-172 10 v12 m162 0 v-12 m-162 12 q0 10 10 10 m142 0 q10 0 10 -10 m-152 10 h10 m34 0 h10 m0 0 h10 m68 0 h10 m20 -32 h10 m90 0 h10 m0 0 h10 m48 0 h10 m0 0 h10 m108 0 h10 m2 0 l2 0 m2 0 l2 0 m2 0 l2 0 m-306 98 l2 0 m2 0 l2 0 m2 0 l2 0 m2 0 h10 m120 0 h10 m0 0 h10 m116 0 h10 m3 0 h-3"></path>
<polygon points="675 115 683 111 683 119"></polygon>
<polygon points="675 115 667 111 667 119"></polygon>
</svg></div>
<rect x="379" y="3" width="96" height="32"></rect>
<rect x="377" y="1" width="96" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="387" y="21">table_name</text><rect x="495" y="3" width="48" height="32" rx="10"></rect>
<rect x="493" y="1" width="48" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="503" y="21">ADD</text>
<rect x="563" y="3" width="110" height="32" rx="10"></rect>
<rect x="561" y="1" width="110" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="571" y="21">CONSTRAINT</text><a xlink:href="sql-grammar.html#constraint_name" xlink:title="constraint_name">
<rect x="217" y="101" width="126" height="32"></rect>
<rect x="215" y="99" width="126" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="225" y="119">constraint_name</text></a><a xlink:href="sql-grammar.html#constraint_elem" xlink:title="constraint_elem">
<rect x="363" y="101" width="122" height="32"></rect>
<rect x="361" y="99" width="122" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="371" y="119">constraint_elem</text></a><a xlink:href="sql-grammar.html#opt_validate_behavior" xlink:title="opt_validate_behavior">
<rect x="505" y="101" width="162" height="32"></rect>
<rect x="503" y="99" width="162" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="513" y="119">opt_validate_behavior</text></a><path class="line" d="m17 17 h2 m0 0 h10 m62 0 h10 m0 0 h10 m62 0 h10 m20 0 h10 m0 0 h134 m-164 0 h20 m144 0 h20 m-184 0 q10 0 10 10 m164 0 q0 -10 10 -10 m-174 10 v12 m164 0 v-12 m-164 12 q0 10 10 10 m144 0 q10 0 10 -10 m-154 10 h10 m34 0 h10 m0 0 h10 m70 0 h10 m20 -32 h10 m96 0 h10 m0 0 h10 m48 0 h10 m0 0 h10 m110 0 h10 m2 0 l2 0 m2 0 l2 0 m2 0 l2 0 m-500 98 l2 0 m2 0 l2 0 m2 0 l2 0 m2 0 h10 m126 0 h10 m0 0 h10 m122 0 h10 m0 0 h10 m162 0 h10 m3 0 h-3"></path>
<polygon points="685 115 693 111 693 119"></polygon>
<polygon points="685 115 677 111 677 119"></polygon></svg></div>
48 changes: 22 additions & 26 deletions _includes/v20.1/sql/diagrams/add_constraint.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div><svg width="684" height="134">
<div><svg width="695" height="135">
<polygon points="9 17 1 13 1 21"></polygon>
<polygon points="17 17 9 13 9 21"></polygon>
<rect x="31" y="3" width="62" height="32" rx="10"></rect>
Expand All @@ -10,29 +10,25 @@
<rect x="215" y="35" width="34" height="32" rx="10"></rect>
<rect x="213" y="33" width="34" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="223" y="53">IF</text>
<rect x="269" y="35" width="68" height="32" rx="10"></rect>
<rect x="267" y="33" width="68" height="32" class="terminal" rx="10"></rect>
<rect x="269" y="35" width="70" height="32" rx="10"></rect>
<rect x="267" y="33" width="70" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="277" y="53">EXISTS</text>
<rect x="377" y="3" width="90" height="32"></rect>
<rect x="375" y="1" width="90" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="385" y="21">table_name</text>
<rect x="487" y="3" width="48" height="32" rx="10"></rect>
<rect x="485" y="1" width="48" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="495" y="21">ADD</text>
<rect x="555" y="3" width="108" height="32" rx="10"></rect>
<rect x="553" y="1" width="108" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="563" y="21">CONSTRAINT</text>
<a xlink:href="sql-grammar.html#constraint_name" xlink:title="constraint_name">
<rect x="401" y="101" width="120" height="32"></rect>
<rect x="399" y="99" width="120" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="409" y="119">constraint_name</text>
</a>
<a xlink:href="sql-grammar.html#constraint_elem" xlink:title="constraint_elem">
<rect x="541" y="101" width="116" height="32"></rect>
<rect x="539" y="99" width="116" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="549" y="119">constraint_elem</text>
</a>
<path class="line" d="m17 17 h2 m0 0 h10 m62 0 h10 m0 0 h10 m62 0 h10 m20 0 h10 m0 0 h132 m-162 0 h20 m142 0 h20 m-182 0 q10 0 10 10 m162 0 q0 -10 10 -10 m-172 10 v12 m162 0 v-12 m-162 12 q0 10 10 10 m142 0 q10 0 10 -10 m-152 10 h10 m34 0 h10 m0 0 h10 m68 0 h10 m20 -32 h10 m90 0 h10 m0 0 h10 m48 0 h10 m0 0 h10 m108 0 h10 m2 0 l2 0 m2 0 l2 0 m2 0 l2 0 m-306 98 l2 0 m2 0 l2 0 m2 0 l2 0 m2 0 h10 m120 0 h10 m0 0 h10 m116 0 h10 m3 0 h-3"></path>
<polygon points="675 115 683 111 683 119"></polygon>
<polygon points="675 115 667 111 667 119"></polygon>
</svg></div>
<rect x="379" y="3" width="96" height="32"></rect>
<rect x="377" y="1" width="96" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="387" y="21">table_name</text><rect x="495" y="3" width="48" height="32" rx="10"></rect>
<rect x="493" y="1" width="48" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="503" y="21">ADD</text>
<rect x="563" y="3" width="110" height="32" rx="10"></rect>
<rect x="561" y="1" width="110" height="32" class="terminal" rx="10"></rect>
<text class="terminal" x="571" y="21">CONSTRAINT</text><a xlink:href="sql-grammar.html#constraint_name" xlink:title="constraint_name">
<rect x="217" y="101" width="126" height="32"></rect>
<rect x="215" y="99" width="126" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="225" y="119">constraint_name</text></a><a xlink:href="sql-grammar.html#constraint_elem" xlink:title="constraint_elem">
<rect x="363" y="101" width="122" height="32"></rect>
<rect x="361" y="99" width="122" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="371" y="119">constraint_elem</text></a><a xlink:href="sql-grammar.html#opt_validate_behavior" xlink:title="opt_validate_behavior">
<rect x="505" y="101" width="162" height="32"></rect>
<rect x="503" y="99" width="162" height="32" class="nonterminal"></rect>
<text class="nonterminal" x="513" y="119">opt_validate_behavior</text></a><path class="line" d="m17 17 h2 m0 0 h10 m62 0 h10 m0 0 h10 m62 0 h10 m20 0 h10 m0 0 h134 m-164 0 h20 m144 0 h20 m-184 0 q10 0 10 10 m164 0 q0 -10 10 -10 m-174 10 v12 m164 0 v-12 m-164 12 q0 10 10 10 m144 0 q10 0 10 -10 m-154 10 h10 m34 0 h10 m0 0 h10 m70 0 h10 m20 -32 h10 m96 0 h10 m0 0 h10 m48 0 h10 m0 0 h10 m110 0 h10 m2 0 l2 0 m2 0 l2 0 m2 0 l2 0 m-500 98 l2 0 m2 0 l2 0 m2 0 l2 0 m2 0 h10 m126 0 h10 m0 0 h10 m122 0 h10 m0 0 h10 m162 0 h10 m3 0 h-3"></path>
<polygon points="685 115 693 111 693 119"></polygon>
<polygon points="685 115 677 111 677 119"></polygon></svg></div>
44 changes: 34 additions & 10 deletions v19.2/add-constraint.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ toc: true

The `ADD CONSTRAINT` [statement](sql-statements.html) is part of `ALTER TABLE` and can add the following [constraints](constraints.html) to columns:

- [`UNIQUE`](#add-the-unique-constraint)
- [`CHECK`](#add-the-check-constraint)
- [Foreign key](#add-the-foreign-key-constraint-with-cascade)
- [`UNIQUE`](#add-a-unique-constraint)
- [`CHECK`](#add-a-check-constraint)
- [`FOREIGN KEY`](#add-a-foreign-key-constraint-with-cascade)

{{site.data.alerts.callout_info}}
The [`PRIMARY KEY`](primary-key.html) can only be applied through [`CREATE TABLE`](create-table.html). The [`DEFAULT`](default-value.html) and [`NOT NULL`](not-null.html) constraints are managed through [`ALTER COLUMN`](alter-column.html).
Expand All @@ -32,26 +32,27 @@ The user must have the `CREATE` [privilege](authorization.html#assign-privileges
-----------|-------------
`table_name` | The name of the table containing the column you want to constrain.
`constraint_name` | The name of the constraint, which must be unique to its table and follow these [identifier rules](keywords-and-identifiers.html#identifiers).
`constraint_elem` | The [`CHECK`](check.html), [foreign key](foreign-key.html), [`UNIQUE`](unique.html) constraint you want to add. <br/><br/>Adding/changing a `DEFAULT` constraint is done through [`ALTER COLUMN`](alter-column.html). <br/><br/>Adding/changing the table's `PRIMARY KEY` is not supported through `ALTER TABLE`; it can only be specified during [table creation](create-table.html).
`constraint_elem` | The [`CHECK`](check.html), [`FOREIGN KEY`](foreign-key.html), [`UNIQUE`](unique.html) constraint you want to add.<br/><br/>Adding/changing a `DEFAULT` constraint is done through [`ALTER COLUMN`](alter-column.html).<br/><br/>Adding/changing the table's `PRIMARY KEY` is not supported through `ALTER TABLE`; it can only be specified during [table creation](create-table.html).<br><br><span class="version-tag">New in v19.2:</span> Applying a foreign key constraint now validates existing rows in addition to enforcing conformance for new rows. As such, it is no longer necessary to use [`VALIDATE CONSTRAINT`](validate-constraint.html) for foreign keys. To create an unvalidated foreign key constraint, the `NOT VALID` keyword can be used.
`opt_validation` | An optional `NOT VALID` modifier used to create unvalidated constraints. When creating an unvalidated constraint, the system does not check that existing table data satisfies the constraint. The constraint is still enforced when table data is modified. An unvalidated constraint can later be validated using [`VALIDATE CONSTRAINT`](validate-constraint.html).

## Viewing schema changes

{% include {{ page.version.version }}/misc/schema-change-view-job.md %}

## Examples

### Add the `UNIQUE` constraint
### Add a `UNIQUE` constraint

Adding the [`UNIQUE` constraint](unique.html) requires that all of a column's values be distinct from one another (except for *NULL* values).
Adding a [`UNIQUE` constraint](unique.html) requires that all of a column's values be distinct from one another (except for *NULL* values).

{% include copy-clipboard.html %}
~~~ sql
> ALTER TABLE orders ADD CONSTRAINT id_customer_unique UNIQUE (id, customer);
~~~

### Add the `CHECK` constraint
### Add a `CHECK` constraint

Adding the [`CHECK` constraint](check.html) requires that all of a column's values evaluate to `TRUE` for a Boolean expression.
Adding a [`CHECK` constraint](check.html) requires that all of a column's values evaluate to `TRUE` for a Boolean expression.

{% include copy-clipboard.html %}
~~~ sql
Expand Down Expand Up @@ -82,9 +83,9 @@ The entire transaction will be rolled back, including any new columns that were
- If a new column has a default value or is a [computed column](computed-columns.html) that would have contained values that violate the new constraint.
{{site.data.alerts.end}}

### Add the foreign key constraint with `CASCADE`
### Add a foreign key constraint with `CASCADE`

To add a foreign key constraint, use the steps shown below.
To add a cascading foreign key constraint, use the steps shown below.

Given two tables, `customers` and `orders`:

Expand Down Expand Up @@ -158,6 +159,29 @@ An index on the referencing columns is automatically created for you when you ad
Adding a foreign key for a non-empty table without an appropriate index will fail, since foreign key columns must be indexed. For more information about the requirements for creating foreign keys, see [Rules for creating foreign keys](foreign-key.html#rules-for-creating-foreign-keys).
{{site.data.alerts.end}}

### Add an unvalidated foreign key constraint

On the same tables as above, we add an unvalidated foreign key constraint.

{% include copy-clipboard.html %}
~~~ sql
> ALTER TABLE orders ADD CONSTRAINT customer_fk FOREIGN KEY (customer_id) REFERENCES customers (id) NOT VALID;
~~~

Even unvalidated, the constraint is enforced during table mutations:
{% include copy-clipboard.html %}
~~~ sql
> INSERT INTO orders VALUES (1, 1, 'open');
pq: foreign key violation: value [1] not found in customers@primary [id] (txn=9d649525-461e-4e0f-906a-712455125f0e)
~~~

To validate the constraint (which checks that all existing data conforms to the
constraint):
{% include copy-clipboard.html %}
~~~ sql
> ALTER TABLE orders VALIDATE CONSTRAINT customer_fk;
~~~

## See also

- [Constraints](constraints.html)
Expand Down
6 changes: 3 additions & 3 deletions v19.2/check.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ The `CHECK` [constraint](constraints.html) specifies that values for the column

## Details

- If you add a `CHECK` constraint to an existing table, CockroachDB will run a background job to validate existing table data in the process of adding the constraint. If a row is found that violates the constraint during the validation step, the [`ADD CONSTRAINT`](add-constraint.html) statement will fail. This differs from previous versions of CockroachDB, which allowed you to add a check constraint that was enforced for writes but could be violated by rows that existed prior to adding the constraint.
- Check constraints can be added to columns that were created earlier in the same transaction. For an example, see [Add the `CHECK` constraint](add-constraint.html#add-the-check-constraint).
- If you add a `CHECK` constraint to an existing table, CockroachDB will run a background job to validate existing table data in the process of adding the constraint. If a row is found that violates the constraint during the validation step, the [`ADD CONSTRAINT`](add-constraint.html) statement will fail. This differs from previous versions of CockroachDB, which allowed you to add a check constraint that was enforced for writes but could be violated by rows that existed prior to adding the constraint. If the previous behavior is preferred, an unvalidated `CHECK` constraint can be created using the `NOT VALID` modifier.
- Check constraints can be added to columns that were created earlier in the same transaction. For an example, see [Add a `CHECK` constraint](add-constraint.html#add-a-check-constraint).
- `CHECK` constraints may be specified at the column or table level and can reference other columns within the table. Internally, all column-level `CHECK` constraints are converted to table-level constraints so they can be handled consistently.
- You can have multiple `CHECK` constraints on a single column but ideally, for performance optimization, these should be combined using the logical operators. For example:

Expand All @@ -28,7 +28,7 @@ The `CHECK` [constraint](constraints.html) specifies that values for the column

`CHECK` constraints can be defined at the [table level](#table-level). However, if you only want the constraint to apply to a single column, it can be applied at the [column level](#column-level).

{{site.data.alerts.callout_info}}You can also add the <code>CHECK</code> constraint to existing tables through <a href="add-constraint.html#add-the-check-constraint"><code>ADD CONSTRAINT</code></a>.{{site.data.alerts.end}}
{{site.data.alerts.callout_info}}You can also add the <code>CHECK</code> constraint to existing tables through <a href="add-constraint.html#add-a-check-constraint"><code>ADD CONSTRAINT</code></a>.{{site.data.alerts.end}}

### Column level

Expand Down
4 changes: 2 additions & 2 deletions v19.2/foreign-key.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ For example, given an `orders` table and a `customers` table, if you create a co
- Cannot be a [computed column](computed-columns.html).
- Foreign key columns must be [indexed](indexes.html). This is required because updates and deletes on the referenced table will need to search the referencing table for any matching records to ensure those operations would not violate existing references. In practice, such indexes are likely also needed by applications using these tables, since finding all records which belong to some entity, for example all orders for a given customer, is very common.
- To meet this requirement when creating a new table, there are a few options:
- An index on the referencing columns is automatically created for you when you add a foreign key constraint to an empty table, if an appropriate index does not already exist. For an example, see [Add the foreign key constraint with `CASCADE`](add-constraint.html#add-the-foreign-key-constraint-with-cascade).
- An index on the referencing columns is automatically created for you when you add a foreign key constraint to an empty table, if an appropriate index does not already exist. For an example, see [Add the foreign key constraint with `CASCADE`](add-constraint.html#add-a-foreign-key-constraint-with-cascade).
- Create indexes explicitly using the [`INDEX`](create-table.html#create-a-table-with-secondary-and-inverted-indexes) clause of `CREATE TABLE`.
- Rely on indexes created by the [`PRIMARY KEY`](primary-key.html) or [`UNIQUE`](unique.html) constraints.
- Have CockroachDB automatically create an index of the foreign key columns for you. However, it's important to note that if you later remove the Foreign Key constraint, this automatically created index _is not_ removed.
Expand Down Expand Up @@ -118,7 +118,7 @@ You can improve the performance of some statements that use foreign keys by also
Foreign key constraints can be defined at the [table level](#table-level). However, if you only want the constraint to apply to a single column, it can be applied at the [column level](#column-level).

{{site.data.alerts.callout_info}}
You can also add the Foreign Key constraint to existing tables through [`ADD CONSTRAINT`](add-constraint.html#add-the-foreign-key-constraint-with-cascade).
You can also add the Foreign Key constraint to existing tables through [`ADD CONSTRAINT`](add-constraint.html#add-a-foreign-key-constraint-with-cascade).
{{site.data.alerts.end}}

### Column level
Expand Down
Loading

0 comments on commit 40c3ae0

Please sign in to comment.