mirror of
https://github.com/postgres/postgres.git
synced 2026-07-15 12:51:05 -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
|
This form replaces the expression of a generated column. Existing data
|
||||||
in a stored generated column is rewritten and all the future changes
|
in a stored generated column is rewritten and all the future changes
|
||||||
will apply the new generation expression.
|
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>
|
||||||
|
|
||||||
<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",
|
errmsg("column \"%s\" of relation \"%s\" is not a generated column",
|
||||||
colName, RelationGetRelationName(rel))));
|
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)
|
if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
|
||||||
tab->verify_new_notnull = true;
|
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 */
|
/* make sure we don't conflict with later attribute modifications */
|
||||||
CommandCounterIncrement();
|
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
|
* Drop the dependency records of the GENERATED expression, in particular
|
||||||
* its INTERNAL dependency on the column, which would otherwise cause
|
* 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
|
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
||||||
ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check"
|
ERROR: new row for relation "gtest20" violates check constraint "gtest20_b_check"
|
||||||
DETAIL: Failing row contains (30, virtual).
|
DETAIL: Failing row contains (30, virtual).
|
||||||
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 * 100); -- violates constraint
|
||||||
ERROR: ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints
|
ERROR: check constraint "gtest20_b_check" of relation "gtest20" is violated by some row
|
||||||
DETAIL: Column "b" of relation "gtest20" is a virtual generated column.
|
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok
|
||||||
ALTER TABLE gtest20 ALTER COLUMN b SET EXPRESSION AS (a * 3); -- ok (currently not supported)
|
-- table rewrite should not happen
|
||||||
ERROR: ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints
|
SELECT pg_relation_filenode('gtest20') AS gtest20_filenode \gset
|
||||||
DETAIL: Column "b" of relation "gtest20" is a virtual generated column.
|
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);
|
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 (10);
|
||||||
INSERT INTO gtest20a (a) VALUES (30);
|
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
|
gtest_child3 | 09-13-2016 | 1 | 4
|
||||||
(3 rows)
|
(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 generation expression of parent and all its children altogether
|
||||||
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
|
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
|
||||||
\d gtest_parent
|
\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 (10); -- ok
|
||||||
INSERT INTO gtest20 (a) VALUES (30); -- violates constraint
|
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 * 100); -- violates constraint
|
||||||
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 * 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);
|
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 (10);
|
||||||
|
|
@ -536,6 +541,13 @@ ALTER TABLE gtest_child ALTER COLUMN f3 SET EXPRESSION AS (f2 * 10);
|
||||||
\d gtest_child3
|
\d gtest_child3
|
||||||
SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3;
|
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 generation expression of parent and all its children altogether
|
||||||
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
|
ALTER TABLE gtest_parent ALTER COLUMN f3 SET EXPRESSION AS (f2 * 2);
|
||||||
\d gtest_parent
|
\d gtest_parent
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue