diff options
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 332 |
1 files changed, 147 insertions, 185 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 40a790ca17e..8023ba83420 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,13 +8,12 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.52 2002/11/09 23:56:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.53 2002/11/11 22:19:21 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/xact.h" #include "access/genam.h" #include "access/tuptoaster.h" #include "catalog/catalog.h" @@ -52,7 +51,27 @@ #include "utils/relcache.h" #include "utils/syscache.h" -static List *temprels = NIL; + +/* + * ON COMMIT action list + */ +typedef struct OnCommitItem +{ + Oid relid; /* relid of relation */ + OnCommitAction oncommit; /* what to do at end of xact */ + + /* + * If this entry was created during this xact, it should be deleted at + * xact abort. Conversely, if this entry was deleted during this + * xact, it should be removed at xact commit. We leave deleted + * entries in the list until commit so that we can roll back if needed. + */ + bool created_in_cur_xact; + bool deleted_in_cur_xact; +} OnCommitItem; + +static List *on_commits = NIL; + static List *MergeAttributes(List *schema, List *supers, bool istemp, List **supOids, List **supconstr, bool *supHasOids); @@ -118,7 +137,6 @@ DefineRelation(CreateStmt *stmt, char relkind) int i; AttrNumber attnum; - /* * Truncate relname to appropriate length (probably a waste of time, * as parser should have done this already). @@ -126,6 +144,12 @@ DefineRelation(CreateStmt *stmt, char relkind) StrNCpy(relname, stmt->relation->relname, NAMEDATALEN); /* + * Check consistency of arguments + */ + if (stmt->oncommit != ONCOMMIT_NOOP && !stmt->relation->istemp) + elog(ERROR, "ON COMMIT can only be used on TEMP tables"); + + /* * Look up the namespace in which we are supposed to create the * relation. Check we have permission to create there. Skip check if * bootstrapping, since permissions machinery may not be working yet. @@ -225,7 +249,7 @@ DefineRelation(CreateStmt *stmt, char relkind) descriptor, relkind, false, - stmt->ateoxact, + stmt->oncommit, allowSystemTableMods); StoreCatalogInheritance(relationId, inheritOids); @@ -333,20 +357,16 @@ RemoveRelation(const RangeVar *relation, DropBehavior behavior) /* * TruncateRelation - * Removes all the rows from a relation - * - * Exceptions: - * BadArg if name is invalid + * Removes all the rows from a relation. * - * Note: - * Rows are removed, indexes are truncated and reconstructed. + * Note: This routine only does safety and permissions checks; + * heap_truncate does the actual work. */ void TruncateRelation(const RangeVar *relation) { Relation rel; Oid relid; - Oid toastrelid; ScanKeyData key; Relation fkeyRel; SysScanDesc fkeyScan; @@ -426,17 +446,11 @@ TruncateRelation(const RangeVar *relation) systable_endscan(fkeyScan); heap_close(fkeyRel, AccessShareLock); - toastrelid = rel->rd_rel->reltoastrelid; - /* Keep the lock until transaction commit */ heap_close(rel, NoLock); - /* Truncate the table proper */ + /* Do the real work */ heap_truncate(relid); - - /* If it has a toast table, truncate that too */ - if (OidIsValid(toastrelid)) - heap_truncate(toastrelid); } /*---------- @@ -3787,18 +3801,12 @@ AlterTableCreateToastTable(Oid relOid, bool silent) * when its master is, so there's no need to handle the toast rel as * temp. */ - - /* - * Pass ATEOXACTNOOP for ateoxact since we want heap_drop_with_catalog() - * to remove TOAST tables for temp tables, not AtEOXact_temp_relations() - */ - toast_relid = heap_create_with_catalog(toast_relname, PG_TOAST_NAMESPACE, tupdesc, RELKIND_TOASTVALUE, shared_relation, - ATEOXACTNOOP, + ONCOMMIT_NOOP, true); /* make the toast relation visible, else index creation will fail */ @@ -3934,205 +3942,159 @@ needs_toast_table(Relation rel) return (tuple_length > TOAST_TUPLE_THRESHOLD); } + /* - * To handle ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS } + * This code supports + * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS } + * + * Because we only support this for TEMP tables, it's sufficient to remember + * the state in a backend-local data structure. + */ + +/* + * Register a newly-created relation's ON COMMIT action. */ void -AtEOXact_temp_relations(bool iscommit, int bstate) +register_on_commit_action(Oid relid, OnCommitAction action) { - List *l, - *prev; - MemoryContext oldctx; - - if (temprels == NIL) - return; + OnCommitItem *oc; + MemoryContext oldcxt; /* - * These loops are tricky because we are removing items from the List - * while we are traversing it. + * We needn't bother registering the relation unless there is an ON COMMIT + * action we need to take. */ - - - /* Remove 'dead' entries on commit and clear 'dead' status on abort */ - l = temprels; - prev = NIL; - while (l != NIL) - { - TempTable *t = lfirst(l); - - if (t->dead) - { - if (iscommit) - { - /* Remove from temprels, since the user has DROP'd */ - oldctx = MemoryContextSwitchTo(CacheMemoryContext); - if (prev == NIL) - { - pfree(t); - temprels = lnext(l); - pfree(l); - l = temprels; - } - else - { - pfree(t); - lnext(prev) = lnext(l); - pfree(l); - l = lnext(prev); - } - MemoryContextSwitchTo(oldctx); - continue; - } - else - /* user dropped but now we're aborted */ - t->dead = false; - } - prev = l; - l = lnext(l); - } - - if ((iscommit && bstate != TBLOCK_END) || - (!iscommit && bstate != TBLOCK_ABORT)) + if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS) return; - /* Perform per-xact actions */ - l = temprels; - prev = NIL; - - if (iscommit) - { - while (l != NIL) - { - TempTable *t = lfirst(l); + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - if (t->ateoxact == ATEOXACTDROP) - { - ObjectAddress object; + oc = (OnCommitItem *) palloc(sizeof(OnCommitItem)); + oc->relid = relid; + oc->oncommit = action; + oc->created_in_cur_xact = true; + oc->deleted_in_cur_xact = false; - object.classId = RelOid_pg_class; - object.objectId = t->relid; - object.objectSubId = 0; + on_commits = lcons(oc, on_commits); - performDeletion(&object, DROP_CASCADE); - oldctx = MemoryContextSwitchTo(CacheMemoryContext); + MemoryContextSwitchTo(oldcxt); +} - if (prev == NIL) - { - pfree(t); - temprels = lnext(l); - pfree(l); - l = temprels; - } - else - { - pfree(t); - lnext(prev) = lnext(l); - pfree(l); - l = lnext(prev); - } +/* + * Unregister any ON COMMIT action when a relation is deleted. + * + * Actually, we only mark the OnCommitItem entry as to be deleted after commit. + */ +void +remove_on_commit_action(Oid relid) +{ + List *l; - MemoryContextSwitchTo(oldctx); - CommandCounterIncrement(); - continue; - } - else if (t->ateoxact == ATEOXACTDELETE) - { - heap_truncate(t->relid); - CommandCounterIncrement(); - } - prev = l; - l = lnext(l); - } - } - else + foreach(l, on_commits) { - /* Abort --- remove entries added by this xact */ - TransactionId curtid = GetCurrentTransactionId(); - - oldctx = MemoryContextSwitchTo(CacheMemoryContext); + OnCommitItem *oc = (OnCommitItem *) lfirst(l); - while (l != NIL) + if (oc->relid == relid) { - TempTable *t = lfirst(l); - - if (t->tid == curtid) - { - if (prev == NIL) - { - pfree(t); - temprels = lnext(l); - pfree(l); - l = temprels; - } - else - { - pfree(t); - lnext(prev) = lnext(l); - pfree(l); - l = lnext(prev); - } - continue; - } - prev = l; - l = lnext(l); + oc->deleted_in_cur_xact = true; + break; } - MemoryContextSwitchTo(oldctx); } } /* - * Register a temp rel in temprels + * Perform ON COMMIT actions. + * + * This is invoked just before actually committing, since it's possible + * to encounter errors. */ - void -reg_temp_rel(TempTable * t) +PreCommit_on_commit_actions(void) { - temprels = lcons(t, temprels); -} + List *l; -/* - * return the ON COMMIT/ateoxact value for a given temp rel - */ + foreach(l, on_commits) + { + OnCommitItem *oc = (OnCommitItem *) lfirst(l); -void -free_temp_rels(void) -{ - MemoryContext oldctx; + /* Ignore entry if already dropped in this xact */ + if (oc->deleted_in_cur_xact) + continue; - oldctx = MemoryContextSwitchTo(CacheMemoryContext); - while (temprels != NIL) - { - List *l = temprels; + switch (oc->oncommit) + { + case ONCOMMIT_NOOP: + case ONCOMMIT_PRESERVE_ROWS: + /* Do nothing (there shouldn't be such entries, actually) */ + break; + case ONCOMMIT_DELETE_ROWS: + heap_truncate(oc->relid); + CommandCounterIncrement(); /* XXX needed? */ + break; + case ONCOMMIT_DROP: + { + ObjectAddress object; - temprels = lnext(temprels); - pfree(lfirst(l)); - pfree(l); + object.classId = RelOid_pg_class; + object.objectId = oc->relid; + object.objectSubId = 0; + performDeletion(&object, DROP_CASCADE); + /* + * Note that table deletion will call remove_on_commit_action, + * so the entry should get marked as deleted. + */ + Assert(oc->deleted_in_cur_xact); + break; + } + } } - MemoryContextSwitchTo(oldctx); } /* - * Remove (actually just mark for deletion, in case we abort) - * Relid from the temprels list + * Post-commit or post-abort cleanup for ON COMMIT management. + * + * All we do here is remove no-longer-needed OnCommitItem entries. + * + * During commit, remove entries that were deleted during this transaction; + * during abort, remove those created during this transaction. */ - void -rm_temp_rel(Oid relid) +AtEOXact_on_commit_actions(bool isCommit) { - List *l; + List *l, + *prev; - foreach(l, temprels) + prev = NIL; + l = on_commits; + while (l != NIL) { - TempTable *t = lfirst(l); + OnCommitItem *oc = (OnCommitItem *) lfirst(l); - if (t->relid == relid) + if (isCommit ? oc->deleted_in_cur_xact : + oc->created_in_cur_xact) { - t->dead = true; - return; + /* This entry must be removed */ + if (prev != NIL) + { + lnext(prev) = lnext(l); + pfree(l); + l = lnext(prev); + } + else + { + on_commits = lnext(l); + pfree(l); + l = on_commits; + } + pfree(oc); + } + else + { + /* This entry must be preserved */ + oc->created_in_cur_xact = false; + oc->deleted_in_cur_xact = false; + prev = l; + l = lnext(l); } } - - /* If we get here, we're in trouble */ - Assert(1==1); } - |