Commit 6d1e3618 authored by Tom Lane's avatar Tom Lane

Change ALTER TABLE SET WITHOUT OIDS to rewrite the whole table to physically

get rid of the OID column.  This eliminates the problem discovered by Heikki
back in November that 8.4's suppression of "unnecessary" junk filtering in
INSERT/SELECT could lead to an Assert failure, or storing of oids into a table
that shouldn't have them if Asserts are off.  While that particular problem
could have been solved in other ways, it seems likely to be just a forerunner
of things to come if we continue to allow tables to contain rows that disagree
with the pg_class.relhasoids setting.  It's better to make this operation slow
than to sacrifice performance or risk bugs in more common code paths.

Also, add ALTER TABLE SET WITH OIDS to rewrite the table to add oids.
This was a bit more controversial, but in view of the very small amount of
extra code needed given the current ALTER TABLE infrastructure, it seems best
to eliminate the asymmetry in features.
parent 68d95f12
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.103 2009/02/09 20:57:59 alvherre Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.104 2009/02/11 21:11:15 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -52,6 +52,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -52,6 +52,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable> ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable> CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
SET WITHOUT CLUSTER SET WITHOUT CLUSTER
SET WITH OIDS
SET WITHOUT OIDS SET WITHOUT OIDS
SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] ) RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
...@@ -217,10 +218,10 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -217,10 +218,10 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
The trigger firing mechanism is also affected by the configuration The trigger firing mechanism is also affected by the configuration
variable <xref linkend="guc-session-replication-role">. Simply enabled variable <xref linkend="guc-session-replication-role">. Simply enabled
triggers will fire when the replication role is <quote>origin</> triggers will fire when the replication role is <quote>origin</>
(the default) or <quote>local</>. Triggers configured <literal>ENABLE REPLICA</literal> (the default) or <quote>local</>. Triggers configured as <literal>ENABLE
will only fire if the session is in <quote>replica</> mode and triggers REPLICA</literal> will only fire if the session is in <quote>replica</>
configured <literal>ENABLE ALWAYS</literal> will fire regardless of the current replication mode, and triggers configured as <literal>ENABLE ALWAYS</literal> will
mode. fire regardless of the current replication mode.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -262,6 +263,23 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -262,6 +263,23 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><literal>SET WITH OIDS</literal></term>
<listitem>
<para>
This form adds an <literal>oid</literal> system column to the
table (see <xref linkend="ddl-system-columns">).
It does nothing if the table already has OIDs.
</para>
<para>
Note that this is not equivalent to <literal>ADD COLUMN oid oid</>;
that would add a normal column that happened to be named
<literal>oid</>, not a system column.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><literal>SET WITHOUT OIDS</literal></term> <term><literal>SET WITHOUT OIDS</literal></term>
<listitem> <listitem>
...@@ -272,12 +290,6 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -272,12 +290,6 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
except that it will not complain if there is already no except that it will not complain if there is already no
<literal>oid</literal> column. <literal>oid</literal> column.
</para> </para>
<para>
Note that there is no variant of <command>ALTER TABLE</command>
that allows OIDs to be restored to a table once they have been
removed.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -302,7 +314,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -302,7 +314,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
in the <literal>WITH (<replaceable in the <literal>WITH (<replaceable
class="PARAMETER">storage_parameter</>)</literal> syntax, class="PARAMETER">storage_parameter</>)</literal> syntax,
<command>ALTER TABLE</> does not treat <literal>OIDS</> as a <command>ALTER TABLE</> does not treat <literal>OIDS</> as a
storage parameter. storage parameter. Instead use the <literal>SET WITH OIDS</>
and <literal>SET WITHOUT OIDS</> forms to change OID status.
</para> </para>
</note> </note>
</listitem> </listitem>
...@@ -637,7 +650,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -637,7 +650,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
Adding a column with a non-null default or changing the type of an Adding a column with a non-null default or changing the type of an
existing column will require the entire table to be rewritten. This existing column will require the entire table to be rewritten. This
might take a significant amount of time for a large table; and it will might take a significant amount of time for a large table; and it will
temporarily require double the disk space. temporarily require double the disk space. Adding or removing a system
<literal>oid</> column likewise requires rewriting the entire table.
</para> </para>
<para> <para>
...@@ -658,7 +672,9 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: ...@@ -658,7 +672,9 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
column. Thus, dropping a column is quick but it will not immediately column. Thus, dropping a column is quick but it will not immediately
reduce the on-disk size of your table, as the space occupied reduce the on-disk size of your table, as the space occupied
by the dropped column is not reclaimed. The space will be by the dropped column is not reclaimed. The space will be
reclaimed over time as existing rows are updated. reclaimed over time as existing rows are updated. (These statements do
not apply when dropping the system <literal>oid</> column; that is done
with an immediate rewrite.)
</para> </para>
<para> <para>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -138,6 +138,7 @@ typedef struct AlteredTableInfo ...@@ -138,6 +138,7 @@ typedef struct AlteredTableInfo
List *constraints; /* List of NewConstraint */ List *constraints; /* List of NewConstraint */
List *newvals; /* List of NewColumnValue */ List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */ 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 */ Oid newTableSpace; /* new tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */ /* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */ List *changedConstraintOids; /* OIDs of constraints to rebuild */
...@@ -269,8 +270,10 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel, ...@@ -269,8 +270,10 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel,
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd); AlterTableCmd *cmd);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, 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 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 ATExecDropNotNull(Relation rel, const char *colName);
static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
const char *colName); const char *colName);
...@@ -282,7 +285,7 @@ static void ATExecSetStatistics(Relation rel, const char *colName, ...@@ -282,7 +285,7 @@ static void ATExecSetStatistics(Relation rel, const char *colName,
Node *newValue); Node *newValue);
static void ATExecSetStorage(Relation rel, const char *colName, static void ATExecSetStorage(Relation rel, const char *colName,
Node *newValue); Node *newValue);
static void ATExecDropColumn(Relation rel, const char *colName, static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
DropBehavior behavior, DropBehavior behavior,
bool recurse, bool recursing); bool recurse, bool recursing);
static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel, static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
...@@ -2452,6 +2455,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, ...@@ -2452,6 +2455,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */ /* No command-specific prep needed */
pass = AT_PASS_MISC; pass = AT_PASS_MISC;
break; 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 */ case AT_DropOids: /* SET WITHOUT OIDS */
ATSimplePermissions(rel, false); ATSimplePermissions(rel, false);
/* Performs own recursion */ /* Performs own recursion */
...@@ -2589,7 +2599,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, ...@@ -2589,7 +2599,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
{ {
case AT_AddColumn: /* ADD COLUMN */ case AT_AddColumn: /* ADD COLUMN */
case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */ case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def); ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false);
break; break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
ATExecColumnDefault(rel, cmd->name, cmd->def); ATExecColumnDefault(rel, cmd->name, cmd->def);
...@@ -2607,10 +2617,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, ...@@ -2607,10 +2617,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
ATExecSetStorage(rel, cmd->name, cmd->def); ATExecSetStorage(rel, cmd->name, cmd->def);
break; break;
case AT_DropColumn: /* DROP COLUMN */ case AT_DropColumn: /* DROP COLUMN */
ATExecDropColumn(rel, cmd->name, cmd->behavior, false, false); ATExecDropColumn(wqueue, rel, cmd->name,
cmd->behavior, false, false);
break; break;
case AT_DropColumnRecurse: /* DROP COLUMN with recursion */ 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; break;
case AT_AddIndex: /* ADD INDEX */ case AT_AddIndex: /* ADD INDEX */
ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false); ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false);
...@@ -2644,6 +2656,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, ...@@ -2644,6 +2656,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_DropCluster: /* SET WITHOUT CLUSTER */ case AT_DropCluster: /* SET WITHOUT CLUSTER */
ATExecDropCluster(rel); ATExecDropCluster(rel);
break; 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 */ case AT_DropOids: /* SET WITHOUT OIDS */
/* /*
...@@ -2748,9 +2765,9 @@ ATRewriteTables(List **wqueue) ...@@ -2748,9 +2765,9 @@ ATRewriteTables(List **wqueue)
/* /*
* We only need to rewrite the table if at least one column needs to * 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 */ /* Build a temporary relation and copy data */
Oid OIDNewHeap; Oid OIDNewHeap;
...@@ -2976,8 +2993,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -2976,8 +2993,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
{ {
NewColumnValue *ex = lfirst(l); NewColumnValue *ex = lfirst(l);
needscan = true;
ex->exprstate = ExecPrepareExpr((Expr *) ex->expr, estate); ex->exprstate = ExecPrepareExpr((Expr *) ex->expr, estate);
} }
...@@ -3000,7 +3015,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ...@@ -3000,7 +3015,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
needscan = true; needscan = true;
} }
if (needscan) if (newrel || needscan)
{ {
ExprContext *econtext; ExprContext *econtext;
Datum *values; Datum *values;
...@@ -3479,7 +3494,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, ...@@ -3479,7 +3494,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
static void static void
ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
ColumnDef *colDef) ColumnDef *colDef, bool isOid)
{ {
Oid myrelid = RelationGetRelid(rel); Oid myrelid = RelationGetRelid(rel);
Relation pgclass, Relation pgclass,
...@@ -3512,7 +3527,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3512,7 +3527,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
Oid ctypeId; Oid ctypeId;
int32 ctypmod; int32 ctypmod;
/* Okay if child matches by type */ /* Child column must match by type */
ctypeId = typenameTypeId(NULL, colDef->typename, &ctypmod); ctypeId = typenameTypeId(NULL, colDef->typename, &ctypmod);
if (ctypeId != childatt->atttypid || if (ctypeId != childatt->atttypid ||
ctypmod != childatt->atttypmod) ctypmod != childatt->atttypmod)
...@@ -3521,6 +3536,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3521,6 +3536,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
errmsg("child table \"%s\" has different type for column \"%s\"", errmsg("child table \"%s\" has different type for column \"%s\"",
RelationGetRelationName(rel), colDef->colname))); 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 */ /* Bump the existing child att's inhcount */
childatt->attinhcount++; childatt->attinhcount++;
simple_heap_update(attrdesc, &tuple->t_self, tuple); simple_heap_update(attrdesc, &tuple->t_self, tuple);
...@@ -3560,12 +3582,18 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3560,12 +3582,18 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
errmsg("column \"%s\" of relation \"%s\" already exists", errmsg("column \"%s\" of relation \"%s\" already exists",
colDef->colname, RelationGetRelationName(rel)))); colDef->colname, RelationGetRelationName(rel))));
/* Determine the new attribute's number */
if (isOid)
newattnum = ObjectIdAttributeNumber;
else
{
newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
if (newattnum > MaxHeapAttributeNumber) if (newattnum > MaxHeapAttributeNumber)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_COLUMNS), (errcode(ERRCODE_TOO_MANY_COLUMNS),
errmsg("tables can have at most %d columns", errmsg("tables can have at most %d columns",
MaxHeapAttributeNumber))); MaxHeapAttributeNumber)));
}
typeTuple = typenameType(NULL, colDef->typename, &typmod); typeTuple = typenameType(NULL, colDef->typename, &typmod);
tform = (Form_pg_type) GETSTRUCT(typeTuple); tform = (Form_pg_type) GETSTRUCT(typeTuple);
...@@ -3578,7 +3606,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3578,7 +3606,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
attribute.attrelid = myrelid; attribute.attrelid = myrelid;
namestrcpy(&(attribute.attname), colDef->colname); namestrcpy(&(attribute.attname), colDef->colname);
attribute.atttypid = typeOid; attribute.atttypid = typeOid;
attribute.attstattarget = -1; attribute.attstattarget = (newattnum > 0) ? -1 : 0;
attribute.attlen = tform->typlen; attribute.attlen = tform->typlen;
attribute.attcacheoff = -1; attribute.attcacheoff = -1;
attribute.atttypmod = typmod; attribute.atttypmod = typmod;
...@@ -3601,8 +3629,11 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3601,8 +3629,11 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
heap_close(attrdesc, RowExclusiveLock); heap_close(attrdesc, RowExclusiveLock);
/* /*
* Update number of attributes in pg_class tuple * Update pg_class tuple as appropriate
*/ */
if (isOid)
((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
else
((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
simple_heap_update(pgclass, &reltup->t_self, reltup); simple_heap_update(pgclass, &reltup->t_self, reltup);
...@@ -3665,7 +3696,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3665,7 +3696,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
* defaults, not even for domain-typed columns. And in any case we mustn't * 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. * 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); defval = (Expr *) build_column_default(rel, attribute.attnum);
...@@ -3702,10 +3733,20 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ...@@ -3702,10 +3733,20 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
/* /*
* If the new column is NOT NULL, tell Phase 3 it needs to test that. * 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; 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. * Add needed dependency entries for the new column.
*/ */
...@@ -3730,6 +3771,30 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) ...@@ -3730,6 +3771,30 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); 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 * ALTER TABLE ALTER COLUMN DROP NOT NULL
*/ */
...@@ -4088,12 +4153,10 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) ...@@ -4088,12 +4153,10 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue)
* because we have to decide at runtime whether to recurse or not depending * 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 * 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 * static pre-pass because it won't handle multiple inheritance situations
* correctly.) Since DROP COLUMN doesn't need to create any work queue * correctly.)
* entries for Phase 3, it's okay to recurse internally in this routine
* without considering the work queue.
*/ */
static void static void
ATExecDropColumn(Relation rel, const char *colName, ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
DropBehavior behavior, DropBehavior behavior,
bool recurse, bool recursing) bool recurse, bool recursing)
{ {
...@@ -4178,7 +4241,8 @@ ATExecDropColumn(Relation rel, const char *colName, ...@@ -4178,7 +4241,8 @@ ATExecDropColumn(Relation rel, const char *colName,
if (childatt->attinhcount == 1 && !childatt->attislocal) if (childatt->attinhcount == 1 && !childatt->attislocal)
{ {
/* Time to delete this child column, too */ /* Time to delete this child column, too */
ATExecDropColumn(childrel, colName, behavior, true, true); ATExecDropColumn(wqueue, childrel, colName,
behavior, true, true);
} }
else else
{ {
...@@ -4230,12 +4294,14 @@ ATExecDropColumn(Relation rel, const char *colName, ...@@ -4230,12 +4294,14 @@ ATExecDropColumn(Relation rel, const char *colName,
performDeletion(&object, behavior); 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) if (attnum == ObjectIdAttributeNumber)
{ {
Relation class_rel; Relation class_rel;
Form_pg_class tuple_class; Form_pg_class tuple_class;
AlteredTableInfo *tab;
class_rel = heap_open(RelationRelationId, RowExclusiveLock); class_rel = heap_open(RelationRelationId, RowExclusiveLock);
...@@ -4254,6 +4320,12 @@ ATExecDropColumn(Relation rel, const char *colName, ...@@ -4254,6 +4320,12 @@ ATExecDropColumn(Relation rel, const char *colName,
CatalogUpdateIndexes(class_rel, tuple); CatalogUpdateIndexes(class_rel, tuple);
heap_close(class_rel, RowExclusiveLock); 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;
} }
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.657 2009/02/02 19:31:39 alvherre Exp $ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.658 2009/02/11 21:11:16 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1636,6 +1636,13 @@ alter_table_cmd: ...@@ -1636,6 +1636,13 @@ alter_table_cmd:
n->behavior = $4; n->behavior = $4;
$$ = (Node *)n; $$ = (Node *)n;
} }
/* ALTER TABLE <name> SET WITH OIDS */
| SET WITH OIDS
{
AlterTableCmd *n = makeNode(AlterTableCmd);
n->subtype = AT_AddOids;
$$ = (Node *)n;
}
/* ALTER TABLE <name> SET WITHOUT OIDS */ /* ALTER TABLE <name> SET WITHOUT OIDS */
| SET WITHOUT OIDS | SET WITHOUT OIDS
{ {
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.390 2009/02/02 19:31:40 alvherre Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.391 2009/02/11 21:11:16 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1127,6 +1127,7 @@ typedef enum AlterTableType ...@@ -1127,6 +1127,7 @@ typedef enum AlterTableType
AT_ChangeOwner, /* change owner */ AT_ChangeOwner, /* change owner */
AT_ClusterOn, /* CLUSTER ON */ AT_ClusterOn, /* CLUSTER ON */
AT_DropCluster, /* SET WITHOUT CLUSTER */ AT_DropCluster, /* SET WITHOUT CLUSTER */
AT_AddOids, /* SET WITH OIDS */
AT_DropOids, /* SET WITHOUT OIDS */ AT_DropOids, /* SET WITHOUT OIDS */
AT_SetTableSpace, /* SET TABLESPACE */ AT_SetTableSpace, /* SET TABLESPACE */
AT_SetRelOptions, /* SET (...) -- AM specific parameters */ AT_SetRelOptions, /* SET (...) -- AM specific parameters */
......
...@@ -1171,7 +1171,7 @@ NOTICE: drop cascades to 2 other objects ...@@ -1171,7 +1171,7 @@ NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table c1 DETAIL: drop cascades to table c1
drop cascades to table gc1 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; create table altstartwith (col integer) with oids;
insert into altstartwith values (1); insert into altstartwith values (1);
...@@ -1192,9 +1192,17 @@ select * from altstartwith; ...@@ -1192,9 +1192,17 @@ select * from altstartwith;
1 1
(1 row) (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; create table altwithoid (col integer) with oids;
-- Inherits parents oid column -- Inherits parents oid column anyway
create table altinhoid () inherits (altwithoid) without oids; create table altinhoid () inherits (altwithoid) without oids;
insert into altinhoid values (1); insert into altinhoid values (1);
select oid > 0, * from altwithoid; select oid > 0, * from altwithoid;
...@@ -1210,7 +1218,6 @@ select oid > 0, * from altinhoid; ...@@ -1210,7 +1218,6 @@ select oid > 0, * from altinhoid;
(1 row) (1 row)
alter table altwithoid set without oids; alter table altwithoid set without oids;
alter table altinhoid set without oids;
select oid > 0, * from altwithoid; -- fails select oid > 0, * from altwithoid; -- fails
ERROR: column "oid" does not exist ERROR: column "oid" does not exist
LINE 1: select oid > 0, * from altwithoid; LINE 1: select oid > 0, * from altwithoid;
...@@ -1231,6 +1238,63 @@ select * from altinhoid; ...@@ -1231,6 +1238,63 @@ select * from altinhoid;
1 1
(1 row) (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 -- test renumbering of child-table columns in inherited operations
create table p1 (f1 int); create table p1 (f1 int);
create table c1 (f2 text, f3 int) inherits (p1); create table c1 (f2 text, f3 int) inherits (p1);
......
...@@ -921,7 +921,7 @@ order by relname, attnum; ...@@ -921,7 +921,7 @@ order by relname, attnum;
drop table p1, p2 cascade; 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; create table altstartwith (col integer) with oids;
...@@ -934,10 +934,16 @@ alter table altstartwith set without oids; ...@@ -934,10 +934,16 @@ alter table altstartwith set without oids;
select oid > 0, * from altstartwith; -- fails select oid > 0, * from altstartwith; -- fails
select * from altstartwith; 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; create table altwithoid (col integer) with oids;
-- Inherits parents oid column -- Inherits parents oid column anyway
create table altinhoid () inherits (altwithoid) without oids; create table altinhoid () inherits (altwithoid) without oids;
insert into altinhoid values (1); insert into altinhoid values (1);
...@@ -946,13 +952,42 @@ select oid > 0, * from altwithoid; ...@@ -946,13 +952,42 @@ select oid > 0, * from altwithoid;
select oid > 0, * from altinhoid; select oid > 0, * from altinhoid;
alter table altwithoid set without oids; alter table altwithoid set without oids;
alter table altinhoid set without oids;
select oid > 0, * from altwithoid; -- fails select oid > 0, * from altwithoid; -- fails
select oid > 0, * from altinhoid; -- fails select oid > 0, * from altinhoid; -- fails
select * from altwithoid; select * from altwithoid;
select * from altinhoid; 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 -- test renumbering of child-table columns in inherited operations
create table p1 (f1 int); create table p1 (f1 int);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment