mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
Add ALTER TABLE ... ALTER CONSTRAINT ... SET [NO] INHERIT
This allows to redefine an existing non-inheritable constraint to be inheritable, which allows to straighten up situations with NO INHERIT constraints so that thay can become normal constraints without having to re-verify existing data. For existing inheritance children this may require creating additional constraints, if they don't exist already. It also allows to do the opposite, if only for symmetry. Author: Suraj Kharage <suraj.kharage@enterprisedb.com> Reviewed-by: jian he <jian.universality@gmail.com> Discussion: https://postgr.es/m/CAF1DzPVfOW6Kk=7SSh7LbneQDJWh=PbJrEC_Wkzc24tHOyQWGg@mail.gmail.com
This commit is contained in:
parent
f4694e0f35
commit
f4e53e10b6
@ -59,6 +59,7 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
|
||||
ADD <replaceable class="parameter">table_constraint</replaceable> [ NOT VALID ]
|
||||
ADD <replaceable class="parameter">table_constraint_using_index</replaceable>
|
||||
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
|
||||
ALTER CONSTRAINT <replaceable class="parameter">constraint_name</replaceable> SET [ INHERIT | NO INHERIT ]
|
||||
VALIDATE CONSTRAINT <replaceable class="parameter">constraint_name</replaceable>
|
||||
DROP CONSTRAINT [ IF EXISTS ] <replaceable class="parameter">constraint_name</replaceable> [ RESTRICT | CASCADE ]
|
||||
DISABLE TRIGGER [ <replaceable class="parameter">trigger_name</replaceable> | ALL | USER ]
|
||||
@ -556,11 +557,31 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
|
||||
<listitem>
|
||||
<para>
|
||||
This form alters the attributes of a constraint that was previously
|
||||
created. Currently only foreign key constraints may be altered.
|
||||
created. Currently only foreign key constraints may be altered in
|
||||
this fashion, but see below.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="sql-altertable-desc-alter-constraint-inherit">
|
||||
<term><literal>ALTER CONSTRAINT ... SET INHERIT</literal></term>
|
||||
<term><literal>ALTER CONSTRAINT ... SET NO INHERIT</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
These forms modify a inheritable constraint so that it becomes not
|
||||
inheritable, or vice-versa. Only not-null constraints may be altered
|
||||
in this fashion at present.
|
||||
In addition to changing the inheritability status of the constraint,
|
||||
in the case where a non-inheritable constraint is being marked
|
||||
inheritable, if the table has children, an equivalent constraint
|
||||
will be added to them. If marking an inheritable constraint as
|
||||
non-inheritable on a table with children, then the corresponding
|
||||
constraint on children will be marked as no longer inherited,
|
||||
but not removed.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry id="sql-altertable-desc-validate-constraint">
|
||||
<term><literal>VALIDATE CONSTRAINT</literal></term>
|
||||
<listitem>
|
||||
|
@ -389,9 +389,10 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel,
|
||||
static void AlterSeqNamespaces(Relation classRel, Relation rel,
|
||||
Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
|
||||
LOCKMODE lockmode);
|
||||
static ObjectAddress ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon,
|
||||
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
|
||||
ATAlterConstraint *cmdcon,
|
||||
bool recurse, LOCKMODE lockmode);
|
||||
static bool ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
|
||||
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
|
||||
Relation tgrel, Relation rel, HeapTuple contuple,
|
||||
bool recurse, List **otherrelids, LOCKMODE lockmode);
|
||||
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
|
||||
@ -5437,8 +5438,8 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
|
||||
lockmode);
|
||||
break;
|
||||
case AT_AlterConstraint: /* ALTER CONSTRAINT */
|
||||
address = ATExecAlterConstraint(rel, castNode(ATAlterConstraint,
|
||||
cmd->def),
|
||||
address = ATExecAlterConstraint(wqueue, rel,
|
||||
castNode(ATAlterConstraint, cmd->def),
|
||||
cmd->recurse, lockmode);
|
||||
break;
|
||||
case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
|
||||
@ -11813,14 +11814,14 @@ GetForeignKeyCheckTriggers(Relation trigrel,
|
||||
*
|
||||
* Update the attributes of a constraint.
|
||||
*
|
||||
* Currently only works for Foreign Key constraints.
|
||||
* Currently only works for Foreign Key and not null constraints.
|
||||
*
|
||||
* If the constraint is modified, returns its address; otherwise, return
|
||||
* InvalidObjectAddress.
|
||||
*/
|
||||
static ObjectAddress
|
||||
ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
|
||||
LOCKMODE lockmode)
|
||||
ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
|
||||
bool recurse, LOCKMODE lockmode)
|
||||
{
|
||||
Relation conrel;
|
||||
Relation tgrel;
|
||||
@ -11871,11 +11872,26 @@ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
|
||||
cmdcon->conname, RelationGetRelationName(rel))));
|
||||
|
||||
currcon = (Form_pg_constraint) GETSTRUCT(contuple);
|
||||
if (currcon->contype != CONSTRAINT_FOREIGN)
|
||||
if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
|
||||
cmdcon->conname, RelationGetRelationName(rel))));
|
||||
if (cmdcon->alterInheritability &&
|
||||
currcon->contype != CONSTRAINT_NOTNULL)
|
||||
ereport(ERROR,
|
||||
errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
|
||||
cmdcon->conname, RelationGetRelationName(rel)));
|
||||
|
||||
/* Refuse to modify inheritability of inherited constraints */
|
||||
if (cmdcon->alterInheritability &&
|
||||
cmdcon->noinherit && currcon->coninhcount > 0)
|
||||
ereport(ERROR,
|
||||
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
|
||||
NameStr(currcon->conname),
|
||||
RelationGetRelationName(rel)));
|
||||
|
||||
/*
|
||||
* If it's not the topmost constraint, raise an error.
|
||||
@ -11926,8 +11942,8 @@ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
|
||||
/*
|
||||
* Do the actual catalog work, and recurse if necessary.
|
||||
*/
|
||||
if (ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, rel, contuple,
|
||||
recurse, &otherrelids, lockmode))
|
||||
if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
|
||||
contuple, recurse, &otherrelids, lockmode))
|
||||
ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
|
||||
|
||||
/*
|
||||
@ -11958,9 +11974,10 @@ ATExecAlterConstraint(Relation rel, ATAlterConstraint *cmdcon, bool recurse,
|
||||
* but existing releases don't do that.)
|
||||
*/
|
||||
static bool
|
||||
ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
|
||||
Relation tgrel, Relation rel, HeapTuple contuple,
|
||||
bool recurse, List **otherrelids, LOCKMODE lockmode)
|
||||
ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
|
||||
Relation conrel, Relation tgrel, Relation rel,
|
||||
HeapTuple contuple, bool recurse,
|
||||
List **otherrelids, LOCKMODE lockmode)
|
||||
{
|
||||
Form_pg_constraint currcon;
|
||||
Oid refrelid = InvalidOid;
|
||||
@ -12040,14 +12057,82 @@ ATExecAlterConstraintInternal(ATAlterConstraint *cmdcon, Relation conrel,
|
||||
Relation childrel;
|
||||
|
||||
childrel = table_open(childcon->conrelid, lockmode);
|
||||
ATExecAlterConstraintInternal(cmdcon, conrel, tgrel, childrel, childtup,
|
||||
recurse, otherrelids, lockmode);
|
||||
ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, childrel,
|
||||
childtup, recurse, otherrelids, lockmode);
|
||||
table_close(childrel, NoLock);
|
||||
}
|
||||
|
||||
systable_endscan(pscan);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the catalog for inheritability. No work if the constraint is
|
||||
* already in the requested state.
|
||||
*/
|
||||
if (cmdcon->alterInheritability &&
|
||||
(cmdcon->noinherit != currcon->connoinherit))
|
||||
{
|
||||
AttrNumber colNum;
|
||||
char *colName;
|
||||
List *children;
|
||||
HeapTuple copyTuple;
|
||||
Form_pg_constraint copy_con;
|
||||
|
||||
/* The current implementation only works for NOT NULL constraints */
|
||||
Assert(currcon->contype == CONSTRAINT_NOTNULL);
|
||||
|
||||
copyTuple = heap_copytuple(contuple);
|
||||
copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
|
||||
copy_con->connoinherit = cmdcon->noinherit;
|
||||
|
||||
CatalogTupleUpdate(conrel, ©Tuple->t_self, copyTuple);
|
||||
CommandCounterIncrement();
|
||||
heap_freetuple(copyTuple);
|
||||
changed = true;
|
||||
|
||||
/* Fetch the column number and name */
|
||||
colNum = extractNotNullColumn(contuple);
|
||||
colName = get_attname(currcon->conrelid, colNum, false);
|
||||
|
||||
/*
|
||||
* Propagate the change to children. For SET NO INHERIT, we don't
|
||||
* recursively affect children, just the immediate level.
|
||||
*/
|
||||
children = find_inheritance_children(RelationGetRelid(rel),
|
||||
lockmode);
|
||||
foreach_oid(childoid, children)
|
||||
{
|
||||
ObjectAddress addr;
|
||||
|
||||
if (cmdcon->noinherit)
|
||||
{
|
||||
HeapTuple childtup;
|
||||
Form_pg_constraint childcon;
|
||||
|
||||
childtup = findNotNullConstraint(childoid, colName);
|
||||
if (!childtup)
|
||||
elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
|
||||
colName, childoid);
|
||||
childcon = (Form_pg_constraint) GETSTRUCT(childtup);
|
||||
Assert(childcon->coninhcount > 0);
|
||||
childcon->coninhcount--;
|
||||
childcon->conislocal = true;
|
||||
CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
|
||||
heap_freetuple(childtup);
|
||||
}
|
||||
else
|
||||
{
|
||||
Relation childrel = table_open(childoid, NoLock);
|
||||
|
||||
addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
|
||||
colName, true, true, lockmode);
|
||||
if (OidIsValid(addr.objectId))
|
||||
CommandCounterIncrement();
|
||||
table_close(childrel, NoLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -2669,6 +2669,34 @@ alter_table_cmd:
|
||||
NULL, NULL, NULL, yyscanner);
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
/* ALTER TABLE <name> ALTER CONSTRAINT SET INHERIT */
|
||||
| ALTER CONSTRAINT name SET INHERIT
|
||||
{
|
||||
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||
ATAlterConstraint *c = makeNode(ATAlterConstraint);
|
||||
|
||||
n->subtype = AT_AlterConstraint;
|
||||
n->def = (Node *) c;
|
||||
c->conname = $3;
|
||||
c->alterInheritability = true;
|
||||
c->noinherit = false;
|
||||
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
/* ALTER TABLE <name> ALTER CONSTRAINT SET NO INHERIT */
|
||||
| ALTER CONSTRAINT name SET NO INHERIT
|
||||
{
|
||||
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||
ATAlterConstraint *c = makeNode(ATAlterConstraint);
|
||||
|
||||
n->subtype = AT_AlterConstraint;
|
||||
n->def = (Node *) c;
|
||||
c->conname = $3;
|
||||
c->alterInheritability = true;
|
||||
c->noinherit = true;
|
||||
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
/* ALTER TABLE <name> VALIDATE CONSTRAINT ... */
|
||||
| VALIDATE CONSTRAINT name
|
||||
{
|
||||
|
@ -2493,6 +2493,8 @@ typedef struct ATAlterConstraint
|
||||
bool alterDeferrability; /* changing deferrability properties? */
|
||||
bool deferrable; /* DEFERRABLE? */
|
||||
bool initdeferred; /* INITIALLY DEFERRED? */
|
||||
bool alterInheritability; /* changing inheritability properties */
|
||||
bool noinherit;
|
||||
} ATAlterConstraint;
|
||||
|
||||
/* Ad-hoc node for AT_ReplicaIdentity */
|
||||
|
@ -2505,8 +2505,10 @@ CREATE TABLE inh_nn3 (a int not null, b int, not null a no inherit);
|
||||
ERROR: conflicting NO INHERIT declaration for not-null constraint on column "a"
|
||||
CREATE TABLE inh_nn4 (a int not null no inherit, b int, not null a);
|
||||
ERROR: conflicting NO INHERIT declaration for not-null constraint on column "a"
|
||||
DROP TABLE inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
ERROR: table "inh_nn2" does not exist
|
||||
DROP TABLE IF EXISTS inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
NOTICE: table "inh_nn2" does not exist, skipping
|
||||
NOTICE: table "inh_nn3" does not exist, skipping
|
||||
NOTICE: table "inh_nn4" does not exist, skipping
|
||||
--
|
||||
-- test inherit/deinherit
|
||||
--
|
||||
@ -2744,6 +2746,131 @@ NOTICE: drop cascades to 2 other objects
|
||||
DETAIL: drop cascades to table inh_multiparent
|
||||
drop cascades to table inh_multiparent2
|
||||
--
|
||||
-- Test ALTER CONSTRAINT SET [NO] INHERIT
|
||||
--
|
||||
create table inh_nn1 (f1 int not null no inherit);
|
||||
create table inh_nn2 (f2 text, f3 int, f1 int);
|
||||
alter table inh_nn2 inherit inh_nn1;
|
||||
create table inh_nn3 (f4 float) inherits (inh_nn2);
|
||||
create table inh_nn4 (f5 int, f4 float, f2 text, f3 int, f1 int);
|
||||
alter table inh_nn4 inherit inh_nn2, inherit inh_nn1, inherit inh_nn3;
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit;
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
conrelid | conname | conkey | coninhcount | conislocal | connoinherit
|
||||
----------+---------------------+--------+-------------+------------+--------------
|
||||
inh_nn1 | inh_nn1_f1_not_null | {1} | 0 | t | f
|
||||
inh_nn2 | inh_nn1_f1_not_null | {3} | 1 | f | f
|
||||
inh_nn3 | inh_nn1_f1_not_null | {3} | 1 | f | f
|
||||
inh_nn4 | inh_nn1_f1_not_null | {5} | 3 | f | f
|
||||
(4 rows)
|
||||
|
||||
-- ALTER CONSTRAINT SET NO INHERIT should work on top-level constraints
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set no inherit;
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
conrelid | conname | conkey | coninhcount | conislocal | connoinherit
|
||||
----------+---------------------+--------+-------------+------------+--------------
|
||||
inh_nn1 | inh_nn1_f1_not_null | {1} | 0 | t | t
|
||||
inh_nn2 | inh_nn1_f1_not_null | {3} | 0 | t | f
|
||||
inh_nn3 | inh_nn1_f1_not_null | {3} | 1 | f | f
|
||||
inh_nn4 | inh_nn1_f1_not_null | {5} | 2 | t | f
|
||||
(4 rows)
|
||||
|
||||
-- A constraint that's NO INHERIT can be dropped without damaging children
|
||||
alter table inh_nn1 drop constraint inh_nn1_f1_not_null;
|
||||
select conrelid::regclass, conname, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
conrelid | conname | coninhcount | conislocal | connoinherit
|
||||
----------+---------------------+-------------+------------+--------------
|
||||
inh_nn2 | inh_nn1_f1_not_null | 0 | t | f
|
||||
inh_nn3 | inh_nn1_f1_not_null | 1 | f | f
|
||||
inh_nn4 | inh_nn1_f1_not_null | 2 | t | f
|
||||
(3 rows)
|
||||
|
||||
drop table inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
-- Test inherit constraint and make sure it validates.
|
||||
create table inh_nn1 (f1 int not null no inherit);
|
||||
create table inh_nn2 (f2 text, f3 int) inherits (inh_nn1);
|
||||
insert into inh_nn2 values(NULL, 'sample', 1);
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit;
|
||||
ERROR: column "f1" of relation "inh_nn2" contains null values
|
||||
delete from inh_nn2;
|
||||
create table inh_nn3 () inherits (inh_nn2);
|
||||
create table inh_nn4 () inherits (inh_nn1, inh_nn2);
|
||||
NOTICE: merging multiple inherited definitions of column "f1"
|
||||
alter table inh_nn1 -- test multicommand alter table while at it
|
||||
alter constraint inh_nn1_f1_not_null set inherit,
|
||||
alter constraint inh_nn1_f1_not_null set no inherit;
|
||||
select conrelid::regclass, conname, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
conrelid | conname | coninhcount | conislocal | connoinherit
|
||||
----------+---------------------+-------------+------------+--------------
|
||||
inh_nn1 | inh_nn1_f1_not_null | 0 | t | t
|
||||
inh_nn2 | inh_nn1_f1_not_null | 0 | t | f
|
||||
inh_nn3 | inh_nn1_f1_not_null | 1 | f | f
|
||||
inh_nn4 | inh_nn1_f1_not_null | 1 | t | f
|
||||
(4 rows)
|
||||
|
||||
drop table inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
-- Test not null inherit constraint which already exists on child table.
|
||||
create table inh_nn1 (f1 int not null no inherit);
|
||||
create table inh_nn2 (f2 text, f3 int) inherits (inh_nn1);
|
||||
create table inh_nn3 (f4 float, constraint nn3_f1 not null f1 no inherit) inherits (inh_nn1, inh_nn2);
|
||||
NOTICE: merging multiple inherited definitions of column "f1"
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3')
|
||||
order by 2, 1;
|
||||
conrelid | conname | conkey | coninhcount | conislocal | connoinherit
|
||||
----------+---------------------+--------+-------------+------------+--------------
|
||||
inh_nn1 | inh_nn1_f1_not_null | {1} | 0 | t | t
|
||||
inh_nn3 | nn3_f1 | {1} | 0 | t | t
|
||||
(2 rows)
|
||||
|
||||
-- error: inh_nn3 has an incompatible NO INHERIT constraint
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit;
|
||||
ERROR: cannot change NO INHERIT status of NOT NULL constraint "nn3_f1" on relation "inh_nn3"
|
||||
alter table inh_nn3 alter constraint nn3_f1 set inherit;
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit; -- now it works
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3')
|
||||
order by 2, 1;
|
||||
conrelid | conname | conkey | coninhcount | conislocal | connoinherit
|
||||
----------+---------------------+--------+-------------+------------+--------------
|
||||
inh_nn1 | inh_nn1_f1_not_null | {1} | 0 | t | f
|
||||
inh_nn2 | inh_nn1_f1_not_null | {1} | 1 | f | f
|
||||
inh_nn3 | nn3_f1 | {1} | 2 | t | f
|
||||
(3 rows)
|
||||
|
||||
drop table inh_nn1, inh_nn2, inh_nn3;
|
||||
-- Negative scenarios for alter constraint .. set inherit.
|
||||
create table inh_nn1 (f1 int check(f1 > 5) primary key references inh_nn1, f2 int not null);
|
||||
-- constraints other than not-null are not supported
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_check set inherit;
|
||||
ERROR: constraint "inh_nn1_f1_check" of relation "inh_nn1" is not a not-null constraint
|
||||
alter table inh_nn1 alter constraint inh_nn1_pkey set inherit;
|
||||
ERROR: constraint "inh_nn1_pkey" of relation "inh_nn1" is not a not-null constraint
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_fkey set inherit;
|
||||
ERROR: constraint "inh_nn1_f1_fkey" of relation "inh_nn1" is not a not-null constraint
|
||||
-- try to drop a nonexistant constraint
|
||||
alter table inh_nn1 alter constraint foo set inherit;
|
||||
ERROR: constraint "foo" of relation "inh_nn1" does not exist
|
||||
-- Can't modify inheritability of inherited constraints
|
||||
create table inh_nn2 () inherits (inh_nn1);
|
||||
alter table inh_nn2 alter constraint inh_nn1_f2_not_null set no inherit;
|
||||
ERROR: cannot alter inherited constraint "inh_nn1_f2_not_null" on relation "inh_nn2"
|
||||
drop table inh_nn1, inh_nn2;
|
||||
--
|
||||
-- Mixed ownership inheritance tree
|
||||
--
|
||||
create role regress_alice;
|
||||
|
@ -957,7 +957,7 @@ CREATE TABLE inh_nn1 (a int not null);
|
||||
CREATE TABLE inh_nn2 (a int not null no inherit) INHERITS (inh_nn1);
|
||||
CREATE TABLE inh_nn3 (a int not null, b int, not null a no inherit);
|
||||
CREATE TABLE inh_nn4 (a int not null no inherit, b int, not null a);
|
||||
DROP TABLE inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
DROP TABLE IF EXISTS inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
|
||||
--
|
||||
-- test inherit/deinherit
|
||||
@ -1090,6 +1090,83 @@ select conrelid::regclass, contype, conname,
|
||||
|
||||
drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
|
||||
|
||||
--
|
||||
-- Test ALTER CONSTRAINT SET [NO] INHERIT
|
||||
--
|
||||
create table inh_nn1 (f1 int not null no inherit);
|
||||
create table inh_nn2 (f2 text, f3 int, f1 int);
|
||||
alter table inh_nn2 inherit inh_nn1;
|
||||
create table inh_nn3 (f4 float) inherits (inh_nn2);
|
||||
create table inh_nn4 (f5 int, f4 float, f2 text, f3 int, f1 int);
|
||||
alter table inh_nn4 inherit inh_nn2, inherit inh_nn1, inherit inh_nn3;
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit;
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
-- ALTER CONSTRAINT SET NO INHERIT should work on top-level constraints
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set no inherit;
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
-- A constraint that's NO INHERIT can be dropped without damaging children
|
||||
alter table inh_nn1 drop constraint inh_nn1_f1_not_null;
|
||||
select conrelid::regclass, conname, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
drop table inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
|
||||
-- Test inherit constraint and make sure it validates.
|
||||
create table inh_nn1 (f1 int not null no inherit);
|
||||
create table inh_nn2 (f2 text, f3 int) inherits (inh_nn1);
|
||||
insert into inh_nn2 values(NULL, 'sample', 1);
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit;
|
||||
delete from inh_nn2;
|
||||
create table inh_nn3 () inherits (inh_nn2);
|
||||
create table inh_nn4 () inherits (inh_nn1, inh_nn2);
|
||||
alter table inh_nn1 -- test multicommand alter table while at it
|
||||
alter constraint inh_nn1_f1_not_null set inherit,
|
||||
alter constraint inh_nn1_f1_not_null set no inherit;
|
||||
select conrelid::regclass, conname, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3', 'inh_nn4')
|
||||
order by 2, 1;
|
||||
drop table inh_nn1, inh_nn2, inh_nn3, inh_nn4;
|
||||
|
||||
-- Test not null inherit constraint which already exists on child table.
|
||||
create table inh_nn1 (f1 int not null no inherit);
|
||||
create table inh_nn2 (f2 text, f3 int) inherits (inh_nn1);
|
||||
create table inh_nn3 (f4 float, constraint nn3_f1 not null f1 no inherit) inherits (inh_nn1, inh_nn2);
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3')
|
||||
order by 2, 1;
|
||||
-- error: inh_nn3 has an incompatible NO INHERIT constraint
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit;
|
||||
alter table inh_nn3 alter constraint nn3_f1 set inherit;
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_not_null set inherit; -- now it works
|
||||
select conrelid::regclass, conname, conkey, coninhcount, conislocal, connoinherit
|
||||
from pg_constraint where contype = 'n' and
|
||||
conrelid::regclass::text in ('inh_nn1', 'inh_nn2', 'inh_nn3')
|
||||
order by 2, 1;
|
||||
drop table inh_nn1, inh_nn2, inh_nn3;
|
||||
|
||||
-- Negative scenarios for alter constraint .. set inherit.
|
||||
create table inh_nn1 (f1 int check(f1 > 5) primary key references inh_nn1, f2 int not null);
|
||||
-- constraints other than not-null are not supported
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_check set inherit;
|
||||
alter table inh_nn1 alter constraint inh_nn1_pkey set inherit;
|
||||
alter table inh_nn1 alter constraint inh_nn1_f1_fkey set inherit;
|
||||
-- try to drop a nonexistant constraint
|
||||
alter table inh_nn1 alter constraint foo set inherit;
|
||||
-- Can't modify inheritability of inherited constraints
|
||||
create table inh_nn2 () inherits (inh_nn1);
|
||||
alter table inh_nn2 alter constraint inh_nn1_f2_not_null set no inherit;
|
||||
|
||||
drop table inh_nn1, inh_nn2;
|
||||
|
||||
--
|
||||
-- Mixed ownership inheritance tree
|
||||
--
|
||||
|
Loading…
x
Reference in New Issue
Block a user