mirror of
https://github.com/postgres/postgres.git
synced 2026-04-22 22:59:54 -04:00
Allow ALTER COLUMN SET EXPRESSION on virtual columns with CHECK constraints
Previously, changing the generation expression of a virtual column was prohibited if the column was referenced by a CHECK constraint. This lifts that restriction. RememberAllDependentForRebuilding within ATExecSetExpression will rebuild all the dependent constraints, later ATPostAlterTypeCleanup queues the required AlterTableStmt operations for ALTER TABLE Phase 3 execution. Overall, ALTER COLUMN SET EXPRESSION on virtual columns may require scanning the table to re-verify any associated CHECK constraints, but it does not require a table rewrite in ALTER TABLE Phase 3. Author: jian he <jian.universality@gmail.com> Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com> Discussion: https://postgr.es/m/CACJufxH3VETr7orF5rW29GnDk3n1wWbOE3WdkHYd3iPGrQ9E_A@mail.gmail.com
This commit is contained in:
parent
462fe0ff62
commit
f80bedd52b
4 changed files with 56 additions and 27 deletions
|
|
@ -278,6 +278,9 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
|
|||
This form replaces the expression of a generated column. Existing data
|
||||
in a stored generated column is rewritten and all the future changes
|
||||
will apply the new generation expression.
|
||||
Replacing the expression of a virtual generated column do not require a
|
||||
table rewrite, but if the column is used in a constraint, the table will
|
||||
be scanned to check that existing rows meet the constraint.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
|
|
|||
|
|
@ -8675,18 +8675,6 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
|
|||
errmsg("column \"%s\" of relation \"%s\" is not a generated column",
|
||||
colName, RelationGetRelationName(rel))));
|
||||
|
||||
/*
|
||||
* TODO: This could be done, just need to recheck any constraints
|
||||
* afterwards.
|
||||
*/
|
||||
if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
|
||||
rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
|
||||
errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
|
||||
colName, RelationGetRelationName(rel))));
|
||||
|
||||
if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
|
||||
tab->verify_new_notnull = true;
|
||||
|
||||
|
|
@ -8719,15 +8707,14 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
|
|||
|
||||
/* make sure we don't conflict with later attribute modifications */
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* Find everything that depends on the column (constraints, indexes,
|
||||
* etc), and record enough information to let us recreate the objects
|
||||
* after rewrite.
|
||||
*/
|
||||
RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find everything that depends on the column (constraints, indexes, etc),
|
||||
* and record enough information to let us recreate the objects.
|
||||
*/
|
||||
RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
|
||||
|
||||
/*
|
||||
* Drop the dependency records of the GENERATED expression, in particular
|
||||
* its INTERNAL dependency on the column, which would otherwise cause
|
||||
|
|
|
|||
|
|
@ -639,12 +639,30 @@ INSERT INTO gtest20 (a) VALUES (10); -- ok
|
|||
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
||||
ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check"
|
||||
DETAIL: Failing row contains (30, virtual).
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100); -- violates constraint (currently not supported)
|
||||
ERROR: ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints
|
||||
DETAIL: Column "b" of relation "gtest20" is a virtual generated column.
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok (currently not supported)
|
||||
ERROR: ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints
|
||||
DETAIL: Column "b" of relation "gtest20" is a virtual generated column.
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100); -- violates constraint
|
||||
ERROR: check constraint "gtest20_b_check" of relation "gtest20" is violated by some row
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok
|
||||
-- table rewrite should not happen
|
||||
SELECT pg_relation_filenode('gtest20') AS gtest20_filenode \gset
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 4), ADD COLUMN c INT DEFAULT 11;
|
||||
SELECT pg_relation_filenode('gtest20') = :gtest20_filenode AS is_same_file;
|
||||
is_same_file
|
||||
--------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
\d gtest20
|
||||
Table "generated_virtual_tests.gtest20"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+-----------------------------
|
||||
a | integer | | not null |
|
||||
b | integer | | | generated always as (a * 4)
|
||||
c | integer | | | 11
|
||||
Indexes:
|
||||
"gtest20_pkey" PRIMARY KEY, btree (a)
|
||||
Check constraints:
|
||||
"gtest20_b_check" CHECK (b < 50)
|
||||
|
||||
CREATE TABLE gtest20a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) VIRTUAL);
|
||||
INSERT INTO gtest20a (a) VALUES (10);
|
||||
INSERT INTO gtest20a (a) VALUES (30);
|
||||
|
|
@ -988,6 +1006,15 @@ SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
|||
gtest_child3 | 09-13-2016 | 1 | 4
|
||||
(3 rows)
|
||||
|
||||
-- check constraint was validated based on each partitions's generation expression
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc1 CHECK (f3 < 19); -- error
|
||||
ERROR: check constraint "cc1" of relation "gtest_child" is violated by some row
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc1 CHECK (f3 < 66); -- error
|
||||
ERROR: check constraint "cc1" of relation "gtest_child2" is violated by some row
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc1 CHECK (f3 <> 33); -- error
|
||||
ERROR: check constraint "cc1" of relation "gtest_child3" is violated by some row
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc CHECK (f3 < 67); -- ok
|
||||
ALTER TABLE gtest_parent DROP CONSTRAINT cc;
|
||||
-- alter generation expression of parent and all its children altogether
|
||||
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
|
||||
\d gtest_parent
|
||||
|
|
|
|||
|
|
@ -317,8 +317,13 @@ CREATE TABLE gtest20 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) VIRTU
|
|||
INSERT INTO gtest20 (a) VALUES (10); -- ok
|
||||
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
||||
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100); -- violates constraint (currently not supported)
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok (currently not supported)
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 100); -- violates constraint
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok
|
||||
-- table rewrite should not happen
|
||||
SELECT pg_relation_filenode('gtest20') AS gtest20_filenode \gset
|
||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 4), ADD COLUMN c INT DEFAULT 11;
|
||||
SELECT pg_relation_filenode('gtest20') = :gtest20_filenode AS is_same_file;
|
||||
\d gtest20
|
||||
|
||||
CREATE TABLE gtest20a (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) VIRTUAL);
|
||||
INSERT INTO gtest20a (a) VALUES (10);
|
||||
|
|
@ -536,6 +541,13 @@ ALTER TABLE gtest_child ALTER COLUMN f3 SET EXPRESSION AS (f2 * 10);
|
|||
\d gtest_child3
|
||||
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
||||
|
||||
-- check constraint was validated based on each partitions's generation expression
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc1 CHECK (f3 < 19); -- error
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc1 CHECK (f3 < 66); -- error
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc1 CHECK (f3 <> 33); -- error
|
||||
ALTER TABLE gtest_parent ADD CONSTRAINT cc CHECK (f3 < 67); -- ok
|
||||
ALTER TABLE gtest_parent DROP CONSTRAINT cc;
|
||||
|
||||
-- alter generation expression of parent and all its children altogether
|
||||
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
|
||||
\d gtest_parent
|
||||
|
|
|
|||
Loading…
Reference in a new issue