- mode.
operations. It does not actually re-cluster the table.
+ It does nothing if the table already has OIDs.
- removed.
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.279 2009/02/02 19:31:38 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.280 2009/02/11 21:11:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *constraints; /* List of NewConstraint */
List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */
+ bool new_changeoids; /* T if we added/dropped the OID column */
Oid newTableSpace; /* new tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
- ColumnDef *colDef);
+ ColumnDef *colDef, bool isOid);
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
+static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
+ AlterTableCmd *cmd);
static void ATExecDropNotNull(Relation rel, const char *colName);
static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
const char *colName);
Node *newValue);
static void ATExecSetStorage(Relation rel, const char *colName,
Node *newValue);
-static void ATExecDropColumn(Relation rel, const char *colName,
+static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
DropBehavior behavior,
bool recurse, bool recursing);
static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AddOids: /* SET WITH OIDS */
+ ATSimplePermissions(rel, false);
+ /* Performs own recursion */
+ if (!rel->rd_rel->relhasoids || recursing)
+ ATPrepAddOids(wqueue, rel, recurse, cmd);
+ pass = AT_PASS_ADD_COL;
+ break;
case AT_DropOids: /* SET WITHOUT OIDS */
ATSimplePermissions(rel, false);
/* Performs own recursion */
{
case AT_AddColumn: /* ADD COLUMN */
case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def);
+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false);
break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
ATExecColumnDefault(rel, cmd->name, cmd->def);
ATExecSetStorage(rel, cmd->name, cmd->def);
break;
case AT_DropColumn: /* DROP COLUMN */
- ATExecDropColumn(rel, cmd->name, cmd->behavior, false, false);
+ ATExecDropColumn(wqueue, rel, cmd->name,
+ cmd->behavior, false, false);
break;
case AT_DropColumnRecurse: /* DROP COLUMN with recursion */
- ATExecDropColumn(rel, cmd->name, cmd->behavior, true, false);
+ ATExecDropColumn(wqueue, rel, cmd->name,
+ cmd->behavior, true, false);
break;
case AT_AddIndex: /* ADD INDEX */
ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false);
case AT_DropCluster: /* SET WITHOUT CLUSTER */
ATExecDropCluster(rel);
break;
+ case AT_AddOids: /* SET WITH OIDS */
+ /* Use the ADD COLUMN code, unless prep decided to do nothing */
+ if (cmd->def != NULL)
+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true);
+ break;
case AT_DropOids: /* SET WITHOUT OIDS */
/*
/*
* We only need to rewrite the table if at least one column needs to
- * be recomputed.
+ * be recomputed, or we are adding/removing the OID column.
*/
- if (tab->newvals != NIL)
+ if (tab->newvals != NIL || tab->new_changeoids)
{
/* Build a temporary relation and copy data */
Oid OIDNewHeap;
{
NewColumnValue *ex = lfirst(l);
- needscan = true;
-
ex->exprstate = ExecPrepareExpr((Expr *) ex->expr, estate);
}
needscan = true;
}
- if (needscan)
+ if (newrel || needscan)
{
ExprContext *econtext;
Datum *values;
static void
ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
- ColumnDef *colDef)
+ ColumnDef *colDef, bool isOid)
{
Oid myrelid = RelationGetRelid(rel);
Relation pgclass,
Oid ctypeId;
int32 ctypmod;
- /* Okay if child matches by type */
+ /* Child column must match by type */
ctypeId = typenameTypeId(NULL, colDef->typename, &ctypmod);
if (ctypeId != childatt->atttypid ||
ctypmod != childatt->atttypmod)
errmsg("child table \"%s\" has different type for column \"%s\"",
RelationGetRelationName(rel), colDef->colname)));
+ /* If it's OID, child column must actually be OID */
+ if (isOid && childatt->attnum != ObjectIdAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table \"%s\" has a conflicting \"%s\" column",
+ RelationGetRelationName(rel), colDef->colname)));
+
/* Bump the existing child att's inhcount */
childatt->attinhcount++;
simple_heap_update(attrdesc, &tuple->t_self, tuple);
errmsg("column \"%s\" of relation \"%s\" already exists",
colDef->colname, RelationGetRelationName(rel))));
- newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
- if (newattnum > MaxHeapAttributeNumber)
- ereport(ERROR,
- (errcode(ERRCODE_TOO_MANY_COLUMNS),
- errmsg("tables can have at most %d columns",
- MaxHeapAttributeNumber)));
+ /* Determine the new attribute's number */
+ if (isOid)
+ newattnum = ObjectIdAttributeNumber;
+ else
+ {
+ newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
+ if (newattnum > MaxHeapAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("tables can have at most %d columns",
+ MaxHeapAttributeNumber)));
+ }
typeTuple = typenameType(NULL, colDef->typename, &typmod);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
attribute.attrelid = myrelid;
namestrcpy(&(attribute.attname), colDef->colname);
attribute.atttypid = typeOid;
- attribute.attstattarget = -1;
+ attribute.attstattarget = (newattnum > 0) ? -1 : 0;
attribute.attlen = tform->typlen;
attribute.attcacheoff = -1;
attribute.atttypmod = typmod;
heap_close(attrdesc, RowExclusiveLock);
/*
- * Update number of attributes in pg_class tuple
+ * Update pg_class tuple as appropriate
*/
- ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
+ if (isOid)
+ ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
+ else
+ ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
simple_heap_update(pgclass, &reltup->t_self, reltup);
* defaults, not even for domain-typed columns. And in any case we mustn't
* invoke Phase 3 on a view, since it has no storage.
*/
- if (relkind != RELKIND_VIEW)
+ if (relkind != RELKIND_VIEW && attribute.attnum > 0)
{
defval = (Expr *) build_column_default(rel, attribute.attnum);
/*
* If the new column is NOT NULL, tell Phase 3 it needs to test that.
+ * (Note we don't do this for an OID column. OID will be marked not
+ * null, but since it's filled specially, there's no need to test
+ * anything.)
*/
tab->new_notnull |= colDef->is_not_null;
}
+ /*
+ * If we are adding an OID column, we have to tell Phase 3 to rewrite
+ * the table to fix that.
+ */
+ if (isOid)
+ tab->new_changeoids = true;
+
/*
* Add needed dependency entries for the new column.
*/
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+/*
+ * ALTER TABLE SET WITH OIDS
+ *
+ * Basically this is an ADD COLUMN for the special OID column. We have
+ * to cons up a ColumnDef node because the ADD COLUMN code needs one.
+ */
+static void
+ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd)
+{
+ /* If we're recursing to a child table, the ColumnDef is already set up */
+ if (cmd->def == NULL)
+ {
+ ColumnDef *cdef = makeNode(ColumnDef);
+
+ cdef->colname = pstrdup("oid");
+ cdef->typename = makeTypeNameFromOid(OIDOID, -1);
+ cdef->inhcount = 0;
+ cdef->is_local = true;
+ cdef->is_not_null = true;
+ cmd->def = (Node *) cdef;
+ }
+ ATPrepAddColumn(wqueue, rel, recurse, cmd);
+}
+
/*
* ALTER TABLE ALTER COLUMN DROP NOT NULL
*/
* because we have to decide at runtime whether to recurse or not depending
* on whether attinhcount goes to zero or not. (We can't check this in a
* static pre-pass because it won't handle multiple inheritance situations
- * correctly.) Since DROP COLUMN doesn't need to create any work queue
- * entries for Phase 3, it's okay to recurse internally in this routine
- * without considering the work queue.
+ * correctly.)
*/
static void
-ATExecDropColumn(Relation rel, const char *colName,
+ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
DropBehavior behavior,
bool recurse, bool recursing)
{
if (childatt->attinhcount == 1 && !childatt->attislocal)
{
/* Time to delete this child column, too */
- ATExecDropColumn(childrel, colName, behavior, true, true);
+ ATExecDropColumn(wqueue, childrel, colName,
+ behavior, true, true);
}
else
{
performDeletion(&object, behavior);
/*
- * If we dropped the OID column, must adjust pg_class.relhasoids
+ * If we dropped the OID column, must adjust pg_class.relhasoids and
+ * tell Phase 3 to physically get rid of the column.
*/
if (attnum == ObjectIdAttributeNumber)
{
Relation class_rel;
Form_pg_class tuple_class;
+ AlteredTableInfo *tab;
class_rel = heap_open(RelationRelationId, RowExclusiveLock);
CatalogUpdateIndexes(class_rel, tuple);
heap_close(class_rel, RowExclusiveLock);
+
+ /* Find or create work queue entry for this table */
+ tab = ATGetQueueEntry(wqueue, rel);
+
+ /* Tell Phase 3 to physically remove the OID column */
+ tab->new_changeoids = true;
}
}
DETAIL: drop cascades to table c1
drop cascades to table gc1
--
--- Test the ALTER TABLE WITHOUT OIDS command
+-- Test the ALTER TABLE SET WITH/WITHOUT OIDS command
--
create table altstartwith (col integer) with oids;
insert into altstartwith values (1);
1
(1 row)
--- Run inheritance tests
+alter table altstartwith set with oids;
+select oid > 0, * from altstartwith;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+drop table altstartwith;
+-- Check inheritance cases
create table altwithoid (col integer) with oids;
--- Inherits parents oid column
+-- Inherits parents oid column anyway
create table altinhoid () inherits (altwithoid) without oids;
insert into altinhoid values (1);
select oid > 0, * from altwithoid;
(1 row)
alter table altwithoid set without oids;
-alter table altinhoid set without oids;
select oid > 0, * from altwithoid; -- fails
ERROR: column "oid" does not exist
LINE 1: select oid > 0, * from altwithoid;
1
(1 row)
+alter table altwithoid set with oids;
+select oid > 0, * from altwithoid;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+select oid > 0, * from altinhoid;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+drop table altwithoid cascade;
+NOTICE: drop cascades to table altinhoid
+create table altwithoid (col integer) without oids;
+-- child can have local oid column
+create table altinhoid () inherits (altwithoid) with oids;
+insert into altinhoid values (1);
+select oid > 0, * from altwithoid; -- fails
+ERROR: column "oid" does not exist
+LINE 1: select oid > 0, * from altwithoid;
+ ^
+select oid > 0, * from altinhoid;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+alter table altwithoid set with oids;
+NOTICE: merging definition of column "oid" for child "altinhoid"
+select oid > 0, * from altwithoid;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+select oid > 0, * from altinhoid;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+-- the child's local definition should remain
+alter table altwithoid set without oids;
+select oid > 0, * from altwithoid; -- fails
+ERROR: column "oid" does not exist
+LINE 1: select oid > 0, * from altwithoid;
+ ^
+select oid > 0, * from altinhoid;
+ ?column? | col
+----------+-----
+ t | 1
+(1 row)
+
+drop table altwithoid cascade;
+NOTICE: drop cascades to table altinhoid
-- test renumbering of child-table columns in inherited operations
create table p1 (f1 int);
create table c1 (f2 text, f3 int) inherits (p1);
drop table p1, p2 cascade;
--
--- Test the ALTER TABLE WITHOUT OIDS command
+-- Test the ALTER TABLE SET WITH/WITHOUT OIDS command
--
create table altstartwith (col integer) with oids;
select oid > 0, * from altstartwith; -- fails
select * from altstartwith;
--- Run inheritance tests
+alter table altstartwith set with oids;
+
+select oid > 0, * from altstartwith;
+
+drop table altstartwith;
+
+-- Check inheritance cases
create table altwithoid (col integer) with oids;
--- Inherits parents oid column
+-- Inherits parents oid column anyway
create table altinhoid () inherits (altwithoid) without oids;
insert into altinhoid values (1);
select oid > 0, * from altinhoid;
alter table altwithoid set without oids;
-alter table altinhoid set without oids;
select oid > 0, * from altwithoid; -- fails
select oid > 0, * from altinhoid; -- fails
select * from altwithoid;
select * from altinhoid;
+alter table altwithoid set with oids;
+
+select oid > 0, * from altwithoid;
+select oid > 0, * from altinhoid;
+
+drop table altwithoid cascade;
+
+create table altwithoid (col integer) without oids;
+
+-- child can have local oid column
+create table altinhoid () inherits (altwithoid) with oids;
+
+insert into altinhoid values (1);
+
+select oid > 0, * from altwithoid; -- fails
+select oid > 0, * from altinhoid;
+
+alter table altwithoid set with oids;
+
+select oid > 0, * from altwithoid;
+select oid > 0, * from altinhoid;
+
+-- the child's local definition should remain
+alter table altwithoid set without oids;
+
+select oid > 0, * from altwithoid; -- fails
+select oid > 0, * from altinhoid;
+
+drop table altwithoid cascade;
+
-- test renumbering of child-table columns in inherited operations
create table p1 (f1 int);