Skip to content

Commit f43ca3c

Browse files
committed
Fix handling of inherited check constraints in ALTER COLUMN TYPE.
This case got broken in 8.4 by the addition of an error check that complains if ALTER TABLE ONLY is used on a table that has children. We do use ONLY for this situation, but it's okay because the necessary recursion occurs at a higher level. So we need to have a separate flag to suppress recursion without making the error check. Reported and patched by Pavan Deolasee, with some editorial adjustments by me. Back-patch to 8.4, since this is a regression of functionality that worked in earlier branches.
1 parent efa81e3 commit f43ca3c

File tree

4 files changed

+68
-9
lines changed

4 files changed

+68
-9
lines changed

src/backend/commands/tablecmds.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,15 @@ static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
323323
IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
324324
static void ATExecAddConstraint(List **wqueue,
325325
AlteredTableInfo *tab, Relation rel,
326-
Constraint *newConstraint, bool recurse, LOCKMODE lockmode);
326+
Constraint *newConstraint, bool recurse, bool is_readd,
327+
LOCKMODE lockmode);
327328
static void ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
328329
IndexStmt *stmt, LOCKMODE lockmode);
329330
static void ATAddCheckConstraint(List **wqueue,
330331
AlteredTableInfo *tab, Relation rel,
331332
Constraint *constr,
332-
bool recurse, bool recursing, LOCKMODE lockmode);
333+
bool recurse, bool recursing, bool is_readd,
334+
LOCKMODE lockmode);
333335
static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
334336
Constraint *fkconstraint, LOCKMODE lockmode);
335337
static void ATExecDropConstraint(Relation rel, const char *constrName,
@@ -2560,6 +2562,7 @@ AlterTableGetLockLevel(List *cmds)
25602562
case AT_ColumnDefault:
25612563
case AT_ProcessedConstraint: /* becomes AT_AddConstraint */
25622564
case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */
2565+
case AT_ReAddConstraint: /* becomes AT_AddConstraint */
25632566
case AT_EnableTrig:
25642567
case AT_EnableAlwaysTrig:
25652568
case AT_EnableReplicaTrig:
@@ -3036,11 +3039,15 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
30363039
break;
30373040
case AT_AddConstraint: /* ADD CONSTRAINT */
30383041
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3039-
false, lockmode);
3042+
false, false, lockmode);
30403043
break;
30413044
case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
30423045
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3043-
true, lockmode);
3046+
true, false, lockmode);
3047+
break;
3048+
case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
3049+
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
3050+
false, true, lockmode);
30443051
break;
30453052
case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
30463053
ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def, lockmode);
@@ -5235,7 +5242,8 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
52355242
*/
52365243
static void
52375244
ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
5238-
Constraint *newConstraint, bool recurse, LOCKMODE lockmode)
5245+
Constraint *newConstraint, bool recurse, bool is_readd,
5246+
LOCKMODE lockmode)
52395247
{
52405248
Assert(IsA(newConstraint, Constraint));
52415249

@@ -5248,7 +5256,8 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
52485256
{
52495257
case CONSTR_CHECK:
52505258
ATAddCheckConstraint(wqueue, tab, rel,
5251-
newConstraint, recurse, false, lockmode);
5259+
newConstraint, recurse, false, is_readd,
5260+
lockmode);
52525261
break;
52535262

52545263
case CONSTR_FOREIGN:
@@ -5300,11 +5309,18 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
53005309
* AddRelationNewConstraints would normally assign different names to the
53015310
* child constraints. To fix that, we must capture the name assigned at
53025311
* the parent table and pass that down.
5312+
*
5313+
* When re-adding a previously existing constraint (during ALTER COLUMN TYPE),
5314+
* we don't need to recurse here, because recursion will be carried out at a
5315+
* higher level; the constraint name issue doesn't apply because the names
5316+
* have already been assigned and are just being re-used. We need a separate
5317+
* "is_readd" flag for that; just setting recurse=false would result in an
5318+
* error if there are child tables.
53035319
*/
53045320
static void
53055321
ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
53065322
Constraint *constr, bool recurse, bool recursing,
5307-
LOCKMODE lockmode)
5323+
bool is_readd, LOCKMODE lockmode)
53085324
{
53095325
List *newcons;
53105326
ListCell *lcon;
@@ -5363,6 +5379,13 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
53635379
if (newcons == NIL)
53645380
return;
53655381

5382+
/*
5383+
* Also, in a re-add operation, we don't need to recurse (that will be
5384+
* handled at higher levels).
5385+
*/
5386+
if (is_readd)
5387+
return;
5388+
53665389
/*
53675390
* Propagate to children as appropriate. Unlike most other ALTER
53685391
* routines, we have to do this one level of recursion at a time; we can't
@@ -5394,7 +5417,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
53945417

53955418
/* Recurse to child */
53965419
ATAddCheckConstraint(wqueue, childtab, childrel,
5397-
constr, recurse, true, lockmode);
5420+
constr, recurse, true, is_readd, lockmode);
53985421

