Move the `ConstraintName` and `Check` `string` fields on an
`alter_column` operation into a new `CheckConstraint` struct and make
validation a method on that new struct.
This is to facilitate being able to create tables and columns with
`CHECK` constraints in later PRs (#108, #109).
Make it required to supply a name for the `CHECK` constraint when adding
one with the `set_check_constraint` operation.
It should be possible to drop constraints with a later migration (not
yet implemented), so requiring a name and not relying on automatic
generation of constraint names will make this easier.
The same thing was done for indexes in
https://github.com/xataio/pg-roll/pull/59
Move the `set_foreign_key` and `set_check_constraint` operations into
the new `alter_column` operation introduced in #91.
The pattern for moving the operations is the same as in #91:
* Update the example migrations to use the new operation type
* Remove serialization and deserialization logic for the individual
operations.
* Make the `alter_column` operation construct the right type of 'inner
operation'.
* Update tests for the foreign key and check constraint ops to use the
`alter_column` operation.
Add support for an operation to add a `CHECK` constraint to an existing
column. The new operation looks like this:
```json
{
"name": "22_add_check_constraint",
"operations": [
{
"set_check_constraint": {
"table": "posts",
"column": "title",
"check": "length(title) > 3",
"up": "(SELECT CASE WHEN length(title) <= 3 THEN LPAD(title, 4, '-') ELSE title END)",
"down": "title"
}
}
]
}
```
This migrations adds a `CHECK (length(title) > 3)` constraint to the
`title` column on the `posts` table. Pre-existing values in the old
schema are rewritten to meet the constraint using the `up` SQL.
The implementation is similar to the **set not null**, **change column
type** and **set foreign key** operations.
* On `Start`:
* The column is duplicated and a `NOT VALID` `CHECK` constraint is added
to the new column.
* Values from the old column are backfilled into the new column using
`up` SQL.
* Triggers are created to copy values from old -> new with `up` SQL and
from new->old using `down` SQL.
* On `Complete`
* The `CHECK` constraint is validated
* The old column is dropped and the new column renamed to the name of
the old column.
* Postgres ensures that the `CHECK` constraint is also updated to apply
to the new column.
* Triggers and trigger functions are removed.
* On `Rollback`
* The new column is removed
* Triggers and trigger functions are removed.
As with other operations involving `up` and `down` SQL, it is the user's
responsibility to ensure that values from the old schema that don't meet
the new `CHECK` constraint are correctly rewritten to meet the
constraint with `up` SQL. If the `up` SQL fails to produce a value that
meets the constraint, the migration will fail either at start (for
existing values in the old schema) or at runtime (for values written to
the old schema during the migration period).