at1.3-tablecmds-enum.patch
text/plain
Filename: at1.3-tablecmds-enum.patch
Type: text/plain
Part: 2
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/tablecmds.c | 0 | 0 |
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,3265 **** 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;
! Oid OIDNewHeap;
! Oid NewTableSpace;
! 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,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("cannot rewrite system relation \"%s\"",
! 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,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 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;
! else
! NewTableSpace = OldHeap->rd_rel->reltablespace;
! 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);
}
}
--- 3183,3271 ----
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;
! Oid OIDNewHeap;
! Oid NewTableSpace;
! 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,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("cannot rewrite system relation \"%s\"",
! 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,
! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! 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;
! else
! NewTableSpace = OldHeap->rd_rel->reltablespace;
! 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,3580 **** 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\"",
! RelationGetRelationName(oldrel))));
! else
! ereport(DEBUG1,
! (errmsg("Verifying table \"%s\"",
! RelationGetRelationName(oldrel))));
! econtext = GetPerTupleExprContext(estate);
! /*
! * Make tuple slots for old and new tuples. Note that even when the
! * tuples are the same, the tupDescs might not be (consider ADD COLUMN
! * without a default).
! */
! oldslot = MakeSingleTupleTableSlot(oldTupDesc);
! newslot = MakeSingleTupleTableSlot(newTupDesc);
! /* Preallocate values/isnull arrays */
! i = Max(newTupDesc->natts, oldTupDesc->natts);
! values = (Datum *) palloc(i * sizeof(Datum));
! isnull = (bool *) palloc(i * sizeof(bool));
! memset(values, 0, i * sizeof(Datum));
! memset(isnull, true, i * sizeof(bool));
! /*
! * Any attributes that are dropped according to the new tuple
! * descriptor can be set to NULL. We precompute the list of dropped
! * attributes to avoid needing to do so in the per-tuple loop.
! */
! for (i = 0; i < newTupDesc->natts; i++)
! {
! if (newTupDesc->attrs[i]->attisdropped)
! dropped_attrs = lappend_int(dropped_attrs, i);
! }
! /*
! * Scan through the rows, generating a new row if needed and then
! * checking all the constraints.
! */
! scan = heap_beginscan(oldrel, SnapshotNow, 0, NULL);
! /*
! * Switch to per-tuple memory context and reset it for each tuple
! * produced, so we don't leak memory.
! */
! oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
! while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
! if (newrel)
! {
! Oid tupOid = InvalidOid;
! /* Extract data from old tuple */
! heap_deform_tuple(tuple, oldTupDesc, values, isnull);
! if (oldTupDesc->tdhasoid)
! tupOid = HeapTupleGetOid(tuple);
! /* Set dropped attributes to null in new tuple */
! foreach(lc, dropped_attrs)
! isnull[lfirst_int(lc)] = true;
! /*
! * Process supplied expressions to replace selected columns.
! * Expression inputs come from the old tuple.
! */
! ExecStoreTuple(tuple, oldslot, InvalidBuffer, false);
! econtext->ecxt_scantuple = oldslot;
! foreach(l, tab->newvals)
! {
! NewColumnValue *ex = lfirst(l);
! values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
! econtext,
! &isnull[ex->attnum - 1],
! NULL);
! }
! /*
! * Form the new tuple. Note that we don't explicitly pfree it,
! * since the per-tuple memory context will be reset shortly.
! */
! tuple = heap_form_tuple(newTupDesc, values, isnull);
! /* Preserve OID, if any */
! if (newTupDesc->tdhasoid)
! HeapTupleSetOid(tuple, tupOid);
! }
! /* Now check any constraints on the possibly-changed tuple */
! ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
! econtext->ecxt_scantuple = newslot;
! foreach(l, notnull_attrs)
! {
! int attn = lfirst_int(l);
! if (heap_attisnull(tuple, attn + 1))
! ereport(ERROR,
! (errcode(ERRCODE_NOT_NULL_VIOLATION),
! errmsg("column \"%s\" contains null values",
NameStr(newTupDesc->attrs[attn]->attname))));
! }
! foreach(l, tab->constraints)
! {
! NewConstraint *con = lfirst(l);
! switch (con->contype)
! {
! case CONSTR_CHECK:
! if (!ExecQual(con->qualstate, econtext, true))
! ereport(ERROR,
! (errcode(ERRCODE_CHECK_VIOLATION),
! errmsg("check constraint \"%s\" is violated by some row",
! con->name)));
! break;
! case CONSTR_FOREIGN:
! /* Nothing to do here */
! break;
! default:
! elog(ERROR, "unrecognized constraint type: %d",
! (int) con->contype);
! }
}
! /* Write the tuple out to the new relation */
! if (newrel)
! heap_insert(newrel, tuple, mycid, hi_options, bistate);
! ResetExprContext(econtext);
! CHECK_FOR_INTERRUPTS();
! }
! MemoryContextSwitchTo(oldCxt);
! heap_endscan(scan);
! ExecDropSingleTupleTableSlot(oldslot);
! ExecDropSingleTupleTableSlot(newslot);
! }
FreeExecutorState(estate);
--- 3428,3578 ----
!newTupDesc->attrs[i]->attisdropped)
notnull_attrs = lappend_int(notnull_attrs, i);
}
}
! if (newrel)
! ereport(DEBUG1,
! (errmsg("Rewriting table \"%s\"",
! RelationGetRelationName(oldrel))));
! else
! ereport(DEBUG1,
! (errmsg("Verifying table \"%s\"",
! RelationGetRelationName(oldrel))));
! econtext = GetPerTupleExprContext(estate);
! /*
! * Make tuple slots for old and new tuples. Note that even when the
! * tuples are the same, the tupDescs might not be (consider ADD COLUMN
! * without a default).
! */
! oldslot = MakeSingleTupleTableSlot(oldTupDesc);
! newslot = MakeSingleTupleTableSlot(newTupDesc);
! /* Preallocate values/isnull arrays */
! i = Max(newTupDesc->natts, oldTupDesc->natts);
! values = (Datum *) palloc(i * sizeof(Datum));
! isnull = (bool *) palloc(i * sizeof(bool));
! memset(values, 0, i * sizeof(Datum));
! memset(isnull, true, i * sizeof(bool));
! /*
! * Any attributes that are dropped according to the new tuple
! * descriptor can be set to NULL. We precompute the list of dropped
! * attributes to avoid needing to do so in the per-tuple loop.
! */
! for (i = 0; i < newTupDesc->natts; i++)
! {
! if (newTupDesc->attrs[i]->attisdropped)
! dropped_attrs = lappend_int(dropped_attrs, i);
! }
! /*
! * Scan through the rows, generating a new row if needed and then
! * checking all the constraints.
! */
! scan = heap_beginscan(oldrel, SnapshotNow, 0, NULL);
! /*
! * Switch to per-tuple memory context and reset it for each tuple
! * produced, so we don't leak memory.
! */
! oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
! while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
! {
! if (newrel)
{
! Oid tupOid = InvalidOid;
! /* Extract data from old tuple */
! heap_deform_tuple(tuple, oldTupDesc, values, isnull);
! if (oldTupDesc->tdhasoid)
! tupOid = HeapTupleGetOid(tuple);
! /* Set dropped attributes to null in new tuple */
! foreach(lc, dropped_attrs)
! isnull[lfirst_int(lc)] = true;
! /*
! * Process supplied expressions to replace selected columns.
! * Expression inputs come from the old tuple.
! */
! ExecStoreTuple(tuple, oldslot, InvalidBuffer, false);
! econtext->ecxt_scantuple = oldslot;
! foreach(l, tab->newvals)
! {
! NewColumnValue *ex = lfirst(l);
! values[ex->attnum - 1] = ExecEvalExpr(ex->exprstate,
! econtext,
! &isnull[ex->attnum - 1],
! NULL);
! }
! /*
! * Form the new tuple. Note that we don't explicitly pfree it,
! * since the per-tuple memory context will be reset shortly.
! */
! tuple = heap_form_tuple(newTupDesc, values, isnull);
! /* Preserve OID, if any */
! if (newTupDesc->tdhasoid)
! HeapTupleSetOid(tuple, tupOid);
! }
! /* Now check any constraints on the possibly-changed tuple */
! ExecStoreTuple(tuple, newslot, InvalidBuffer, false);
! econtext->ecxt_scantuple = newslot;
! foreach(l, notnull_attrs)
! {
! int attn = lfirst_int(l);
! if (heap_attisnull(tuple, attn + 1))
! ereport(ERROR,
! (errcode(ERRCODE_NOT_NULL_VIOLATION),
! errmsg("column \"%s\" contains null values",
NameStr(newTupDesc->attrs[attn]->attname))));
! }
! foreach(l, tab->constraints)
! {
! NewConstraint *con = lfirst(l);
! switch (con->contype)
! {
! case CONSTR_CHECK:
! if (!ExecQual(con->qualstate, econtext, true))
! ereport(ERROR,
! (errcode(ERRCODE_CHECK_VIOLATION),
! errmsg("check constraint \"%s\" is violated by some row",
! con->name)));
! break;
! case CONSTR_FOREIGN:
! /* Nothing to do here */
! break;
! default:
! elog(ERROR, "unrecognized constraint type: %d",
! (int) con->contype);
}
+ }
! /* Write the tuple out to the new relation */
! if (newrel)
! heap_insert(newrel, tuple, mycid, hi_options, bistate);
! ResetExprContext(econtext);
! CHECK_FOR_INTERRUPTS();
! }
! MemoryContextSwitchTo(oldCxt);
! heap_endscan(scan);
! ExecDropSingleTupleTableSlot(oldslot);
! ExecDropSingleTupleTableSlot(newslot);
FreeExecutorState(estate);
***************
*** 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