ADD/DROPS inherits

From: Greg Stark <gsstark(at)mit(dot)edu>
To: pgsql-patches(at)postgresql(dot)org
Subject: ADD/DROPS inherits
Date: 2006-06-12 21:39:08
Message-ID: 87slmadpfn.fsf@stark.xeocode.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches


I couldn't figure out how to make use of the predigested constraints in the
relcache, so I continued on the tack I was on. I just stuf the entire
HeapTuple in a list and decompiled the constraint source only if I find a
constraint with a matching name.

Points I'm uncertain about:

. I throw an elog() error if there's a null conbin for a CHECK constraint. Is
it possible for a valid CHECK constraint structure to have a null conbin?

. Memory management. Do I need the heap_copytuple or is that unnecessary?
Would it be ok to simply store the actual HeapTuples as the scan proceeds?

. Locking -- all of it :)

I added some basic (very basic) regression tests and documentation. Did I find
the right places? Is that enough or should I add more?

Index: doc/src/sgml/ref/alter_table.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v
retrieving revision 1.84
diff -u -p -c -r1.84 alter_table.sgml
cvs diff: conflicting specifications of output style
*** doc/src/sgml/ref/alter_table.sgml 12 Feb 2006 19:11:00 -0000 1.84
--- doc/src/sgml/ref/alter_table.sgml 12 Jun 2006 21:30:54 -0000
*************** where <replaceable class="PARAMETER">act
*** 46,51 ****
--- 46,53 ----
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
SET WITHOUT CLUSTER
SET WITHOUT OIDS
+ INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
+ NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
</synopsis>
*************** where <replaceable class="PARAMETER">act
*** 250,255 ****
--- 252,303 ----
</varlistentry>

