### Eclipse Workspace Patch 1.0 #P Postgres-HEAD Index: src/backend/access/heap/heapam.c =================================================================== RCS file: /root/cvsrepo/pgsql/src/backend/access/heap/heapam.c,v retrieving revision 1.268 diff -u -r1.268 heapam.c --- src/backend/access/heap/heapam.c 31 Oct 2008 19:40:26 -0000 1.268 +++ src/backend/access/heap/heapam.c 7 Nov 2008 23:09:11 -0000 @@ -878,7 +878,7 @@ /* Make note that we've accessed a temporary relation */ if (r->rd_istemp) - MyXactAccessedTempRel = true; + enlistRelationIdFor2PCChecks(relationId); pgstat_initstats(r); @@ -926,7 +926,7 @@ /* Make note that we've accessed a temporary relation */ if (r->rd_istemp) - MyXactAccessedTempRel = true; + enlistRelationIdFor2PCChecks(relationId); pgstat_initstats(r); @@ -976,7 +976,7 @@ /* Make note that we've accessed a temporary relation */ if (r->rd_istemp) - MyXactAccessedTempRel = true; + enlistRelationIdFor2PCChecks(relationId); pgstat_initstats(r); Index: src/backend/access/transam/xact.c =================================================================== RCS file: /root/cvsrepo/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.266 diff -u -r1.266 xact.c --- src/backend/access/transam/xact.c 20 Oct 2008 19:18:18 -0000 1.266 +++ src/backend/access/transam/xact.c 7 Nov 2008 23:09:11 -0000 @@ -65,13 +65,6 @@ int CommitDelay = 0; /* precommit delay in microseconds */ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ -/* - * MyXactAccessedTempRel is set when a temporary relation is accessed. - * We don't allow PREPARE TRANSACTION in that case. (This is global - * so that it can be set from heapam.c.) - */ -bool MyXactAccessedTempRel = false; - /* * transaction states - transaction state from server perspective @@ -209,6 +202,9 @@ */ static MemoryContext TransactionAbortContext = NULL; +/* Hash table containing Oids of accessed temporary relations */ +HTAB *accessedTempRel; + /* * List of add-on start- and end-of-xact callbacks */ @@ -250,6 +246,7 @@ SubTransactionId mySubid, SubTransactionId parentSubid); static void CleanupTransaction(void); +static void CleanupAccessedTempRel(void); static void CommitTransaction(void); static TransactionId RecordTransactionAbort(bool isSubXact); static void StartTransaction(void); @@ -623,7 +620,7 @@ /* Propagate new command ID into static snapshots */ SnapshotSetCommandId(currentCommandId); - + /* * Make any catalog changes done by the just-completed command * visible in the local syscache. We obviously don't need to do @@ -1110,10 +1107,10 @@ */ if (s->parent->childXids == NULL) new_childXids = - MemoryContextAlloc(TopTransactionContext, + MemoryContextAlloc(TopTransactionContext, new_maxChildXids * sizeof(TransactionId)); else - new_childXids = repalloc(s->parent->childXids, + new_childXids = repalloc(s->parent->childXids, new_maxChildXids * sizeof(TransactionId)); s->parent->childXids = new_childXids; @@ -1466,7 +1463,6 @@ XactIsoLevel = DefaultXactIsoLevel; XactReadOnly = DefaultXactReadOnly; forceSyncCommit = false; - MyXactAccessedTempRel = false; /* * reinitialize within-transaction counters @@ -1715,6 +1711,8 @@ AtCommit_Memory(); + CleanupAccessedTempRel(); + s->transactionId = InvalidTransactionId; s->subTransactionId = InvalidSubTransactionId; s->nestingLevel = 0; @@ -1732,6 +1730,50 @@ RESUME_INTERRUPTS(); } +/* ---------------- + * enlistRelationIdFor2PCChecks - enlist a relation in the list of + * resources to check at PREPARE COMMIT time if we are part of + * a 2PC transaction. The resource will be removed from the list + * if the table is dropped before commit. + * ---------------- + */ +void +enlistRelationIdFor2PCChecks(Oid relationId) +{ + Oid *tid; + + /* + * Each time a temporary relation is accessed, it is added to the + * accessedTempRel list. PREPARE TRANSACTION will fail if any + * of the accessed relation is still valid (not dropped). (This is + * called from from heapam.c.) + */ + if (accessedTempRel == NULL) + { // Allocate the list on-demand + HASHCTL ctl; + + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(Oid); + accessedTempRel = hash_create("accessed temp tables", 4, &ctl, + HASH_ELEM); + } + + // Add to the hash list if missing + tid = hash_search(accessedTempRel, &relationId, HASH_ENTER, NULL); + *tid = relationId; +} + +/* + * Cleanup the list of prepared temp tables that were accessed during this transaction. + */ +static void CleanupAccessedTempRel(void) +{ + if (accessedTempRel != NULL) + { + hash_destroy(accessedTempRel); + accessedTempRel = NULL; + } +} /* * PrepareTransaction @@ -1808,14 +1850,37 @@ * We must check this after executing any ON COMMIT actions, because * they might still access a temp relation. * - * XXX In principle this could be relaxed to allow some useful special - * cases, such as a temp table created and dropped all within the - * transaction. That seems to require much more bookkeeping though. + * We only allow to proceed further if the accessed temp tables have + * been dropped before PREPARE COMMIT. */ - if (MyXactAccessedTempRel) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot PREPARE a transaction that has operated on temporary tables"))); + if (accessedTempRel != NULL) + { + HASH_SEQ_STATUS status; + Oid* tempTableOid; + + /* Prevent further updates to the list as recommended in hash_seq_init doc. */ + hash_freeze(accessedTempRel); + hash_seq_init(&status, accessedTempRel); + while ((tempTableOid = (Oid *) hash_seq_search(&status)) != NULL) + { /* Check all accessed temp tables. If the table has been dropped, + * try_relation_open will fail and we can safely continue. */ + Relation tempTable = try_relation_open(*tempTableOid, NoLock); + + if (tempTable != NULL) + { // We have an open temp table at PREPARE COMMIT time throw an error + relation_close(tempTable, NoLock); + hash_seq_term(&status); + hash_destroy(accessedTempRel); + accessedTempRel = NULL; + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot PREPARE a transaction that has operated on temporary tables that are not dropped at commit time"))); + } + } + + hash_destroy(accessedTempRel); + accessedTempRel = NULL; + } /* Prevent cancel/die interrupt while cleaning up */ HOLD_INTERRUPTS(); @@ -2106,6 +2171,8 @@ elog(FATAL, "CleanupTransaction: unexpected state %s", TransStateAsString(s->state)); + CleanupAccessedTempRel(); + /* * do abort cleanup processing */ Index: src/include/access/xact.h =================================================================== RCS file: /root/cvsrepo/pgsql/src/include/access/xact.h,v retrieving revision 1.95 diff -u -r1.95 xact.h --- src/include/access/xact.h 11 Aug 2008 11:05:11 -0000 1.95 +++ src/include/access/xact.h 7 Nov 2008 23:09:11 -0000 @@ -18,6 +18,7 @@ #include "nodes/pg_list.h" #include "storage/relfilenode.h" #include "utils/timestamp.h" +#include "postgres_ext.h" /* @@ -44,8 +45,8 @@ /* Asynchronous commits */ extern bool XactSyncCommit; -/* Kluge for 2PC support */ -extern bool MyXactAccessedTempRel; +/* List of temp tables accessed during a transaction for 2PC support */ +extern void enlistRelationIdFor2PCChecks(Oid relationId); /* * start- and end-of-transaction callbacks for dynamically loaded modules