<para>
Adding a column with a non-null default or changing the type of an
existing column will require the entire table and indexes to be rewritten.
- This might take a significant amount of time for a large table; and it will
- temporarily require double the disk space. Adding or removing a system
- <literal>oid</> column likewise requires rewriting the entire table.
+ As an exception, if the old type type is binary coercible to the new
+ type and the <literal>USING</> clause does not change the column contents,
+ a table rewrite is not needed, but any indexes on the affected columns
+ must still be rebuilt. Adding or removing a system <literal>oid</> column
+ also requires rewriting the entire table. Table and/or index rebuilds may
+ take a significant amount of time for a large table; and will temporarily
+ require as much as double the disk space.
</para>
<para>
<para>
To force an immediate rewrite of the table, you can use
<link linkend="SQL-VACUUM">VACUUM FULL</>, <xref linkend="SQL-CLUSTER">
- or one of the forms of ALTER TABLE that forces a rewrite, such as
- SET DATA TYPE. This results in no semantically-visible change in the
- table, but gets rid of no-longer-useful data.
+ or one of the forms of ALTER TABLE that forces a rewrite. This results in
+ no semantically-visible change in the table, but gets rid of
+ no-longer-useful data.
</para>
<para>
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 */
+ bool rewrite; /* T if we a rewrite is forced */
Oid newTableSpace; /* new tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
AlteredTableInfo *tab, Relation rel,
bool recurse, bool recursing,
AlterTableCmd *cmd, LOCKMODE lockmode);
+static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
const char *colName, TypeName *typeName, LOCKMODE lockmode);
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
if (tab->relkind == RELKIND_FOREIGN_TABLE)
continue;
+ /*
+ * If we change column data types or add/remove OIDs, the operation
+ * has to be propagated to tables that use this table's rowtype as a
+ * column type. tab->newvals will also be non-NULL in the case where
+ * we're adding a column with a default. We choose to forbid that
+ * case as well, since composite types might eventually support
+ * defaults.
+ *
+ * (Eventually we'll probably need to check for composite type
+ * dependencies even when we're just scanning the table without a
+ * rewrite, but at the moment a composite type does not enforce any
+ * constraints, so it's not necessary/appropriate to enforce them
+ * just during ALTER.)
+ */
+ if (tab->newvals != NIL || tab->rewrite)
+ {
+ Relation rel;
+
+ rel = heap_open(tab->relid, NoLock);
+ find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
+ heap_close(rel, NoLock);
+ }
+
/*
* We only need to rewrite the table if at least one column needs to
* be recomputed, or we are adding/removing the OID column.
*/
- if (tab->newvals != NIL || tab->new_changeoids)
+ if (tab->rewrite)
{
/* Build a temporary relation and copy data */
Relation OldHeap;
hi_options = 0;
}
- /*
- * If we change column data types or add/remove OIDs, the operation has to
- * be propagated to tables that use this table's rowtype as a column type.
- * newrel will also be non-NULL in the case where we're adding a column
- * with a default. We choose to forbid that case as well, since composite
- * types might eventually support defaults.
- *
- * (Eventually we'll probably need to check for composite type
- * dependencies even when we're just scanning the table without a rewrite,
- * but at the moment a composite type does not enforce any constraints,
- * so it's not necessary/appropriate to enforce them just during ALTER.)
- */
- if (newrel)
- find_composite_type_dependencies(oldrel->rd_rel->reltype,
- oldrel, NULL);
-
/*
* Generate the constraint and default execution states
*/
List *dropped_attrs = NIL;
ListCell *lc;
+ if (newrel)
+ ereport(DEBUG1,
+ (errmsg("rewriting table \"%s\"",
+ RelationGetRelationName(oldrel))));
+ else
+ ereport(DEBUG1,
+ (errmsg("verifying table \"%s\"",
+ RelationGetRelationName(oldrel))));
+
econtext = GetPerTupleExprContext(estate);
/*
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
- if (newrel)
+ if (tab->rewrite)
{
Oid tupOid = InvalidOid;
newval->expr = defval;
tab->newvals = lappend(tab->newvals, newval);
+ tab->rewrite = true;
}
/*
* table to fix that.
*/
if (isOid)
- tab->new_changeoids = true;
+ tab->rewrite = true;
/*
* Add needed dependency entries for the new column.
tab = ATGetQueueEntry(wqueue, rel);
/* Tell Phase 3 to physically remove the OID column */
- tab->new_changeoids = true;
+ tab->rewrite = true;
}
}
/* suppress schema rights check when rebuilding existing index */
check_rights = !is_rebuild;
/* skip index build if phase 3 will have to rewrite table anyway */
- skip_build = (tab->newvals != NIL);
+ skip_build = tab->rewrite;
/* suppress notices when rebuilding existing index */
quiet = is_rebuild;
HeapTuple tuple;
Trigger trig;
+ ereport(DEBUG1,
+ (errmsg("validating foreign key constraint \"%s\"", conname)));
+
/*
* Build a trigger call structure; we'll need it either way.
*/
newval->expr = (Expr *) transform;
tab->newvals = lappend(tab->newvals, newval);
+ if (ATColumnChangeRequiresRewrite(transform, attnum))
+ tab->rewrite = true;
}
else if (tab->relkind == RELKIND_FOREIGN_TABLE)
{
ATTypedTableRecursion(wqueue, rel, cmd, lockmode);
}
+/*
+ * When the data type of a column is changed, a rewrite might not be require
+ * if the data type is being changed to its current type, or more interestingly
+ * to a type to which it is binary coercible. But we must check carefully that
+ * the USING clause isn't trying to insert some other value.
+ */
+static bool
+ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
+{
+ Assert(expr != NULL);
+
+ for (;;)
+ {
+ /* only one varno, so no need to check that */
+ if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
+ return false;
+ else if (IsA(expr, RelabelType))
+ expr = (Node *) ((RelabelType *) expr)->arg;
+ else
+ return true;
+ }
+}
+
static void
ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
const char *colName, TypeName *typeName, LOCKMODE lockmode)