mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
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.
This commit is contained in:
@ -291,11 +291,11 @@ static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
|||||||
IndexStmt *stmt, bool is_rebuild);
|
IndexStmt *stmt, bool is_rebuild);
|
||||||
static void ATExecAddConstraint(List **wqueue,
|
static void ATExecAddConstraint(List **wqueue,
|
||||||
AlteredTableInfo *tab, Relation rel,
|
AlteredTableInfo *tab, Relation rel,
|
||||||
Node *newConstraint, bool recurse);
|
Node *newConstraint, bool recurse, bool is_readd);
|
||||||
static void ATAddCheckConstraint(List **wqueue,
|
static void ATAddCheckConstraint(List **wqueue,
|
||||||
AlteredTableInfo *tab, Relation rel,
|
AlteredTableInfo *tab, Relation rel,
|
||||||
Constraint *constr,
|
Constraint *constr,
|
||||||
bool recurse, bool recursing);
|
bool recurse, bool recursing, bool is_readd);
|
||||||
static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
|
||||||
FkConstraint *fkconstraint);
|
FkConstraint *fkconstraint);
|
||||||
static void ATExecDropConstraint(Relation rel, const char *constrName,
|
static void ATExecDropConstraint(Relation rel, const char *constrName,
|
||||||
@ -2559,10 +2559,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true);
|
ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true);
|
||||||
break;
|
break;
|
||||||
case AT_AddConstraint: /* ADD CONSTRAINT */
|
case AT_AddConstraint: /* ADD CONSTRAINT */
|
||||||
ATExecAddConstraint(wqueue, tab, rel, cmd->def, false);
|
ATExecAddConstraint(wqueue, tab, rel, cmd->def, false, false);
|
||||||
break;
|
break;
|
||||||
case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
|
case AT_AddConstraintRecurse: /* ADD CONSTRAINT with recursion */
|
||||||
ATExecAddConstraint(wqueue, tab, rel, cmd->def, true);
|
ATExecAddConstraint(wqueue, tab, rel, cmd->def, true, false);
|
||||||
|
break;
|
||||||
|
case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
|
||||||
|
ATExecAddConstraint(wqueue, tab, rel, cmd->def, false, true);
|
||||||
break;
|
break;
|
||||||
case AT_DropConstraint: /* DROP CONSTRAINT */
|
case AT_DropConstraint: /* DROP CONSTRAINT */
|
||||||
ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
|
ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
|
||||||
@ -4309,7 +4312,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||||
Node *newConstraint, bool recurse)
|
Node *newConstraint, bool recurse, bool is_readd)
|
||||||
{
|
{
|
||||||
switch (nodeTag(newConstraint))
|
switch (nodeTag(newConstraint))
|
||||||
{
|
{
|
||||||
@ -4327,7 +4330,7 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
{
|
{
|
||||||
case CONSTR_CHECK:
|
case CONSTR_CHECK:
|
||||||
ATAddCheckConstraint(wqueue, tab, rel,
|
ATAddCheckConstraint(wqueue, tab, rel,
|
||||||
constr, recurse, false);
|
constr, recurse, false, is_readd);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized constraint type: %d",
|
elog(ERROR, "unrecognized constraint type: %d",
|
||||||
@ -4387,10 +4390,18 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
* AddRelationNewConstraints would normally assign different names to the
|
* AddRelationNewConstraints would normally assign different names to the
|
||||||
* child constraints. To fix that, we must capture the name assigned at
|
* child constraints. To fix that, we must capture the name assigned at
|
||||||
* the parent table and pass that down.
|
* the parent table and pass that down.
|
||||||
|
*
|
||||||
|
* When re-adding a previously existing constraint (during ALTER COLUMN TYPE),
|
||||||
|
* we don't need to recurse here, because recursion will be carried out at a
|
||||||
|
* higher level; the constraint name issue doesn't apply because the names
|
||||||
|
* have already been assigned and are just being re-used. We need a separate
|
||||||
|
* "is_readd" flag for that; just setting recurse=false would result in an
|
||||||
|
* error if there are child tables.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
||||||
Constraint *constr, bool recurse, bool recursing)
|
Constraint *constr, bool recurse, bool recursing,
|
||||||
|
bool is_readd)
|
||||||
{
|
{
|
||||||
List *newcons;
|
List *newcons;
|
||||||
ListCell *lcon;
|
ListCell *lcon;
|
||||||
@ -4449,6 +4460,13 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
if (newcons == NIL)
|
if (newcons == NIL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Also, in a re-add operation, we don't need to recurse (that will be
|
||||||
|
* handled at higher levels).
|
||||||
|
*/
|
||||||
|
if (is_readd)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Propagate to children as appropriate. Unlike most other ALTER
|
* Propagate to children as appropriate. Unlike most other ALTER
|
||||||
* routines, we have to do this one level of recursion at a time; we can't
|
* routines, we have to do this one level of recursion at a time; we can't
|
||||||
@ -4481,7 +4499,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
|
|
||||||
/* Recurse to child */
|
/* Recurse to child */
|
||||||
ATAddCheckConstraint(wqueue, childtab, childrel,
|
ATAddCheckConstraint(wqueue, childtab, childrel,
|
||||||
constr, recurse, true);
|
constr, recurse, true, is_readd);
|
||||||
|
|
||||||
heap_close(childrel, NoLock);
|
heap_close(childrel, NoLock);
|
||||||
}
|
}
|
||||||
@ -6057,6 +6075,10 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
|
|||||||
/*
|
/*
|
||||||
* Attach each generated command to the proper place in the work queue.
|
* Attach each generated command to the proper place in the work queue.
|
||||||
* Note this could result in creation of entirely new work-queue entries.
|
* Note this could result in creation of entirely new work-queue entries.
|
||||||
|
*
|
||||||
|
* Also note that we have to tweak the command subtypes, because it turns
|
||||||
|
* out that re-creation of indexes and constraints has to act a bit
|
||||||
|
* differently from initial creation.
|
||||||
*/
|
*/
|
||||||
foreach(list_item, querytree_list)
|
foreach(list_item, querytree_list)
|
||||||
{
|
{
|
||||||
@ -6100,6 +6122,7 @@ ATPostAlterTypeParse(char *cmd, List **wqueue)
|
|||||||
lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
|
lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
|
||||||
break;
|
break;
|
||||||
case AT_AddConstraint:
|
case AT_AddConstraint:
|
||||||
|
cmd->subtype = AT_ReAddConstraint;
|
||||||
tab->subcmds[AT_PASS_OLD_CONSTR] =
|
tab->subcmds[AT_PASS_OLD_CONSTR] =
|
||||||
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
|
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
|
||||||
break;
|
break;
|
||||||
|
@ -1131,7 +1131,9 @@ typedef enum AlterTableType
|
|||||||
AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */
|
AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */
|
||||||
AT_DisableRule, /* DISABLE RULE name */
|
AT_DisableRule, /* DISABLE RULE name */
|
||||||
AT_AddInherit, /* INHERIT parent */
|
AT_AddInherit, /* INHERIT parent */
|
||||||
AT_DropInherit /* NO INHERIT parent */
|
AT_DropInherit, /* NO INHERIT parent */
|
||||||
|
/* this will be in a more natural position in 9.3: */
|
||||||
|
AT_ReAddConstraint /* internal to commands/tablecmds.c */
|
||||||
} AlterTableType;
|
} AlterTableType;
|
||||||
|
|
||||||
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
|
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
|
||||||
|
@ -1456,6 +1456,27 @@ select * from another;
|
|||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
drop table another;
|
drop table another;
|
||||||
|
-- ALTER TYPE with a check constraint and a child table (bug before Nov 2012)
|
||||||
|
CREATE TABLE test_inh_check (a float check (a > 10.2));
|
||||||
|
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);
|
||||||
|
ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric;
|
||||||
|
\d test_inh_check
|
||||||
|
Table "public.test_inh_check"
|
||||||
|
Column | Type | Modifiers
|
||||||
|
--------+---------+-----------
|
||||||
|
a | numeric |
|
||||||
|
Check constraints:
|
||||||
|
"test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
|
||||||
|
|
||||||
|
\d test_inh_check_child
|
||||||
|
Table "public.test_inh_check_child"
|
||||||
|
Column | Type | Modifiers
|
||||||
|
--------+---------+-----------
|
||||||
|
a | numeric |
|
||||||
|
Check constraints:
|
||||||
|
"test_inh_check_a_check" CHECK (a::double precision > 10.2::double precision)
|
||||||
|
Inherits: test_inh_check
|
||||||
|
|
||||||
-- disallow recursive containment of row types
|
-- disallow recursive containment of row types
|
||||||
create temp table recur1 (f1 int);
|
create temp table recur1 (f1 int);
|
||||||
alter table recur1 add column f2 recur1; -- fails
|
alter table recur1 add column f2 recur1; -- fails
|
||||||
|
@ -1081,6 +1081,13 @@ select * from another;
|
|||||||
|
|
||||||
drop table another;
|
drop table another;
|
||||||
|
|
||||||
|
-- ALTER TYPE with a check constraint and a child table (bug before Nov 2012)
|
||||||
|
CREATE TABLE test_inh_check (a float check (a > 10.2));
|
||||||
|
CREATE TABLE test_inh_check_child() INHERITS(test_inh_check);
|
||||||
|
ALTER TABLE test_inh_check ALTER COLUMN a TYPE numeric;
|
||||||
|
\d test_inh_check
|
||||||
|
\d test_inh_check_child
|
||||||
|
|
||||||
-- disallow recursive containment of row types
|
-- disallow recursive containment of row types
|
||||||
create temp table recur1 (f1 int);
|
create temp table recur1 (f1 int);
|
||||||
alter table recur1 add column f2 recur1; -- fails
|
alter table recur1 add column f2 recur1; -- fails
|
||||||
|
Reference in New Issue
Block a user