Upgrade ALTER TABLE DROP COLUMN so that it can drop an OID column, and

remove separate implementation of ALTER TABLE SET WITHOUT OIDS in favor
of doing a regular DROP.  Also, cause CREATE TABLE to account completely
correctly for the inheritance status of the OID column.  This fixes
problems with dropping OID columns that have dependencies, as noted by
Christopher Kings-Lynne, as well as making sure that you can't drop an
OID column that was inherited from a parent.
This commit is contained in:
Tom Lane 2004-03-23 19:35:17 +00:00
parent 446b5476e5
commit 24614a9880
11 changed files with 151 additions and 167 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.66 2004/03/09 16:57:47 neilc Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.67 2004/03/23 19:35:15 tgl Exp $
PostgreSQL documentation
-->
@ -150,12 +150,11 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
<term><literal>SET WITHOUT OIDS</literal></term>
<listitem>
<para>
This form removes the <literal>oid</literal> column from the
table. Removing OIDs from a table does not occur immediately.
The space that the OID uses will be reclaimed when the row is
updated. Without updating the row, both the space and the value
of the OID are kept indefinitely. This is semantically similar
to the <literal>DROP COLUMN</literal> process.
This form removes the <literal>oid</literal> system column from the
table. This is exactly equivalent to
<literal>DROP COLUMN oid RESTRICT</literal>,
except that it will not complain if there is already no
<literal>oid</literal> column.
</para>
<para>

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.64 2004/01/07 18:56:25 neilc Exp $
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.65 2004/03/23 19:35:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -197,6 +197,8 @@ Boot_CreateStmt:
tupdesc,
RELKIND_RELATION,
$3,
true,
0,
ONCOMMIT_NOOP,
true);
elog(DEBUG4, "relation created with oid %u", id);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.260 2004/02/15 21:01:39 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.261 2004/03/23 19:35:16 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -458,7 +458,9 @@ CheckAttributeType(const char *attname, Oid atttypid)
static void
AddNewAttributeTuples(Oid new_rel_oid,
TupleDesc tupdesc,
char relkind)
char relkind,
bool oidislocal,
int oidinhcount)
{
Form_pg_attribute *dpp;
int i;
@ -531,11 +533,18 @@ AddNewAttributeTuples(Oid new_rel_oid,
false,
ATTRIBUTE_TUPLE_SIZE,
(void *) *dpp);
attStruct = (Form_pg_attribute) GETSTRUCT(tup);
/* Fill in the correct relation OID in the copied tuple */
attStruct = (Form_pg_attribute) GETSTRUCT(tup);
attStruct->attrelid = new_rel_oid;
/* Fill in correct inheritance info for the OID column */
if (attStruct->attnum == ObjectIdAttributeNumber)
{
attStruct->attislocal = oidislocal;
attStruct->attinhcount = oidinhcount;
}
/*
* Unneeded since they should be OK in the constant data
* anyway
@ -713,6 +722,8 @@ heap_create_with_catalog(const char *relname,
TupleDesc tupdesc,
char relkind,
bool shared_relation,
bool oidislocal,
int oidinhcount,
OnCommitAction oncommit,
bool allow_system_table_mods)
{
@ -786,7 +797,8 @@ heap_create_with_catalog(const char *relname,
* now add tuples to pg_attribute for the attributes in our new
* relation.
*/
AddNewAttributeTuples(new_rel_oid, new_rel_desc->rd_att, relkind);
AddNewAttributeTuples(new_rel_oid, new_rel_desc->rd_att, relkind,
oidislocal, oidinhcount);
/*
* make a dependency link to force the relation to be deleted if its
@ -973,35 +985,46 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
attnum, relid);
attStruct = (Form_pg_attribute) GETSTRUCT(tuple);
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
if (attnum < 0)
{
/* System attribute (probably OID) ... just delete the row */
/*
* Set the type OID to invalid. A dropped attribute's type link
* cannot be relied on (once the attribute is dropped, the type might
* be too). Fortunately we do not need the type row --- the only
* really essential information is the type's typlen and typalign,
* which are preserved in the attribute's attlen and attalign. We set
* atttypid to zero here as a means of catching code that incorrectly
* expects it to be valid.
*/
attStruct->atttypid = InvalidOid;
simple_heap_delete(attr_rel, &tuple->t_self);
}
else
{
/* Dropping user attributes is lots harder */
/* Remove any NOT NULL constraint the column may have */
attStruct->attnotnull = false;
/* Mark the attribute as dropped */
attStruct->attisdropped = true;
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
/*
* Set the type OID to invalid. A dropped attribute's type link
* cannot be relied on (once the attribute is dropped, the type might
* be too). Fortunately we do not need the type row --- the only
* really essential information is the type's typlen and typalign,
* which are preserved in the attribute's attlen and attalign. We set
* atttypid to zero here as a means of catching code that incorrectly
* expects it to be valid.
*/
attStruct->atttypid = InvalidOid;
/* Change the column name to something that isn't likely to conflict */
snprintf(newattname, sizeof(newattname),
"........pg.dropped.%d........", attnum);
namestrcpy(&(attStruct->attname), newattname);
/* Remove any NOT NULL constraint the column may have */
attStruct->attnotnull = false;
simple_heap_update(attr_rel, &tuple->t_self, tuple);
/* We don't want to keep stats for it anymore */
attStruct->attstattarget = 0;
/* keep the system catalog indexes current */
CatalogUpdateIndexes(attr_rel, tuple);
/* Change the column name to something that isn't likely to conflict */
snprintf(newattname, sizeof(newattname),
"........pg.dropped.%d........", attnum);
namestrcpy(&(attStruct->attname), newattname);
simple_heap_update(attr_rel, &tuple->t_self, tuple);
/* keep the system catalog indexes current */
CatalogUpdateIndexes(attr_rel, tuple);
}
/*
* Because updating the pg_attribute row will trigger a relcache flush
@ -1011,7 +1034,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
heap_close(attr_rel, RowExclusiveLock);
RemoveStatistics(rel, attnum);
if (attnum > 0)
RemoveStatistics(rel, attnum);
relation_close(rel, NoLock);
}

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.119 2003/11/29 19:51:47 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.120 2004/03/23 19:35:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -501,6 +501,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName)
tupdesc,
OldHeap->rd_rel->relkind,
OldHeap->rd_rel->relisshared,
true,
0,
ONCOMMIT_NOOP,
allowSystemTableMods);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.100 2004/03/13 22:09:13 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.101 2004/03/23 19:35:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -77,7 +77,7 @@ static List *on_commits = NIL;
static List *MergeAttributes(List *schema, List *supers, bool istemp,
List **supOids, List **supconstr, bool *supHasOids);
List **supOids, List **supconstr, int *supOidCount);
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
static void StoreCatalogInheritance(Oid relationId, List *supers);
static int findAttrByName(const char *attributeName, List *schema);
@ -133,7 +133,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
TupleDesc descriptor;
List *inheritOids;
List *old_constraints;
bool parentHasOids;
bool localHasOids;
int parentOidCount;
List *rawDefaults;
List *listptr;
int i;
@ -177,7 +178,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
*/
schema = MergeAttributes(schema, stmt->inhRelations,
stmt->relation->istemp,
&inheritOids, &old_constraints, &parentHasOids);
&inheritOids, &old_constraints, &parentOidCount);
/*
* Create a relation descriptor from the relation schema and create
@ -188,10 +189,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
*/
descriptor = BuildDescForRelation(schema);
if (parentHasOids)
descriptor->tdhasoid = true;
else
descriptor->tdhasoid = interpretOidsOption(stmt->hasoids);
localHasOids = interpretOidsOption(stmt->hasoids);
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
if (old_constraints != NIL)
{
@ -255,6 +254,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
descriptor,
relkind,
false,
localHasOids,
parentOidCount,
stmt->oncommit,
allowSystemTableMods);
@ -436,7 +437,7 @@ TruncateRelation(const RangeVar *relation)
* 'supOids' receives a list of the OIDs of the parent relations.
* 'supconstr' receives a list of constraints belonging to the parents,
* updated as necessary to be valid for the child.
* 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE.
* 'supOidCount' is set to the number of parents that have OID columns.
*
* Return value:
* Completed schema list.
@ -482,13 +483,13 @@ TruncateRelation(const RangeVar *relation)
*/
static List *
MergeAttributes(List *schema, List *supers, bool istemp,
List **supOids, List **supconstr, bool *supHasOids)
List **supOids, List **supconstr, int *supOidCount)
{
List *entry;
List *inhSchema = NIL;
List *parentOids = NIL;
List *constraints = NIL;
bool parentHasOids = false;
int parentsWithOids = 0;
bool have_bogus_defaults = false;
char *bogus_marker = "Bogus!"; /* marks conflicting
* defaults */
@ -566,7 +567,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
parentOids = lappendo(parentOids, RelationGetRelid(relation));
parentHasOids |= relation->rd_rel->relhasoids;
if (relation->rd_rel->relhasoids)
parentsWithOids++;
tupleDesc = RelationGetDescr(relation);
constr = tupleDesc->constr;
@ -825,7 +827,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
*supOids = parentOids;
*supconstr = constraints;
*supHasOids = parentHasOids;
*supOidCount = parentsWithOids;
return schema;
}
@ -2422,92 +2424,30 @@ AlterTableAlterColumnFlags(Oid myrelid, bool recurse,
}
/*
* ALTER TABLE SET {WITHOUT} OIDS
* ALTER TABLE SET WITH/WITHOUT OIDS
*/
void
AlterTableAlterOids(Oid myrelid, bool recurse, bool setOid)
AlterTableAlterOids(Oid myrelid, bool setOid, bool recurse,
DropBehavior behavior)
{
Relation rel;
Relation class_rel;
HeapTuple tuple;
Form_pg_class tuple_class;
rel = heap_open(myrelid, AccessExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
RelationGetRelationName(rel))));
/* Permissions checks */
if (!pg_class_ownercheck(myrelid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(rel));
if (!allowSystemTableMods && IsSystemRelation(rel))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied: \"%s\" is a system catalog",
RelationGetRelationName(rel))));
/*
* Propagate to children if desired
*/
if (recurse)
{
List *child,
*children;
/* this routine is actually in the planner */
children = find_all_inheritors(myrelid);
/*
* find_all_inheritors does the recursive search of the
* inheritance hierarchy, so all we have to do is process all of
* the relids in the list that it returns.
*/
foreach(child, children)
{
Oid childrelid = lfirsti(child);
if (childrelid == myrelid)
continue;
AlterTableAlterOids(childrelid, false, setOid);
}
}
/* Do the thing on this relation */
class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(myrelid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", myrelid);
tuple_class = (Form_pg_class) GETSTRUCT(tuple);
/*
* check to see if we actually need to change anything
*/
if (tuple_class->relhasoids == setOid)
if (rel->rd_rel->relhasoids == setOid)
{
heap_close(class_rel, RowExclusiveLock);
heap_close(rel, NoLock); /* close rel, but keep lock! */
return;
}
tuple_class->relhasoids = setOid;
simple_heap_update(class_rel, &tuple->t_self, tuple);
/* Keep the catalog indexes up to date */
CatalogUpdateIndexes(class_rel, tuple);
if (setOid)
{
/*
* TODO: Generate the now required OID pg_attribute entry
* TODO: Generate the now required OID pg_attribute entry, and
* modify physical rows to have OIDs.
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@ -2515,34 +2455,10 @@ AlterTableAlterOids(Oid myrelid, bool recurse, bool setOid)
}
else
{
HeapTuple atttup;
Relation attrel;
heap_close(rel, NoLock); /* close rel, but keep lock! */
/* Add / Remove the oid record from pg_attribute */
attrel = heap_open(RelOid_pg_attribute, RowExclusiveLock);
/*
* Oids are being removed from the relation, so we need to remove
* the oid pg_attribute record relating.
*/
atttup = SearchSysCache(ATTNUM,
ObjectIdGetDatum(myrelid),
Int16GetDatum(ObjectIdAttributeNumber),
0, 0);
if (!HeapTupleIsValid(atttup))
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
ObjectIdAttributeNumber, myrelid);
simple_heap_delete(attrel, &atttup->t_self);
ReleaseSysCache(atttup);
heap_close(attrel, RowExclusiveLock);
AlterTableDropColumn(myrelid, recurse, false, "oid", behavior);
}
heap_close(class_rel, RowExclusiveLock);
heap_close(rel, NoLock); /* close rel, but keep lock! */
}
/*
@ -2555,7 +2471,8 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
{
Relation rel;
AttrNumber attnum;
TupleDesc tupleDesc;
HeapTuple tuple;
Form_pg_attribute targetatt;
ObjectAddress object;
rel = heap_open(myrelid, AccessExclusiveLock);
@ -2580,29 +2497,32 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
/*
* get the number of the attribute
*/
attnum = get_attnum(myrelid, colName);
if (attnum == InvalidAttrNumber)
tuple = SearchSysCacheAttName(myrelid, colName);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
colName, RelationGetRelationName(rel))));
errmsg("column \"%s\" of relation \"%s\" does not exist",
colName, RelationGetRelationName(rel))));
targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
/* Can't drop a system attribute */
/* XXX perhaps someday allow dropping OID? */
if (attnum < 0)
attnum = targetatt->attnum;
/* Can't drop a system attribute, except OID */
if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot drop system column \"%s\"",
colName)));
/* Don't drop inherited columns */
tupleDesc = RelationGetDescr(rel);
if (tupleDesc->attrs[attnum - 1]->attinhcount > 0 && !recursing)
if (targetatt->attinhcount > 0 && !recursing)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot drop inherited column \"%s\"",
colName)));
ReleaseSysCache(tuple);
/*
* If we are asked to drop ONLY in this table (no recursion), we need
* to mark the inheritors' attribute as locally defined rather than
@ -2622,7 +2542,6 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
{
Oid childrelid = lfirsto(child);
Relation childrel;
HeapTuple tuple;
Form_pg_attribute childatt;
childrel = heap_open(childrelid, AccessExclusiveLock);
@ -2670,7 +2589,6 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
{
Oid childrelid = lfirsto(child);
Relation childrel;
HeapTuple tuple;
Form_pg_attribute childatt;
if (childrelid == myrelid)
@ -2712,7 +2630,7 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
}
/*
* Perform the actual deletion
* Perform the actual column deletion
*/
object.classId = RelOid_pg_class;
object.objectId = myrelid;
@ -2720,6 +2638,32 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
performDeletion(&object, behavior);
/*
* If we dropped the OID column, must adjust pg_class.relhasoids
*/
if (attnum == ObjectIdAttributeNumber)
{
Relation class_rel;
Form_pg_class tuple_class;
class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
tuple = SearchSysCacheCopy(RELOID,
ObjectIdGetDatum(myrelid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", myrelid);
tuple_class = (Form_pg_class) GETSTRUCT(tuple);
tuple_class->relhasoids = false;
simple_heap_update(class_rel, &tuple->t_self, tuple);
/* Keep the catalog indexes up to date */
CatalogUpdateIndexes(class_rel, tuple);
heap_close(class_rel, RowExclusiveLock);
}
heap_close(rel, NoLock); /* close rel, but keep lock! */
}
@ -4171,6 +4115,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
tupdesc,
RELKIND_TOASTVALUE,
shared_relation,
true,
0,
ONCOMMIT_NOOP,
true);

