diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 78262d8..63e2538 100644 *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 129,134 **** static List *on_commits = NIL; --- 129,137 ---- #define AT_PASS_MISC 8 /* other stuff */ #define AT_NUM_PASSES 9 + /* Level of effort required in Phase 3 (ATRewriteTables). */ + typedef enum { WORK_NONE = 0, WORK_SCAN = 1, WORK_REWRITE = 2 } WorkLevel; + typedef struct AlteredTableInfo { /* Information saved before any work commences: */ *************** *** 141,147 **** typedef struct AlteredTableInfo 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 */ --- 144,150 ---- List *constraints; /* List of NewConstraint */ List *newvals; /* List of NewColumnValue */ bool new_notnull; /* T if we added new NOT NULL constraints */ ! WorkLevel worklevel; /* How much work shall we do on the heap? */ Oid newTableSpace; /* new tablespace; 0 means no change */ /* Objects to rebuild after completing ALTER TYPE operations */ List *changedConstraintOids; /* OIDs of constraints to rebuild */ *************** *** 3180,3190 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode) if (tab->relkind == RELKIND_FOREIGN_TABLE) continue; ! /* ! * 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) { /* Build a temporary relation and copy data */ Relation OldHeap; --- 3183,3195 ---- if (tab->relkind == RELKIND_FOREIGN_TABLE) continue; ! /* New NOT NULL constraints always require a scan. */ ! if (tab->new_notnull) ! tab->worklevel = Max(tab->worklevel, WORK_SCAN); ! ! switch (tab->worklevel) ! { ! case WORK_REWRITE: { /* Build a temporary relation and copy data */ Relation OldHeap; *************** *** 3194,3202 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode) OldHeap = heap_open(tab->relid, NoLock); /* ! * We don't support rewriting of system catalogs; there are too ! * many corner cases and too little benefit. In particular this ! * is certainly not going to work for mapped catalogs. */ if (IsSystemRelation(OldHeap)) ereport(ERROR, --- 3199,3207 ---- OldHeap = heap_open(tab->relid, NoLock); /* ! * We don't support rewriting of system catalogs; there are ! * too many corner cases and too little benefit. In ! * particular, it would fail for mapped catalogs. */ if (IsSystemRelation(OldHeap)) ereport(ERROR, *************** *** 3205,3212 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode) RelationGetRelationName(OldHeap)))); /* ! * Don't allow rewrite on temp tables of other backends ... their ! * local buffer manager is not going to cope. */ if (RELATION_IS_OTHER_TEMP(OldHeap)) ereport(ERROR, --- 3210,3217 ---- RelationGetRelationName(OldHeap)))); /* ! * Don't allow rewrite on temp tables of other backends ! * ... their local buffer manager is not going to cope. */ if (RELATION_IS_OTHER_TEMP(OldHeap)) ereport(ERROR, *************** *** 3214,3221 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode) errmsg("cannot rewrite temporary tables of other sessions"))); /* ! * Select destination tablespace (same as original unless user ! * requested a change) */ if (tab->newTableSpace) NewTableSpace = tab->newTableSpace; --- 3219,3226 ---- errmsg("cannot rewrite temporary tables of other sessions"))); /* ! * Select destination tablespace (same as original unless ! * user requested a change) */ if (tab->newTableSpace) NewTableSpace = tab->newTableSpace; *************** *** 3224,3265 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode) heap_close(OldHeap, NoLock); ! /* Create transient table that will receive the modified data */ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace); /* * Copy the heap data into the new table with the desired * modifications, and test the current data within the table ! * against new constraints generated by ALTER TABLE commands. */ ATRewriteTable(tab, OIDNewHeap, lockmode); /* ! * Swap the physical files of the old and new heaps, then rebuild ! * indexes and discard the old heap. We can use RecentXmin for ! * the table's new relfrozenxid because we rewrote all the tuples ! * in ATRewriteTable, so no older Xid remains in the table. Also, ! * we never try to swap toast tables by content, since we have no ! * interest in letting this code work on system catalogs. */ finish_heap_swap(tab->relid, OIDNewHeap, false, false, true, RecentXmin); } ! else ! { ! /* ! * Test the current data within the table against new constraints ! * generated by ALTER TABLE commands, but don't rebuild data. ! */ ! if (tab->constraints != NIL || tab->new_notnull) ATRewriteTable(tab, InvalidOid, lockmode); /* * If we had SET TABLESPACE but no reason to reconstruct tuples, * just do a block-by-block copy. */ if (tab->newTableSpace) ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode); } } --- 3229,3271 ---- heap_close(OldHeap, NoLock); ! /* Create transient table for the modified data */ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace); /* * Copy the heap data into the new table with the desired * modifications, and test the current data within the table ! * against new constraints. */ ATRewriteTable(tab, OIDNewHeap, lockmode); /* ! * Swap the physical files of the old and new heaps, then ! * rebuild indexes and discard the old heap. We can use ! * RecentXmin for the table's new relfrozenxid because we ! * rewrote all the tuples in ATRewriteTable, so no older Xid ! * remains in the table. Also, we never try to swap toast ! * tables by content, since we have no interest in letting ! * this code work on system catalogs. */ finish_heap_swap(tab->relid, OIDNewHeap, false, false, true, RecentXmin); } ! break; ! ! case WORK_SCAN: ! /* Test the current table data. */ ATRewriteTable(tab, InvalidOid, lockmode); + break; + case WORK_NONE: /* * If we had SET TABLESPACE but no reason to reconstruct tuples, * just do a block-by-block copy. */ if (tab->newTableSpace) ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode); + break; } } *************** *** 3320,3326 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) Relation newrel; TupleDesc oldTupDesc; TupleDesc newTupDesc; - bool needscan = false; List *notnull_attrs; int i; ListCell *l; --- 3326,3331 ---- *************** *** 3328,3333 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) --- 3333,3348 ---- CommandId mycid; BulkInsertState bistate; int hi_options; + ExprContext *econtext; + Datum *values; + bool *isnull; + TupleTableSlot *oldslot; + TupleTableSlot *newslot; + HeapScanDesc scan; + HeapTuple tuple; + MemoryContext oldCxt; + List *dropped_attrs = NIL; + ListCell *lc; /* * Open the relation(s). We have surely already locked the existing *************** *** 3379,3385 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - needscan = true; con->qualstate = (List *) ExecPrepareExpr((Expr *) con->qual, estate); break; --- 3394,3399 ---- *************** *** 3414,3436 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) !newTupDesc->attrs[i]->attisdropped) notnull_attrs = lappend_int(notnull_attrs, i); } - if (notnull_attrs) - needscan = true; } - if (newrel || needscan) - { - ExprContext *econtext; - Datum *values; - bool *isnull; - TupleTableSlot *oldslot; - TupleTableSlot *newslot; - HeapScanDesc scan; - HeapTuple tuple; - MemoryContext oldCxt; - List *dropped_attrs = NIL; - ListCell *lc; - if (newrel) ereport(DEBUG1, (errmsg("Rewriting table \"%s\"", --- 3428,3435 ---- *************** *** 3574,3580 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) ExecDropSingleTupleTableSlot(oldslot); ExecDropSingleTupleTableSlot(newslot); - } FreeExecutorState(estate); --- 3573,3578 ---- *************** *** 4271,4276 **** ATExecAddColumn(AlteredTableInfo *tab, Relation rel, --- 4269,4275 ---- newval->expr = defval; tab->newvals = lappend(tab->newvals, newval); + tab->worklevel = WORK_REWRITE; } /* *************** *** 4288,4294 **** ATExecAddColumn(AlteredTableInfo *tab, Relation rel, */ if (isOid) { ! tab->new_changeoids = true; /* See comments in ATExecDropColumn. */ find_composite_type_dependencies(rel->rd_rel->reltype, --- 4287,4293 ---- */ if (isOid) { ! tab->worklevel = WORK_REWRITE; /* See comments in ATExecDropColumn. */ find_composite_type_dependencies(rel->rd_rel->reltype, *************** *** 4967,4973 **** ATExecDropColumn(List **wqueue, Relation rel, const char *colName, tab = ATGetQueueEntry(wqueue, rel); /* Tell Phase 3 to physically remove the OID column */ ! tab->new_changeoids = true; /* * We would also need to rewrite all tables using this table's rowtype --- 4966,4972 ---- tab = ATGetQueueEntry(wqueue, rel); /* Tell Phase 3 to physically remove the OID column */ ! tab->worklevel = WORK_REWRITE; /* * We would also need to rewrite all tables using this table's rowtype *************** *** 5000,5006 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel, /* 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); /* suppress notices when rebuilding existing index */ quiet = is_rebuild; --- 4999,5005 ---- /* 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->worklevel == WORK_REWRITE); /* suppress notices when rebuilding existing index */ quiet = is_rebuild; *************** *** 5139,5144 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, --- 5138,5144 ---- newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr); tab->constraints = lappend(tab->constraints, newcon); + tab->worklevel = Max(tab->worklevel, WORK_SCAN); /* Save the actually assigned name if it was defaulted */ if (constr->conname == NULL) *************** *** 5485,5490 **** ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, --- 5485,5494 ---- newcon->qual = (Node *) fkconstraint; tab->constraints = lappend(tab->constraints, newcon); + /* + * No need to set tab->worklevel; foreign key validation is a distinct + * aspect of Phase 3. + */ } /* *************** *** 6369,6374 **** ATPrepAlterColumnType(List **wqueue, --- 6373,6379 ---- newval->expr = (Expr *) transform; tab->newvals = lappend(tab->newvals, newval); + tab->worklevel = WORK_REWRITE; /* * If we need to rewrite or scan this table, tables using its rowtype as