53995422
heap_close(childrel, NoLock);
54005423
}
@@ -7220,6 +7243,10 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode)
72207243
/*
72217244
* Attach each generated command to the proper place in the work queue.
72227245
* Note this could result in creation of entirely new work-queue entries.
7246+
*
7247+
* Also note that we have to tweak the command subtypes, because it turns
7248+
* out that re-creation of indexes and constraints has to act a bit
7249+
* differently from initial creation.
72237250
*/
72247251
foreach(list_item, querytree_list)
72257252
{
@@ -7263,6 +7290,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode)
72637290
lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
72647291
break;
72657292
case AT_AddConstraint:
7293+
cmd->subtype = AT_ReAddConstraint;
72667294
tab->subcmds[AT_PASS_OLD_CONSTR] =
72677295
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
72687296
break;

src/include/nodes/parsenodes.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1221,7 +1221,9 @@ typedef enum AlterTableType
12211221
AT_DropInherit, /* NO INHERIT parent */
12221222
AT_AddOf, /* OF <type_name> */
12231223
AT_DropOf, /* NOT OF */
1224-
AT_GenericOptions /* OPTIONS (...) */
1224+
AT_GenericOptions, /* OPTIONS (...) */
1225+
/* this will be in a more natural position in 9.3: */
1226+
AT_ReAddConstraint /* internal to commands/tablecmds.c */
12251227
} AlterTableType;
12261228

12271229
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */

src/test/regress/expected/alter_table.out

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,28 @@ where oid = 'test_storage'::regclass;
15381538
t
15391539
(1 row)
15401540

1541+
-- ALTER TYPE with a check constraint and a child table (bug before Nov 2012)
1542+
CREATE TABLE test_inh_check (a float check (a > 10.2));
1543+
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);
1544+
ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric;
1545+
\d test_inh_check
1546+
Table "public.test_inh_check"
1547+
Column | Type | Modifiers
1548+
--------+---------+-----------
1549+
a | numeric |
1550+
Check constraints:
1551+
"test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
1552+
Number of child tables: 1 (Use \d+ to list them.)
1553+
1554+
\d test_inh_check_child
1555+
Table "public.test_inh_check_child"
1556+
Column | Type | Modifiers
1557+
--------+---------+-----------
1558+
a | numeric |
1559+
Check constraints:
1560+
"test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
1561+
Inherits: test_inh_check
1562+
15411563
--
15421564
-- lock levels
15431565
--

src/test/regress/sql/alter_table.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,13 @@ select reltoastrelid <> 0 as has_toast_table
11451145
from pg_class
11461146
where oid = 'test_storage'::regclass;
11471147

1148+
-- ALTER TYPE with a check constraint and a child table (bug before Nov 2012)
1149+
CREATE TABLE test_inh_check (a float check (a > 10.2));
1150+
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);
1151+
ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric;
1152+
\d test_inh_check
1153+
\d test_inh_check_child
1154+
11481155
--
11491156
-- lock levels
11501157
--

0 commit comments

Comments
 (0)