<varlistentry>
+ <term><literal>INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+
+ This form adds a new parent table to the table. This won't add new
+ columns to the child table, instead all columns of the parent table must
+ already exist in the child table. They must have matching data types,
+ and if they have <literal>NOT NULL</literal> constraints in the parent
+ then they must also have <literal>NOT NULL</literal> constraints in the
+ child.
+
+ </para>
+ <para>
+
+ There must also be matching table constraints for all
+ <literal>CHECK</literal> table constraints of the parent. Currently
+ <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and
+ <literal>FOREIGN KEY</literal> constraints are ignored however this may
+ change in the future.
+
+ </para>
+ <para>
+
+ The easiest way to create a suitable table is to create a table using
+ <literal>INHERITS</literal> and then remove it via <literal>NO
+ INHERIT</literal>. Alternatively create a table using
+ <literal>LIKE</literal> however note that <literal>LIKE</literal> does
+ not create the necessary constraints.
+
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+ This form removes a parent table from the list of parents of the table.
+ Queries against the parent table will no longer include records drawn
+ from the target table.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>OWNER</literal></term>
<listitem>
<para>
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.184
diff -u -p -c -r1.184 tablecmds.c
cvs diff: conflicting specifications of output style
*** src/backend/commands/tablecmds.c 10 May 2006 23:18:39 -0000 1.184
--- src/backend/commands/tablecmds.c 12 Jun 2006 21:30:54 -0000
*************** typedef struct NewColumnValue
*** 159,166 ****
--- 159,168 ----
static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, bool istemp,
List **supOids, List **supconstr, int *supOidCount);
+ static void MergeAttributesIntoExisting(Relation rel, Relation relation);
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
static void StoreCatalogInheritance(Oid relationId, List *supers);
+ static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int16 seqNumber, Relation catalogRelation);
static int findAttrByName(const char *attributeName, List *schema);
static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
static bool needs_toast_table(Relation rel);
*************** static void ATPrepSetTableSpace(AlteredT
*** 246,251 ****
--- 248,255 ----
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace);
static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
bool enable, bool skip_system);
+ static void ATExecAddInherits(Relation rel, RangeVar *parent);
+ static void ATExecDropInherits(Relation rel, RangeVar *parent);
static void copy_relation_data(Relation rel, SMgrRelation dst);
static void update_ri_trigger_args(Oid relid,
const char *oldname,
*************** static void
*** 1156,1165 ****
StoreCatalogInheritance(Oid relationId, List *supers)
{
Relation relation;
- TupleDesc desc;
int16 seqNumber;
ListCell *entry;
- HeapTuple tuple;

/*
* sanity checks
--- 1160,1167 ----
*************** StoreCatalogInheritance(Oid relationId,
*** 1179,1194 ****
* anymore, there's no need to look for indirect ancestors.)
*/
relation = heap_open(InheritsRelationId, RowExclusiveLock);
- desc = RelationGetDescr(relation);

seqNumber = 1;
foreach(entry, supers)
{
! Oid parentOid = lfirst_oid(entry);
Datum datum[Natts_pg_inherits];
char nullarr[Natts_pg_inherits];
ObjectAddress childobject,
parentobject;

datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
datum[1] = ObjectIdGetDatum(parentOid); /* inhparent */
--- 1181,1206 ----
* anymore, there's no need to look for indirect ancestors.)
*/
relation = heap_open(InheritsRelationId, RowExclusiveLock);

seqNumber = 1;
foreach(entry, supers)
{
! StoreCatalogInheritance1(relationId, lfirst_oid(entry), seqNumber, relation);
! seqNumber += 1;
! }
!
! heap_close(relation, RowExclusiveLock);
! }
!
! static void
! StoreCatalogInheritance1(Oid relationId, Oid parentOid, int16 seqNumber, Relation relation)
! {
Datum datum[Natts_pg_inherits];
char nullarr[Natts_pg_inherits];
ObjectAddress childobject,
parentobject;
+ HeapTuple tuple;
+ TupleDesc desc = RelationGetDescr(relation);

datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
datum[1] = ObjectIdGetDatum(parentOid); /* inhparent */
*************** StoreCatalogInheritance(Oid relationId,
*** 1223,1232 ****
*/
setRelhassubclassInRelation(parentOid, true);

- seqNumber += 1;
- }
-
- heap_close(relation, RowExclusiveLock);
}

/*
--- 1235,1240 ----
*************** ATPrepCmd(List **wqueue, Relation rel, A
*** 2053,2058 ****
--- 2061,2068 ----
case AT_DisableTrig: /* DISABLE TRIGGER variants */
case AT_DisableTrigAll:
case AT_DisableTrigUser:
+ case AT_AddInherits:
+ case AT_DropInherits:
ATSimplePermissions(rel, false);
/* These commands never recurse */
/* No command-specific prep needed */
*************** ATExecCmd(AlteredTableInfo *tab, Relatio
*** 2233,2238 ****
--- 2243,2254 ----
case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
ATExecEnableDisableTrigger(rel, NULL, false, true);
break;
+ case AT_DropInherits:
+ ATExecDropInherits(rel, cmd->parent);
+ break;
+ case AT_AddInherits:
+ ATExecAddInherits(rel, cmd->parent);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
*************** ATExecEnableDisableTrigger(Relation rel,
*** 5880,5885 ****
--- 5896,6294 ----
EnableDisableTrigger(rel, trigname, enable, skip_system);
}

+ static char *
+ decompile_conbin(HeapTuple contuple, TupleDesc tupledesc)
+ {
+ Form_pg_constraint con = (Form_pg_constraint)(GETSTRUCT(contuple));
+ bool isnull;
+ Datum d;
+
+ d = fastgetattr(contuple, Anum_pg_constraint_conbin, tupledesc, &isnull);
+ if (isnull)
+ elog(ERROR, "conbin is null for constraint \"%s\"", NameStr(con->conname));
+ d = DirectFunctionCall2(pg_get_expr, d, ObjectIdGetDatum(con->conrelid));
+ return DatumGetCString(DirectFunctionCall1(textout,d));
+ }
+
+
+ static void
+ ATExecAddInherits(Relation rel, RangeVar *parent)
+ {
+ Relation relation, catalogRelation;
+ SysScanDesc scan;
+ ScanKeyData key;
+ HeapTuple inheritsTuple, constraintTuple;
+ int4 inhseqno;
+ ListCell *elem;
+ List *children, *constraints;
+ TupleDesc tupleDesc;
+
+ relation = heap_openrv(parent, AccessShareLock); /* XXX is this enough locking? */
+ if (relation->rd_rel->relkind != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("inherited relation \"%s\" is not a table",
+ parent->relname)));
+
+
+ /* Permanent rels cannot inherit from temporary ones */
+ if (!rel->rd_istemp && isTempNamespace(RelationGetNamespace(relation)))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot inherit from temporary relation \"%s\"",
+ parent->relname)));
+
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(relation));
+
+ /* If parent has OIDs then all children must have OIDs */
+ if (relation->rd_rel->relhasoids && !rel->rd_rel->relhasoids)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("table \"%s\" without OIDs cannot inherit from table \"%s\" with OIDs",
+ RelationGetRelationName(rel), parent->relname)));
+
+ /*
+ * Reject duplications in the list of parents. -- this is the same check as
+ * when creating a table, but maybe we should check for the parent anywhere
+ * higher in the inheritance structure?
+ */
+ catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
+ ScanKeyInit(&key,
+ Anum_pg_inherits_inhrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true, SnapshotNow, 1, &key);
+ inhseqno = 0; /* inhseqno sequences are supposed to start at 1 */
+ while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
+ {
+ Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
+ if (inh->inhparent == RelationGetRelid(relation))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("inherited relation \"%s\" duplicated",
+ parent->relname)));
+ /* Accumulate the max seqno -- BUT if we spot a hole use it instead
+ * This means if you DROP and ADD the same parent the order will be maintained
+ * which will mean pg_dump won't gratuitously reorder your columns.
+ */
+ if (inh->inhseqno == inhseqno+1)
+ inhseqno = inh->inhseqno;
+ }
+ systable_endscan(scan);
+ heap_close(catalogRelation, RowExclusiveLock);
+
+ /* Get children because we have to manually recurse and also because we
+ * have to check for recursive inheritance graphs */
+
+ /* this routine is actually in the planner */
+ children = find_all_inheritors(RelationGetRelid(rel));
+
+ if (list_member_oid(children, RelationGetRelid(relation)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("circular inheritance structure found, \"%s\" is already a child of \"%s\"",
+ parent->relname, RelationGetRelationName(rel))));
+
+ foreach(elem, children)
+ {
+ Oid childrelid = lfirst_oid(elem);
+ Relation childrel;
+
+ childrel = relation_open(childrelid, AccessExclusiveLock);
+ MergeAttributesIntoExisting(childrel, relation);
+ relation_close(childrel, NoLock);
+ }
+
+
+
+ /* Now check the constraints
+ *
+ * Currently only check constraints are copied when making children so only
+ * check those -- foreign key constraints can and probably ought to be
+ * added but unique/primary key are a whole other ball of wax
+ */
+
+ /* XXX this is O(n^2) which may be issue with tables with many constraints. As
+ * long as tables usually have more like 10 constraints it shouldn't be an
+ * issue. even 100 constraints shouldn't be the end of the world.
+ */
+
+ /* First gather up the child's constraint definitions */
+ catalogRelation = heap_open(ConstraintRelationId, AccessShareLock);
+ tupleDesc = RelationGetDescr(catalogRelation);
+
+ ScanKeyInit(&key,
+ Anum_pg_constraint_conrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, SnapshotNow, 1, &key);
+ constraints = NIL;
+ while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) {
+ Form_pg_constraint con = (Form_pg_constraint)(GETSTRUCT(constraintTuple));
+ if (con->contype != CONSTRAINT_CHECK)
+ continue;
+ constraints = lappend(constraints, heap_copytuple(constraintTuple)); /* XXX Do I need the copytuple here? */
+ }
+ systable_endscan(scan);
+
+ /* Then loop through the parent's constraints */
+ ScanKeyInit(&key,
+ Anum_pg_constraint_conrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(relation)));
+ scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, SnapshotNow, 1, &key);
+ while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) {
+ bool found = 0;
+ Form_pg_constraint parent_con = (Form_pg_constraint)(GETSTRUCT(constraintTuple));
+ if (parent_con->contype != CONSTRAINT_CHECK)
+ continue;
+
+ foreach(elem, constraints) {
+ HeapTuple child_contuple = lfirst(elem);
+ Form_pg_constraint child_con = (Form_pg_constraint)(GETSTRUCT(child_contuple));
+
+ if (strcmp(NameStr(parent_con->conname),
+ NameStr(child_con->conname)))
+ continue;
+
+ if (parent_con->condeferrable != child_con->condeferrable ||
+ parent_con->condeferred != child_con->condeferred ||
+ parent_con->contypid != child_con->contypid ||
+ strcmp(decompile_conbin(constraintTuple, tupleDesc),
+ decompile_conbin(child_contuple, tupleDesc))
+ )
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("constraint definition for CHECK constraint \"%s\" doesn't match",
+ NameStr(parent_con->conname))));
+
+ found = 1;
+ break;
+ }
+ if (!found)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table missing constraint matching parent table constraint \"%s\"",
+ NameStr(parent_con->conname))));
+
+ }
+ systable_endscan(scan);
+ heap_close(catalogRelation, AccessShareLock);
+
+ catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
+ StoreCatalogInheritance1(RelationGetRelid(rel), RelationGetRelid(relation), inhseqno+1, catalogRelation);
+ heap_close(catalogRelation, RowExclusiveLock);
+
+ heap_close(relation, AccessShareLock);
+ }
+
+ static void
+ MergeAttributesIntoExisting(Relation rel, Relation relation)
+ {
+ Relation attrdesc;
+ AttrNumber parent_attno, child_attno;
+ TupleDesc tupleDesc;
+ TupleConstr *constr;
+ HeapTuple tuple;
+
+ child_attno = RelationGetNumberOfAttributes(rel);
+
+ tupleDesc = RelationGetDescr(relation);
+ constr = tupleDesc->constr;
+
+ for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+ parent_attno++)
+ {
+ Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
+ char *attributeName = NameStr(attribute->attname);
+
+ /* Ignore dropped columns in the parent. */
+ if (attribute->attisdropped)
+ continue;
+
+ /* Does it conflict with an existing column? */
+ attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), attributeName);
+ if (HeapTupleIsValid(tuple)) {
+ /*
+ * Yes, try to merge the two column definitions. They must
+ * have the same type and typmod.
+ */
+ Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
+ if (attribute->atttypid != childatt->atttypid ||
+ attribute->atttypmod != childatt->atttypmod ||
+ (attribute->attnotnull && !childatt->attnotnull))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table \"%s\" has different type for column \"%s\"",
+ RelationGetRelationName(rel), NameStr(attribute->attname))));
+
+ childatt->attinhcount++;
+ simple_heap_update(attrdesc, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(attrdesc, tuple); /* XXX strength reduce openindexes to outside loop? */
+ heap_freetuple(tuple);
+
+ /* We don't touch default at all since we're not making any other
+ * DDL changes to the child */
+
+ } else {
+ /*
+ * No, create a new inherited column
+ *
+ * Creating inherited columns in this case seems to be unpopular.
+ * In the common use case of partitioned tables it's a foot-gun.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table missing column \"%s\"",
+ NameStr(attribute->attname))));
+ }
+ heap_close(attrdesc, RowExclusiveLock);
+ }
+
+ }
+
+ static void
+ ATExecDropInherits(Relation rel, RangeVar *parent)
+ {
+
+
+ Relation catalogRelation;
+ SysScanDesc scan;
+ ScanKeyData key[2];
+ HeapTuple inheritsTuple, attributeTuple, depTuple;
+ Oid inhparent;
+ Oid dropparent;
+ int found = 0;
+
+ /* Get the OID of parent -- if no schema is specified use the regular
+ * search path and only drop the one table that's found. We could try to be
+ * clever and look at each parent and see if it matches but that would be
+ * inconsistent with other operations I think. */
+
+ Assert(rel);
+ Assert(parent);
+
+ dropparent = RangeVarGetRelid(parent, false);
+
+ /* Search through the direct parents of rel looking for dropparent oid */
+
+ catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
+ ScanKeyInit(key,
+ Anum_pg_inherits_inhrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true, SnapshotNow, 1, key);
+ while (!found && HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
+ {
+ inhparent = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
+ if (inhparent == dropparent) {
+ simple_heap_delete(catalogRelation, &inheritsTuple->t_self);
+ found = 1;
+ }
+ }
+ systable_endscan(scan);
+ heap_close(catalogRelation, RowExclusiveLock);
+
+
+ if (!found) {
+ /* would it be better to look up the actual schema of dropparent and
+ * make the error message explicitly name the qualified name it's
+ * trying to drop ?*/
+ if (parent->schemaname)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s.%s\" is not a parent of relation \"%s\"",
+ parent->schemaname, parent->relname, RelationGetRelationName(rel))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" is not a parent of relation \"%s\"",
+ parent->relname, RelationGetRelationName(rel))));
+ }
+
+ /* Search through columns looking for matching columns from parent table */
+
+ catalogRelation = heap_open(AttributeRelationId, RowExclusiveLock);
+ ScanKeyInit(key,
+ Anum_pg_attribute_attrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId, true, SnapshotNow, 1, key);
+ while (HeapTupleIsValid(attributeTuple = systable_getnext(scan))) {
+ Form_pg_attribute att = ((Form_pg_attribute)GETSTRUCT(attributeTuple));
+ /* Not an inherited column at all
+ * (do NOT use islocal for this test--it can be true for inherited columns)
+ */
+ if (att->attinhcount == 0)
+ continue;
+ if (att->attisdropped) /* XXX Is this right? */
+ continue;
+ if (SearchSysCacheExistsAttName(dropparent, NameStr(att->attname))) {
+ /* Decrement inhcount and possibly set islocal to 1 */
+ HeapTuple copyTuple = heap_copytuple(attributeTuple);
+ Form_pg_attribute copy_att = ((Form_pg_attribute)GETSTRUCT(copyTuple));
+
+ copy_att->attinhcount--;
+ if (copy_att->attinhcount == 0)
+ copy_att->attislocal = 1;
+
+ simple_heap_update(catalogRelation, &copyTuple->t_self, copyTuple);
+ /* XXX "Avoid using it for multiple tuples, since opening the
+ * indexes and building the index info structures is moderately
+ * expensive." Perhaps this can be moved outside the loop or else
+ * at least the CatalogOpenIndexes/CatalogCloseIndexes moved
+ * outside the loop but when I try that it seg faults?!*/
+ CatalogUpdateIndexes(catalogRelation, copyTuple);
+ heap_freetuple(copyTuple);
+ }
+ }
+ systable_endscan(scan);
+ heap_close(catalogRelation, RowExclusiveLock);
+
+
+ /* Drop the dependency
+ *
+ * There's no convenient way to do this, so go trawling through pg_depend
+ */
+
+ catalogRelation = heap_open(DependRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_classid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ RelationRelationId);
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_objid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+
+ scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid(depTuple = systable_getnext(scan))) {
+ Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
+
+ if (dep->refclassid == RelationRelationId &&
+ dep->refobjid == dropparent &&
+ dep->deptype == DEPENDENCY_NORMAL) {
+
+ /* Only delete a single dependency -- there shouldn't be more but just in case... */
+ simple_heap_delete(catalogRelation, &depTuple->t_self);
+
+ break;
+ }
+ }
+ systable_endscan(scan);
+
+ heap_close(catalogRelation, RowExclusiveLock);
+ }
+
+
+
/*
* ALTER TABLE CREATE TOAST TABLE
*
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.335
diff -u -p -c -r1.335 copyfuncs.c
cvs diff: conflicting specifications of output style
*** src/backend/nodes/copyfuncs.c 30 Apr 2006 18:30:38 -0000 1.335
--- src/backend/nodes/copyfuncs.c 12 Jun 2006 21:30:54 -0000
*************** _copyAlterTableCmd(AlterTableCmd *from)
*** 1799,1804 ****
--- 1799,1805 ----
COPY_SCALAR_FIELD(subtype);
COPY_STRING_FIELD(name);
COPY_NODE_FIELD(def);
+ COPY_NODE_FIELD(parent);
COPY_NODE_FIELD(transform);
COPY_SCALAR_FIELD(behavior);

Index: src/backend/parser/gram.y
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.545
diff -u -p -c -r2.545 gram.y
cvs diff: conflicting specifications of output style
*** src/backend/parser/gram.y 27 May 2006 17:38:45 -0000 2.545
--- src/backend/parser/gram.y 12 Jun 2006 21:30:57 -0000
*************** alter_table_cmd:
*** 1514,1519 ****
--- 1514,1535 ----
n->subtype = AT_DisableTrigUser;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER INHERITS ADD <parent> */
+ | INHERIT qualified_name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddInherits;
+ n->parent = $2;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> alter INHERITS DROP <parent> */
+ | NO INHERIT qualified_name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropInherits;
+ n->parent = $3;
+ $$ = (Node *)n;
+ }
| alter_rel_cmd
{
$$ = $1;
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.310
diff -u -p -c -r1.310 parsenodes.h
cvs diff: conflicting specifications of output style
*** src/include/nodes/parsenodes.h 30 Apr 2006 18:30:40 -0000 1.310
--- src/include/nodes/parsenodes.h 12 Jun 2006 21:31:06 -0000
*************** typedef enum AlterTableType
*** 874,880 ****
AT_EnableTrigAll, /* ENABLE TRIGGER ALL */
AT_DisableTrigAll, /* DISABLE TRIGGER ALL */
AT_EnableTrigUser, /* ENABLE TRIGGER USER */
! AT_DisableTrigUser /* DISABLE TRIGGER USER */
} AlterTableType;

typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
--- 874,882 ----
AT_EnableTrigAll, /* ENABLE TRIGGER ALL */
AT_DisableTrigAll, /* DISABLE TRIGGER ALL */
AT_EnableTrigUser, /* ENABLE TRIGGER USER */
! AT_DisableTrigUser, /* DISABLE TRIGGER USER */
! AT_AddInherits, /* ADD INHERITS parent */
! AT_DropInherits /* DROP INHERITS parent */
} AlterTableType;

typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
*************** typedef struct AlterTableCmd /* one subc
*** 883,888 ****
--- 885,891 ----
AlterTableType subtype; /* Type of table alteration to apply */
char *name; /* column, constraint, or trigger to act on,
* or new owner or tablespace */
+ RangeVar *parent; /* Parent table for add/drop inherits */
Node *def; /* definition of new column, column type,
* index, or constraint */
Node *transform; /* transformation expr for ALTER TYPE */
Index: src/test/regress/expected/alter_table.out
===================================================================
RCS file: /projects/cvsroot/pgsql/src/test/regress/expected/alter_table.out,v
retrieving revision 1.94
diff -u -p -c -r1.94 alter_table.out
cvs diff: conflicting specifications of output style
*** src/test/regress/expected/alter_table.out 23 Mar 2006 00:19:30 -0000 1.94
--- src/test/regress/expected/alter_table.out 12 Jun 2006 21:31:12 -0000
*************** insert into atacc3 (test2) values (3);
*** 306,311 ****
--- 306,361 ----
drop table atacc3;
drop table atacc2;
drop table atacc1;
+ -- same things with one created with INHERIT
+ create table atacc1 (test int);
+ create table atacc2 (test2 int);
+ create table atacc3 (test3 int) inherits (atacc1, atacc2);
+ alter table atacc3 no inherit atacc2;
+ -- fail
+ alter table atacc3 no inherit atacc2;
+ ERROR: relation "atacc2" is not a parent of relation "atacc3"
+ -- make sure it really isn't a child
+ insert into atacc3 (test2) values (3);
+ select test2 from atacc2;
+ test2
+ -------
+ (0 rows)
+
+ -- fail due to missing constraint
+ alter table atacc2 add constraint foo check (test2>0);
+ alter table atacc3 inherit atacc2;
+ ERROR: child table missing constraint matching parent table constraint "foo"
+ -- fail due to missing column
+ alter table atacc3 rename test2 to testx;
+ alter table atacc3 inherit atacc2;
+ ERROR: child table missing column "test2"
+ -- fail due to mismatched data type
+ alter table atacc3 add test2 bool;
+ alter table atacc3 add inherit atacc2;
+ alter table atacc3 drop test2;
+ -- succeed
+ alter table atacc3 add test2 int;
+ update atacc3 set test2 = 4 where test2 is null;
+ alter table atacc3 add constraint foo check (test2>0);
+ alter table atacc3 inherit atacc2;
+ -- fail due to duplicates and circular inheritance
+ alter table atacc3 inherit atacc2;
+ ERROR: inherited relation "atacc2" duplicated
+ alter table atacc2 inherit atacc3;
+ ERROR: circular inheritance structure found, "atacc3" is already a child of "atacc2"
+ alter table atacc2 inherit atacc2;
+ ERROR: circular inheritance structure found, "atacc2" is already a child of "atacc2"
+ -- test that we really are a child now (should see 4 not 3 and cascade should go through)
+ select test2 from atacc2;
+ test2
+ -------
+ 4
+ (1 row)
+
+ drop table atacc2 cascade;
+ NOTICE: drop cascades to table atacc3
+ NOTICE: drop cascades to constraint foo on table atacc3
+ drop table atacc1;
-- let's try only to add only to the parent
create table atacc1 (test int);
create table atacc2 (test2 int);
Index: src/test/regress/sql/alter_table.sql
===================================================================
RCS file: /projects/cvsroot/pgsql/src/test/regress/sql/alter_table.sql,v
retrieving revision 1.54
diff -u -p -c -r1.54 alter_table.sql
cvs diff: conflicting specifications of output style
*** src/test/regress/sql/alter_table.sql 12 Feb 2006 19:11:01 -0000 1.54
--- src/test/regress/sql/alter_table.sql 12 Jun 2006 21:31:12 -0000
*************** drop table atacc3;
*** 336,341 ****
--- 336,375 ----
drop table atacc2;
drop table atacc1;

+ -- same things with one created with INHERIT
+ create table atacc1 (test int);
+ create table atacc2 (test2 int);
+ create table atacc3 (test3 int) inherits (atacc1, atacc2);
+ alter table atacc3 no inherit atacc2;
+ -- fail
+ alter table atacc3 no inherit atacc2;
+ -- make sure it really isn't a child
+ insert into atacc3 (test2) values (3);
+ select test2 from atacc2;
+ -- fail due to missing constraint
+ alter table atacc2 add constraint foo check (test2>0);
+ alter table atacc3 inherit atacc2;
+ -- fail due to missing column
+ alter table atacc3 rename test2 to testx;
+ alter table atacc3 inherit atacc2;
+ -- fail due to mismatched data type
+ alter table atacc3 add test2 bool;
+ alter table atacc3 add inherit atacc2;
+ alter table atacc3 drop test2;
+ -- succeed
+ alter table atacc3 add test2 int;
+ update atacc3 set test2 = 4 where test2 is null;
+ alter table atacc3 add constraint foo check (test2>0);
+ alter table atacc3 inherit atacc2;
+ -- fail due to duplicates and circular inheritance
+ alter table atacc3 inherit atacc2;
+ alter table atacc2 inherit atacc3;
+ alter table atacc2 inherit atacc2;
+ -- test that we really are a child now (should see 4 not 3 and cascade should go through)
+ select test2 from atacc2;
+ drop table atacc2 cascade;
+ drop table atacc1;
+
-- let's try only to add only to the parent

create table atacc1 (test int);

--
greg

Responses

Browse pgsql-patches by date

  From Date Subject
Next Message Simon Riggs 2006-06-12 22:15:19 Re: Non-transactional pg_class, try 2
Previous Message Alvaro Herrera 2006-06-12 19:05:07 Re: Non-transactional pg_class, try 2