mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04: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,7 +557,27 @@ 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> | ||||
|  | ||||
| @ -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