View File

@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.229 2004/03/02 18:56:15 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.230 2004/03/23 19:35:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -797,6 +797,8 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
tupdesc,
RELKIND_RELATION,
false,
true,
0,
ONCOMMIT_NOOP,
allowSystemTableMods);

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.210 2004/02/10 01:55:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.211 2004/03/23 19:35:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -608,10 +608,11 @@ ProcessUtility(Node *parsetree,
case 'L': /* CLUSTER ON */
AlterTableClusterOn(relid, stmt->name);
break;
case 'o': /* ADD OIDS */
case 'o': /* SET WITHOUT OIDS */
AlterTableAlterOids(relid,
false,
interpretInhOption(stmt->relation->inhOpt),
false);
DROP_RESTRICT);
break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.64 2004/02/15 21:01:39 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.65 2004/03/23 19:35:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,6 +41,8 @@ extern Oid heap_create_with_catalog(const char *relname,
TupleDesc tupdesc,
char relkind,
bool shared_relation,
bool oidislocal,
int oidinhcount,
OnCommitAction oncommit,
bool allow_system_table_mods);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.14 2003/11/29 22:40:59 pgsql Exp $
* $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.15 2004/03/23 19:35:17 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -49,7 +49,8 @@ extern void AlterTableCreateToastTable(Oid relOid, bool silent);
extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId);
extern void AlterTableAlterOids(Oid myrelid, bool recurse, bool setOid);
extern void AlterTableAlterOids(Oid myrelid, bool setOid, bool recurse,
DropBehavior behavior);
extern Oid DefineRelation(CreateStmt *stmt, char relkind);

View File

@ -688,9 +688,11 @@ delete from atacc1;
-- try dropping a non-existent column, should fail
alter table atacc1 drop bar;
ERROR: column "bar" of relation "atacc1" does not exist
-- try dropping the oid column, should fail
-- try dropping the oid column, should succeed
alter table atacc1 drop oid;
ERROR: cannot drop system column "oid"
-- try dropping the xmin column, should fail
alter table atacc1 drop xmin;
ERROR: cannot drop system column "xmin"
-- try creating a view and altering that, should fail
create view myview as select * from atacc1;
select * from myview;

View File

@ -632,9 +632,12 @@ delete from atacc1;
-- try dropping a non-existent column, should fail
alter table atacc1 drop bar;
-- try dropping the oid column, should fail
-- try dropping the oid column, should succeed
alter table atacc1 drop oid;
-- try dropping the xmin column, should fail
alter table atacc1 drop xmin;
-- try creating a view and altering that, should fail
create view myview as select * from atacc1;
select * from myview;