mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Fix ALTER COLUMN ... DROP EXPRESSION with subpartitions
Per commit8bf6ec3ba3, a column can be GENERATED only if it is such in the whole inheritance tree. For this reason, ATPrepDropExpression refuses to be called with ONLY on a partitioned table. To detect this, the current implementation checks whether recurse is set to false and the rel has direct children. Recursion is implemented with ATSimpleRecursion, which calls ATPrepCmd with recurse = false for every node in the tree. Inner nodes (for example a partition which itself has subpartitions) then fail the check, accidentally preventing the command from working on inheritance trees of depth > 2. This commit fixes it by also checking that we're at the top level of the recursive calls using the recursing parameter, which is always true when called through ATSimpleRecursion, always false when invoked on the root rel. Also, remove a comment claiming that DROP EXPRESSION could be implemented with some effort. It cannot, as the commit message for8bf6ec3ba3explains. Author: Alberto Piai <alberto.piai@gmail.com> Backpatch-through: 14 Discussion: https://postgr.es/m/DHMT78XOD8BK.341V3H87KZ7NO@gmail.com
This commit is contained in:
@@ -7854,17 +7854,12 @@ static void
|
||||
ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
|
||||
{
|
||||
/*
|
||||
* Reject ONLY if there are child tables. We could implement this, but it
|
||||
* is a bit complicated. GENERATED clauses must be attached to the column
|
||||
* definition and cannot be added later like DEFAULT, so if a child table
|
||||
* has a generation expression that the parent does not have, the child
|
||||
* column will necessarily be an attlocal column. So to implement ONLY
|
||||
* here, we'd need extra code to update attislocal of the direct child
|
||||
* tables, somewhat similar to how DROP COLUMN does it, so that the
|
||||
* resulting state can be properly dumped and restored.
|
||||
* Reject ONLY if there are child tables -- but only, of course, at the
|
||||
* top of the tree, otherwise it'd be impossible to run this command with
|
||||
* trees deeper than two levels. Caller already got lock.
|
||||
*/
|
||||
if (!recurse &&
|
||||
find_inheritance_children(RelationGetRelid(rel), lockmode))
|
||||
if (!recurse && !recursing &&
|
||||
find_inheritance_children(RelationGetRelid(rel), NoLock))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
|
||||
|
||||
@@ -960,6 +960,56 @@ Inherits: gtest30
|
||||
|
||||
ALTER TABLE gtest30_1 ALTER COLUMN b DROP EXPRESSION; -- error
|
||||
ERROR: cannot drop generation expression from inherited column
|
||||
BEGIN;
|
||||
CREATE TABLE gtest30_1_1 () INHERITS (gtest30_1);
|
||||
ALTER TABLE gtest30 ALTER COLUMN b DROP EXPRESSION;
|
||||
\d gtest30_1_1
|
||||
Table "public.gtest30_1_1"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+---------
|
||||
a | integer | | |
|
||||
b | integer | | |
|
||||
Inherits: gtest30_1
|
||||
|
||||
ROLLBACK;
|
||||
-- test drop expression with subpartitions
|
||||
CREATE TABLE gtest_root (a int, b int, c int GENERATED ALWAYS AS (a + b) STORED) PARTITION BY LIST (a);
|
||||
CREATE TABLE gtest_node PARTITION OF gtest_root FOR VALUES IN (1) PARTITION BY LIST (b);
|
||||
CREATE TABLE gtest_leaf PARTITION OF gtest_node FOR VALUES IN (1);
|
||||
ALTER TABLE gtest_node ALTER COLUMN c DROP EXPRESSION; -- fails
|
||||
ERROR: cannot drop generation expression from inherited column
|
||||
ALTER TABLE ONLY gtest_root ALTER COLUMN c DROP EXPRESSION; -- fails
|
||||
ERROR: ALTER TABLE / DROP EXPRESSION must be applied to child tables too
|
||||
ALTER TABLE gtest_root ALTER COLUMN c DROP EXPRESSION;
|
||||
\d gtest_(root|node|leaf)
|
||||
Table "public.gtest_leaf"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+---------
|
||||
a | integer | | |
|
||||
b | integer | | |
|
||||
c | integer | | |
|
||||
Partition of: gtest_node FOR VALUES IN (1)
|
||||
|
||||
Partitioned table "public.gtest_node"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+---------
|
||||
a | integer | | |
|
||||
b | integer | | |
|
||||
c | integer | | |
|
||||
Partition of: gtest_root FOR VALUES IN (1)
|
||||
Partition key: LIST (b)
|
||||
Number of partitions: 1 (Use \d+ to list them.)
|
||||
|
||||
Partitioned table "public.gtest_root"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+---------
|
||||
a | integer | | |
|
||||
b | integer | | |
|
||||
c | integer | | |
|
||||
Partition key: LIST (a)
|
||||
Number of partitions: 1 (Use \d+ to list them.)
|
||||
|
||||
DROP TABLE gtest_root;
|
||||
-- triggers
|
||||
CREATE TABLE gtest26 (
|
||||
a int PRIMARY KEY,
|
||||
|
||||
@@ -480,6 +480,21 @@ ALTER TABLE ONLY gtest30 ALTER COLUMN b DROP EXPRESSION; -- error
|
||||
\d gtest30
|
||||
\d gtest30_1
|
||||
ALTER TABLE gtest30_1 ALTER COLUMN b DROP EXPRESSION; -- error
|
||||
BEGIN;
|
||||
CREATE TABLE gtest30_1_1 () INHERITS (gtest30_1);
|
||||
ALTER TABLE gtest30 ALTER COLUMN b DROP EXPRESSION;
|
||||
\d gtest30_1_1
|
||||
ROLLBACK;
|
||||
|
||||
-- test drop expression with subpartitions
|
||||
CREATE TABLE gtest_root (a int, b int, c int GENERATED ALWAYS AS (a + b) STORED) PARTITION BY LIST (a);
|
||||
CREATE TABLE gtest_node PARTITION OF gtest_root FOR VALUES IN (1) PARTITION BY LIST (b);
|
||||
CREATE TABLE gtest_leaf PARTITION OF gtest_node FOR VALUES IN (1);
|
||||
ALTER TABLE gtest_node ALTER COLUMN c DROP EXPRESSION; -- fails
|
||||
ALTER TABLE ONLY gtest_root ALTER COLUMN c DROP EXPRESSION; -- fails
|
||||
ALTER TABLE gtest_root ALTER COLUMN c DROP EXPRESSION;
|
||||
\d gtest_(root|node|leaf)
|
||||
DROP TABLE gtest_root;
|
||||
|
||||
-- triggers
|
||||
CREATE TABLE gtest26 (
|
||||
|
||||
Reference in New Issue
Block a user