From: Emmanuel Cecchet <manu@frogthinker.org>
To: David Fetter <david@fetter.org>
Cc: Heikki Linnakangas <heikki.linnakangas@enterprisedb.com>
Cc: pgsql-hackers@postgresql.org
Subject: Re: Transactions and temp tables
Date: Tue, 07 Oct 2008 22:02:48 -0400
Message-ID: <48EC14C8.4000805@frogthinker.org> (raw)
In-Reply-To: <20081007223757.GA6508@fetter.org>
References: <48EA7B12.6050906@frogthinker.org>
<48EB0551.50205@enterprisedb.com>
<48EB95B7.10604@frogthinker.org>
<48EBCD41.7070409@frogthinker.org>
<20081007215024.GL16846@fetter.org>
<48EBDEBE.4040601@frogthinker.org>
<20081007223757.GA6508@fetter.org>
Hi,
Here are the patches for 8.4 (1 patch for the code and 1 patch for the
regression tests).
Thanks in advance for your feedback,
Emmanuel
David Fetter wrote:
> On Tue, Oct 07, 2008 at 06:12:14PM -0400, Emmanuel Cecchet wrote:
>
>> Hi,
>>
>>
>>> On Tue, Oct 07, 2008 at 04:57:37PM -0400, Emmanuel Cecchet wrote:
>>>
>>>
>>>> Heikki,
>>>>
>>>> Here is a new version of the patch using a hash table as you
>>>> suggested. I also include the tests that I have added to the
>>>> regression test suite to test the various scenarios. All patches
>>>> are based on Postgres 8.3.3, let me know if you want me to
>>>> generate patch for 8.4.
>>>>
>>> CVS TIP is the only place where new features, like this, go :)
>>>
>>>
>> I looked at the Wiki and it looks like I should add en entry
>> (assuming I get a patch for the current CVS HEAD) to CommitFest
>> 2008-11. Is that correct?
>>
>>
>>> I didn't see the attachment anyhow...
>>>
>>>
>> Good point! The same with the attachments now!
>>
>
> Perhaps we did not make this clear. The patch is a new feature. New
> features are not going into 8.3, as it has already been released.
>
> Make a patch against CVS TIP aka 8.4, and re-submit.
>
> Cheers,
> David
>
--
Emmanuel Cecchet
FTO @ Frog Thinker
Open Source Development & Consulting
--
Web: http://www.frogthinker.org
email: manu@frogthinker.org
Skype: emmanuel_cecchet
Attachments:patch-2pc-regression-test-suite-8.4.txt (text/plain, 17.7K) download
### Eclipse Workspace Patch 1.0
#P Postgres-HEAD
Index: src/test/regress/parallel_schedule
===================================================================
RCS file: /root/cvsrepo/pgsql/src/test/regress/parallel_schedule,v
retrieving revision 1.46
diff -u -r1.46 parallel_schedule
--- src/test/regress/parallel_schedule 24 Nov 2007 19:49:23 -0000 1.46
+++ src/test/regress/parallel_schedule 7 Oct 2008 23:41:14 -0000
@@ -90,3 +90,11 @@
# run tablespace by itself
test: tablespace
+
+# --------
+# 2PC related tests
+# --------
+test: 2pc_persistent_table 2pc_on_delete_rows_transaction 2pc_on_commit_drop 2pc_temp_table_transaction 2pc_temp_table_rollback 2pc_temp_table_savepoint 2pc_temp_table_failure
+test: 2pc_dirty_buffer_check
+test: 2pc_temp_table_session
+test: 2pc_on_delete_rows_session
\ No newline at end of file
Index: src/test/regress/serial_schedule
===================================================================
RCS file: /root/cvsrepo/pgsql/src/test/regress/serial_schedule,v
retrieving revision 1.43
diff -u -r1.43 serial_schedule
--- src/test/regress/serial_schedule 24 Nov 2007 20:41:35 -0000 1.43
+++ src/test/regress/serial_schedule 7 Oct 2008 23:41:14 -0000
@@ -115,3 +115,13 @@
test: xml
test: stats
test: tablespace
+test: 2pc_persistent_table
+test: 2pc_on_delete_rows_transaction
+test: 2pc_on_commit_drop
+test: 2pc_temp_table_transaction
+test: 2pc_temp_table_rollback
+test: 2pc_temp_table_savepoint
+test: 2pc_dirty_buffer_check
+test: 2pc_temp_table_session
+test: 2pc_on_delete_rows_session
+test: 2pc_temp_table_failure
\ No newline at end of file
Index: src/test/regress/sql/2pc_on_delete_rows_transaction.sql
===================================================================
RCS file: src/test/regress/sql/2pc_on_delete_rows_transaction.sql
diff -N src/test/regress/sql/2pc_on_delete_rows_transaction.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_on_delete_rows_transaction.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+-- Temp table with ON DELETE ROWS option (transaction scope)
+begin;
+create temp table foo(x int) on commit delete rows;
+insert into foo values(1);
+prepare transaction 'onCommitDeleteRowsTempTableShouldSucceed';
+commit prepared 'onCommitDeleteRowsTempTableShouldSucceed';
+drop table foo;
Index: src/test/regress/expected/2pc_on_commit_drop.out
===================================================================
RCS file: src/test/regress/expected/2pc_on_commit_drop.out
diff -N src/test/regress/expected/2pc_on_commit_drop.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_on_commit_drop.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,6 @@
+-- Temp table with ON COMMIT DROP option
+begin;
+create temp table foo(x int) on commit drop;
+insert into foo values(1);
+prepare transaction 'onCommitDropTempTableShouldSucceed';
+commit prepared 'onCommitDropTempTableShouldSucceed';
Index: src/test/regress/expected/2pc_temp_table_transaction.out
===================================================================
RCS file: src/test/regress/expected/2pc_temp_table_transaction.out
diff -N src/test/regress/expected/2pc_temp_table_transaction.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_temp_table_transaction.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+-- Transaction-scope dropped temp table use case
+begin;
+create temp table foo(x int);
+insert into foo values(1);
+drop table foo;
+prepare transaction 'dropTempTableShouldSucceed';
+commit prepared 'dropTempTableShouldSucceed';
Index: src/test/regress/sql/2pc_temp_table_failure.sql
===================================================================
RCS file: src/test/regress/sql/2pc_temp_table_failure.sql
diff -N src/test/regress/sql/2pc_temp_table_failure.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_temp_table_failure.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,6 @@
+-- Existing non-empty temp table at commit time should still fail
+begin;
+create temp table foo(x int);
+insert into foo values(1);
+prepare transaction 'existingTempTableShouldFail';
+commit prepared 'existingTempTableShouldFail';
Index: src/test/regress/expected/2pc_temp_table_savepoint.out
===================================================================
RCS file: src/test/regress/expected/2pc_temp_table_savepoint.out
diff -N src/test/regress/expected/2pc_temp_table_savepoint.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_temp_table_savepoint.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,8 @@
+-- Rollback to savepoint test case
+BEGIN;
+SAVEPOINT sp;
+CREATE TEMP TABLE foo(bar int4);
+INSERT INTO foo VALUES (1);
+ROLLBACK TO sp;
+PREPARE TRANSACTION 'savepointTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
+COMMIT PREPARED 'savepointTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
Index: src/test/regress/sql/2pc_temp_table_transaction.sql
===================================================================
RCS file: src/test/regress/sql/2pc_temp_table_transaction.sql
diff -N src/test/regress/sql/2pc_temp_table_transaction.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_temp_table_transaction.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+-- Transaction-scope dropped temp table use case
+begin;
+create temp table foo(x int);
+insert into foo values(1);
+drop table foo;
+prepare transaction 'dropTempTableShouldSucceed';
+commit prepared 'dropTempTableShouldSucceed';
Index: src/test/regress/expected/2pc_temp_table_failure.out
===================================================================
RCS file: src/test/regress/expected/2pc_temp_table_failure.out
diff -N src/test/regress/expected/2pc_temp_table_failure.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_temp_table_failure.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,8 @@
+-- Existing non-empty temp table at commit time should still fail
+begin;
+create temp table foo(x int);
+insert into foo values(1);
+prepare transaction 'existingTempTableShouldFail';
+ERROR: cannot PREPARE a transaction that has operated on temporary tables that are not empty at commit time
+commit prepared 'existingTempTableShouldFail';
+ERROR: prepared transaction with identifier "existingTempTableShouldFail" does not exist
Index: src/test/regress/sql/2pc_temp_table_session.sql
===================================================================
RCS file: src/test/regress/sql/2pc_temp_table_session.sql
diff -N src/test/regress/sql/2pc_temp_table_session.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_temp_table_session.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,8 @@
+-- Session-scope temp table use case
+create temp table foo(x int);
+begin;
+insert into foo values(1);
+delete from foo;
+prepare transaction 'dropTempTableShouldSucceed';
+commit prepared 'dropTempTableShouldSucceed';
+drop table foo;
Index: src/test/regress/expected/2pc_dirty_buffer_check.out
===================================================================
RCS file: src/test/regress/expected/2pc_dirty_buffer_check.out
diff -N src/test/regress/expected/2pc_dirty_buffer_check.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_dirty_buffer_check.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,52 @@
+-- 2PC Dirty buffer check
+begin;
+create temp table foo(a int, b int, c int) on commit drop;
+insert into foo values(1,1,1);
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+prepare transaction 'bcd';
+commit prepared 'bcd';
+begin;
+create temp table bar(a int, b int, c int) on commit drop;
+insert into bar values(1,1,1);
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+commit;
Index: src/test/regress/sql/2pc_on_commit_drop.sql
===================================================================
RCS file: src/test/regress/sql/2pc_on_commit_drop.sql
diff -N src/test/regress/sql/2pc_on_commit_drop.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_on_commit_drop.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,6 @@
+-- Temp table with ON COMMIT DROP option
+begin;
+create temp table foo(x int) on commit drop;
+insert into foo values(1);
+prepare transaction 'onCommitDropTempTableShouldSucceed';
+commit prepared 'onCommitDropTempTableShouldSucceed';
Index: src/test/regress/sql/2pc_temp_table_savepoint.sql
===================================================================
RCS file: src/test/regress/sql/2pc_temp_table_savepoint.sql
diff -N src/test/regress/sql/2pc_temp_table_savepoint.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_temp_table_savepoint.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,8 @@
+-- Rollback to savepoint test case
+BEGIN;
+SAVEPOINT sp;
+CREATE TEMP TABLE foo(bar int4);
+INSERT INTO foo VALUES (1);
+ROLLBACK TO sp;
+PREPARE TRANSACTION 'savepointTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
+COMMIT PREPARED 'savepointTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
Index: src/test/regress/sql/2pc_temp_table_rollback.sql
===================================================================
RCS file: src/test/regress/sql/2pc_temp_table_rollback.sql
diff -N src/test/regress/sql/2pc_temp_table_rollback.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_temp_table_rollback.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+-- Rollback prepared
+BEGIN;
+CREATE TEMP TABLE foo(bar int4);
+PREPARE TRANSACTION 'rollbackTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
+ROLLBACK PREPARED 'rollbackTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
Index: src/test/regress/expected/2pc_on_delete_rows_session.out
===================================================================
RCS file: src/test/regress/expected/2pc_on_delete_rows_session.out
diff -N src/test/regress/expected/2pc_on_delete_rows_session.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_on_delete_rows_session.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+-- Temp table with ON DELETE ROWS option (session scope)
+create temp table foo(x int) on commit delete rows;
+begin;
+insert into foo values(1);
+prepare transaction 'onCommitDeleteRowsTempTableShouldSucceed';
+commit prepared 'onCommitDeleteRowsTempTableShouldSucceed';
+drop table foo;
Index: src/test/regress/sql/2pc_persistent_table.sql
===================================================================
RCS file: src/test/regress/sql/2pc_persistent_table.sql
diff -N src/test/regress/sql/2pc_persistent_table.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_persistent_table.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,12 @@
+-- Creation of a persistent table (not temp)
+begin;
+create table paul(x int);
+insert into paul values(1);
+prepare transaction 'persistentTableShouldSucceed';
+commit prepared 'persistentTableShouldSucceed';
+
+-- Drop of a persistent table (not temp)
+begin;
+drop table paul;
+prepare transaction 'dropPersistentTableShouldSucceed';
+commit prepared 'dropPersistentTableShouldSucceed';
Index: src/test/regress/expected/2pc_temp_table_rollback.out
===================================================================
RCS file: src/test/regress/expected/2pc_temp_table_rollback.out
diff -N src/test/regress/expected/2pc_temp_table_rollback.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_temp_table_rollback.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+-- Rollback prepared
+BEGIN;
+CREATE TEMP TABLE foo(bar int4);
+PREPARE TRANSACTION 'rollbackTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
+ROLLBACK PREPARED 'rollbackTestCase_archives.postgresql.org_pgsql-hackers_2008-03_msg00017.php';
Index: src/test/regress/sql/2pc_dirty_buffer_check.sql
===================================================================
RCS file: src/test/regress/sql/2pc_dirty_buffer_check.sql
diff -N src/test/regress/sql/2pc_dirty_buffer_check.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_dirty_buffer_check.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,52 @@
+-- 2PC Dirty buffer check
+begin;
+create temp table foo(a int, b int, c int) on commit drop;
+insert into foo values(1,1,1);
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+insert into foo select * from foo;
+prepare transaction 'bcd';
+commit prepared 'bcd';
+begin;
+create temp table bar(a int, b int, c int) on commit drop;
+insert into bar values(1,1,1);
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+insert into bar select * from bar;
+commit;
Index: src/test/regress/expected/2pc_persistent_table.out
===================================================================
RCS file: src/test/regress/expected/2pc_persistent_table.out
diff -N src/test/regress/expected/2pc_persistent_table.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_persistent_table.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,11 @@
+-- Creation of a persistent table (not temp)
+begin;
+create table paul(x int);
+insert into paul values(1);
+prepare transaction 'persistentTableShouldSucceed';
+commit prepared 'persistentTableShouldSucceed';
+-- Drop of a persistent table (not temp)
+begin;
+drop table paul;
+prepare transaction 'dropPersistentTableShouldSucceed';
+commit prepared 'dropPersistentTableShouldSucceed';
Index: src/test/regress/expected/2pc_temp_table_session.out
===================================================================
RCS file: src/test/regress/expected/2pc_temp_table_session.out
diff -N src/test/regress/expected/2pc_temp_table_session.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_temp_table_session.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,8 @@
+-- Session-scope temp table use case
+create temp table foo(x int);
+begin;
+insert into foo values(1);
+delete from foo;
+prepare transaction 'dropTempTableShouldSucceed';
+commit prepared 'dropTempTableShouldSucceed';
+drop table foo;
Index: src/test/regress/sql/2pc_on_delete_rows_session.sql
===================================================================
RCS file: src/test/regress/sql/2pc_on_delete_rows_session.sql
diff -N src/test/regress/sql/2pc_on_delete_rows_session.sql
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/sql/2pc_on_delete_rows_session.sql 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+-- Temp table with ON DELETE ROWS option (session scope)
+create temp table foo(x int) on commit delete rows;
+begin;
+insert into foo values(1);
+prepare transaction 'onCommitDeleteRowsTempTableShouldSucceed';
+commit prepared 'onCommitDeleteRowsTempTableShouldSucceed';
+drop table foo;
Index: src/test/regress/expected/2pc_on_delete_rows_transaction.out
===================================================================
RCS file: src/test/regress/expected/2pc_on_delete_rows_transaction.out
diff -N src/test/regress/expected/2pc_on_delete_rows_transaction.out
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/test/regress/expected/2pc_on_delete_rows_transaction.out 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+-- Temp table with ON DELETE ROWS option (transaction scope)
+begin;
+create temp table foo(x int) on commit delete rows;
+insert into foo values(1);
+prepare transaction 'onCommitDeleteRowsTempTableShouldSucceed';
+commit prepared 'onCommitDeleteRowsTempTableShouldSucceed';
+drop table foo;
patch-2pc-temp-table-8.4.txt (text/plain, 6.5K) download
### 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.264
diff -u -r1.264 heapam.c
--- src/backend/access/heap/heapam.c 30 Sep 2008 10:52:10 -0000 1.264
+++ src/backend/access/heap/heapam.c 8 Oct 2008 01:53:44 -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.265
diff -u -r1.265 xact.c
--- src/backend/access/transam/xact.c 11 Aug 2008 11:05:10 -0000 1.265
+++ src/backend/access/transam/xact.c 8 Oct 2008 01:53:44 -0000
@@ -47,6 +47,7 @@
#include "utils/memutils.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
+#include "utils/tqual.h"
#include "utils/xml.h"
#include "pg_trace.h"
@@ -65,13 +66,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 +203,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
*/
@@ -1511,7 +1508,6 @@
XactIsoLevel = DefaultXactIsoLevel;
XactReadOnly = DefaultXactReadOnly;
forceSyncCommit = false;
- MyXactAccessedTempRel = false;
/*
* reinitialize within-transaction counters
@@ -1777,6 +1773,36 @@
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)
+{
+ /*
+ * 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
+ Oid *tid = hash_search(accessedTempRel, &relationId, HASH_ENTER, NULL);
+ *tid = relationId;
+}
/*
* PrepareTransaction
@@ -1853,14 +1879,45 @@
* 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. We
+ * will only accept empty temp tables and throw an error
+ * in other cases. */
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ scan = heap_beginscan(tempTable, SnapshotNow, 0, NULL);
+ if ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ 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 empty at commit time")));
+ }
+ heap_endscan(scan);
+ relation_close(tempTable, NoLock);
+ }
+ }
+ hash_seq_term(&status);
+ hash_destroy(accessedTempRel);
+ accessedTempRel = NULL;
+ }
/* Prevent cancel/die interrupt while cleaning up */
HOLD_INTERRUPTS();
@@ -2151,6 +2208,12 @@
elog(FATAL, "CleanupTransaction: unexpected state %s",
TransStateAsString(s->state));
+ if (accessedTempRel != NULL)
+ {
+ hash_destroy(accessedTempRel);
+ accessedTempRel = NULL;
+ }
+
/*
* 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 8 Oct 2008 01:53:44 -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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: manu@frogthinker.org, david@fetter.org, heikki.linnakangas@enterprisedb.com
Subject: Re: Transactions and temp tables
In-Reply-To: <48EC14C8.4000805@frogthinker.org>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox