diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/tablecmds.c | 518 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 3 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 18 | ||||
-rw-r--r-- | src/include/nodes/parsenodes.h | 7 | ||||
-rw-r--r-- | src/test/regress/expected/alter_table.out | 50 | ||||
-rw-r--r-- | src/test/regress/sql/alter_table.sql | 34 |
6 files changed, 617 insertions, 13 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 27dbf545115..d22db6e45b1 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.188 2006/06/27 18:35:05 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.189 2006/07/02 01:58:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -159,8 +159,12 @@ typedef struct NewColumnValue static void truncate_check_rel(Relation rel); static List *MergeAttributes(List *schema, List *supers, bool istemp, List **supOids, List **supconstr, int *supOidCount); +static void MergeConstraintsIntoExisting(Relation rel, Relation relation); +static void MergeAttributesIntoExisting(Relation rel, Relation relation); static bool change_varattnos_walker(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); @@ -246,6 +250,8 @@ static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, 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, @@ -1195,10 +1201,8 @@ static void StoreCatalogInheritance(Oid relationId, List *supers) { Relation relation; - TupleDesc desc; int16 seqNumber; ListCell *entry; - HeapTuple tuple; /* * sanity checks @@ -1218,16 +1222,26 @@ StoreCatalogInheritance(Oid relationId, List *supers) * 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); + 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 */ @@ -1262,10 +1276,6 @@ StoreCatalogInheritance(Oid relationId, List *supers) */ setRelhassubclassInRelation(parentOid, true); - seqNumber += 1; - } - - heap_close(relation, RowExclusiveLock); } /* @@ -2092,6 +2102,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, 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 */ @@ -2272,6 +2284,12 @@ ATExecCmd(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd) 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); @@ -5922,6 +5940,488 @@ ATExecEnableDisableTrigger(Relation rel, char *trigname, 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)); +} + + +/* ALTER TABLE INHERIT */ + +/* Add a parent to the child's parents. This verifies that all the columns and + * check constraints of the parent appear in the child and that they have the + * same data type and expressions. + */ + +static void +ATExecAddInherits(Relation rel, RangeVar *parent) +{ + Relation relation, + catalogRelation; + SysScanDesc scan; + ScanKeyData key; + HeapTuple inheritsTuple; + int4 inhseqno; + List *children; + + + /* XXX is this enough locking? */ + relation = heap_openrv(parent, AccessShareLock); + + /* + * Must be owner of both parent and child -- child is taken care of by + * ATSimplePermissions call in ATPrepCmd + */ + ATSimplePermissions(relation, false); + + /* Permanent rels cannot inherit from temporary ones */ + if (!isTempNamespace(RelationGetNamespace(rel)) && + isTempNamespace(RelationGetNamespace(relation))) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit from temporary relation \"%s\"", + parent->relname))); + + /* 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. We scan through the list of + * parents in pg_inherit and keep track of the first open inhseqno slot + * found to use for the new parent. + */ + 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))); + if (inh->inhseqno == inhseqno + 1) + inhseqno = inh->inhseqno; + } + systable_endscan(scan); + heap_close(catalogRelation, RowExclusiveLock); + + /* + * If the new parent is found in our list of inheritors we have a circular + * structure + */ + + /* 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)))); + + + /* Match up the columns and bump attinhcount and attislocal */ + MergeAttributesIntoExisting(rel, relation); + + /* Match up the constraints and make sure they're present in child */ + MergeConstraintsIntoExisting(rel, relation); + + /* + * Use this refactored part of StoreCatalogInheritance which CREATE TABLE + * uses to add the pg_inherit line + */ + catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock); + StoreCatalogInheritance1(RelationGetRelid(rel), RelationGetRelid(relation), + inhseqno + 1, catalogRelation); + heap_close(catalogRelation, RowExclusiveLock); + + heap_close(relation, AccessShareLock); +} + +/* + * Check columns in child table match up with columns in parent + * + * Called by ATExecAddInherits + * + * Currently all columns must be found in child. Missing columns are an error. + * One day we might consider creating new columns like CREATE TABLE does. + * + * The data type must match perfectly, if the parent column is NOT NULL then + * the child table must be as well. Defaults are ignored however. + * + */ + +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); + /* XXX strength reduce open indexes to outside loop? */ + CatalogUpdateIndexes(attrdesc, tuple); + 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); + } + +} + +/* + * Check constraints in child table match up with constraints in parent + * + * Called by ATExecAddInherits + * + * Currently all constraints in parent must be present in the child. One day we + * may consider adding new constraints like CREATE TABLE does. We may also want + * to allow an optional flag on parent table constraints indicating they are + * intended to ONLY apply to the master table, not to the children. That would + * make it possible to ensure no records are mistakenly inserted into the + * master in partitioned tables rather than the appropriate child. + * + * XXX this is O(n^2) which may be issue with tables with hundreds of + * constraints. As long as tables have more like 10 constraints it shouldn't be + * an issue though. Even 100 constraints ought not be the end of the world. + */ + +static void +MergeConstraintsIntoExisting(Relation rel, Relation relation) +{ + Relation catalogRelation; + TupleDesc tupleDesc; + SysScanDesc scan; + ScanKeyData key; + HeapTuple constraintTuple; + ListCell *elem; + List *constraints; + + /* 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; + /* XXX Do I need the copytuple here? */ + constraints = lappend(constraints, heap_copytuple(constraintTuple)); + } + systable_endscan(scan); + + /* Then loop through the parent's constraints looking for them in the list */ + 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 = false; + Form_pg_constraint parent_con = (Form_pg_constraint) (GETSTRUCT(constraintTuple)); + Form_pg_constraint child_con = NULL; + HeapTuple child_contuple = NULL; + + if (parent_con->contype != CONSTRAINT_CHECK) + continue; + + foreach(elem, constraints) + { + child_contuple = lfirst(elem); + child_con = (Form_pg_constraint) (GETSTRUCT(child_contuple)); + if (!strcmp(NameStr(parent_con->conname), + NameStr(child_con->conname))) + { + found = true; + break; + } + } + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("child table missing constraint matching parent table constraint \"%s\"", + NameStr(parent_con->conname)))); + + 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)))); + + /* + * TODO: add conislocal,coninhcount to constraints. This is where we + * would have to bump them just like attributes + */ + } + systable_endscan(scan); + heap_close(catalogRelation, AccessShareLock); +} + +/* ALTER TABLE NO INHERIT */ + +/* Drop a parent from the child's parents. This just adjusts the attinhcount + * and attislocal of the columns and removes the pg_inherit and pg_depend + * entries. + * + * If attinhcount goes to 0 then attislocal gets set to true. If it goes back up + * attislocal stays 0 which means if a child is ever removed from a parent then + * its columns will never be automatically dropped which may surprise. But at + * least we'll never surprise by dropping columns someone isn't expecting to be + * dropped which would actually mean data loss. + */ + +static void +ATExecDropInherits(Relation rel, RangeVar *parent) +{ + + + Relation catalogRelation; + SysScanDesc scan; + ScanKeyData key[2]; + HeapTuple inheritsTuple, + attributeTuple, + depTuple; + Oid inhparent; + Oid dropparent; + int found = false; + + /* + * 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 = true; + } + } + systable_endscan(scan); + heap_close(catalogRelation, RowExclusiveLock); + + + if (!found) + { + 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) + 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 = true; + + simple_heap_update(catalogRelation, ©Tuple->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 * diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6cf580c136c..7277e539e8d 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.338 2006/07/01 18:38:32 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.339 2006/07/02 01:58:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1800,6 +1800,7 @@ _copyAlterTableCmd(AlterTableCmd *from) COPY_SCALAR_FIELD(subtype); COPY_STRING_FIELD(name); COPY_NODE_FIELD(def); + COPY_NODE_FIELD(parent); COPY_NODE_FIELD(transform); COPY_SCALAR_FIELD(behavior); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 246d1ef3a33..9ad722528e1 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.548 2006/06/27 03:43:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.549 2006/07/02 01:58:36 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1514,6 +1514,22 @@ alter_table_cmd: 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; diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 2fb77fd6859..f9c1524bc98 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.312 2006/06/27 03:43:20 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.313 2006/07/02 01:58:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -874,7 +874,9 @@ typedef enum AlterTableType AT_EnableTrigAll, /* ENABLE TRIGGER ALL */ AT_DisableTrigAll, /* DISABLE TRIGGER ALL */ AT_EnableTrigUser, /* ENABLE TRIGGER USER */ - AT_DisableTrigUser /* DISABLE 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 */ @@ -883,6 +885,7 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ 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 */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index b011f9e1eda..30fb47f776f 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -306,6 +306,56 @@ insert into atacc3 (test2) values (3); 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); diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 8690f61dbed..2ed67b3a1c6 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -336,6 +336,40 @@ 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; +-- 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); |