diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e1948b8bde249385c7b3802a57aaae590a484d59..03dccd47f5df37e8e11bd066b8d4e24de345197e 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.125 2007/06/03 17:05:29 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.126 2007/06/07 19:19:56 tgl Exp $ -->
 
 <chapter Id="runtime-config">
   <title>Server Configuration</title>
@@ -3510,16 +3510,22 @@ SELECT * FROM parent WHERE key = 2400;
        <para>
         The value is a list of names of tablespaces.  When there is more than
         one name in the list, <productname>PostgreSQL</> chooses a random
-        member of the list each time a temporary object is to be created.
+        member of the list each time a temporary object is to be created;
+        except that within a transaction, successively created temporary
+        objects are placed in successive tablespaces from the list.
+        If any element of the list is an empty string,
+        <productname>PostgreSQL</> will automatically use the default
+        tablespace of the current database instead.
        </para>
 
        <para>
-        If any element of the list is an empty string or does not match the
-        name of any existing tablespace, <productname>PostgreSQL</> will
-        automatically use the default tablespace of the current database
-        instead.  If a nondefault tablespace
-        is specified, the user must have <literal>CREATE</> privilege
-        for it, or creation attempts will fail.
+        When <varname>temp_tablespaces</> is set interactively, specifying a
+        nonexistent tablespace is an error, as is specifying a tablespace for
+        which the user does not have <literal>CREATE</> privilege.  However,
+        when using a previously set value, nonexistent tablespaces are
+        ignored, as are tablespaces for which the user lacks
+        <literal>CREATE</> privilege.  In particular, this rule applies when
+        using a value set in <filename>postgresql.conf</>.
        </para>
 
        <para>
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8201a4f33411317d68f51189a41ff7bf7b82bf44..d0dacf10782754e4b36905adaece541166b18b52 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.47 2007/06/03 17:06:59 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.48 2007/06/07 19:19:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,6 +63,7 @@
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 
 
 /* GUC variables */
@@ -72,7 +73,6 @@ char	   *temp_tablespaces = NULL;
 
 static bool remove_tablespace_directories(Oid tablespaceoid, bool redo);
 static void set_short_version(const char *path);
-static Oid	getTempTablespace(void);
 
 
 /*
@@ -921,9 +921,12 @@ GetDefaultTablespace(bool forTemp)
 {
 	Oid			result;
 
-	/* The temp-table case is handled by getTempTablespace() */
+	/* The temp-table case is handled elsewhere */
 	if (forTemp)
-		return getTempTablespace();
+	{
+		PrepareTempTablespaces();
+		return GetNextTempTableSpace();
+	}
 
 	/* Fast path for default_tablespace == "" */
 	if (default_tablespace == NULL || default_tablespace[0] == '\0')
@@ -958,7 +961,6 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
 {
 	char	   *rawname;
 	List	   *namelist;
-	ListCell   *l;
 
 	/* Need a modifiable copy of string */
 	rawname = pstrdup(newval);
@@ -975,24 +977,79 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
 	/*
 	 * If we aren't inside a transaction, we cannot do database access so
 	 * cannot verify the individual names.	Must accept the list on faith.
+	 * Fortunately, there's then also no need to pass the data to fd.c.
 	 */
-	if (source >= PGC_S_INTERACTIVE && IsTransactionState())
+	if (IsTransactionState())
 	{
+		/*
+		 * If we error out below, or if we are called multiple times in one
+		 * transaction, we'll leak a bit of TopTransactionContext memory.
+		 * Doesn't seem worth worrying about.
+		 */
+		Oid	   *tblSpcs;
+		int		numSpcs;
+		ListCell   *l;
+
+		tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
+									list_length(namelist) * sizeof(Oid));
+		numSpcs = 0;
 		foreach(l, namelist)
 		{
 			char	   *curname = (char *) lfirst(l);
+			Oid			curoid;
+			AclResult	aclresult;
 
 			/* Allow an empty string (signifying database default) */
 			if (curname[0] == '\0')
+			{
+				tblSpcs[numSpcs++] = InvalidOid;
 				continue;
+			}
 
 			/* Else verify that name is a valid tablespace name */
-			if (get_tablespace_oid(curname) == InvalidOid)
-				ereport(ERROR,
-						(errcode(ERRCODE_UNDEFINED_OBJECT),
-						 errmsg("tablespace \"%s\" does not exist",
-								curname)));
+			curoid = get_tablespace_oid(curname);
+			if (curoid == InvalidOid)
+			{
+				/*
+				 * In an interactive SET command, we ereport for bad info.
+				 * Otherwise, silently ignore any bad list elements.
+				 */
+				if (source >= PGC_S_INTERACTIVE)
+					ereport(ERROR,
+							(errcode(ERRCODE_UNDEFINED_OBJECT),
+							 errmsg("tablespace \"%s\" does not exist",
+									curname)));
+				continue;
+			}
+
+			/*
+			 * Allow explicit specification of database's default tablespace
+			 * in temp_tablespaces without triggering permissions checks.
+			 */
+			if (curoid == MyDatabaseTableSpace)
+			{
+				tblSpcs[numSpcs++] = InvalidOid;
+				continue;
+			}
+
+			/* Check permissions similarly */
+			aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
+											   ACL_CREATE);
+			if (aclresult != ACLCHECK_OK)
+			{
+				if (source >= PGC_S_INTERACTIVE)
+					aclcheck_error(aclresult, ACL_KIND_TABLESPACE, curname);
+				continue;
+			}
+
+			tblSpcs[numSpcs++] = curoid;
 		}
+
+		/* If actively "doing it", give the new list to fd.c */
+		if (doit)
+			SetTempTablespaces(tblSpcs, numSpcs);
+		else
+			pfree(tblSpcs);
 	}
 
 	pfree(rawname);
@@ -1002,69 +1059,34 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source)
 }
 
 /*
- * GetTempTablespace -- get the OID of the next temp tablespace to use
- *
- * May return InvalidOid to indicate "use the database's default tablespace".
+ * PrepareTempTablespaces -- prepare to use temp tablespaces
  *
- * This is different from GetDefaultTablespace(true) in just two ways:
- * 1. We check privileges here instead of leaving it to the caller.
- * 2. It's safe to call this outside a transaction (we just return InvalidOid).
- * The transaction state check is used so that this can be called from
- * low-level places that might conceivably run outside a transaction.
+ * If we have not already done so in the current transaction, parse the
+ * temp_tablespaces GUC variable and tell fd.c which tablespace(s) to use
+ * for temp files.
  */
-Oid
-GetTempTablespace(void)
-{
-	Oid			result;
-
-	/* Can't do catalog access unless within a transaction */
-	if (!IsTransactionState())
-		return InvalidOid;
-
-	/* OK, select a temp tablespace */
-	result = getTempTablespace();
-
-	/* Check permissions except when using database's default */
-	if (OidIsValid(result))
-	{
-		AclResult	aclresult;
-
-		aclresult = pg_tablespace_aclcheck(result, GetUserId(),
-										   ACL_CREATE);
-		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
-						   get_tablespace_name(result));
-	}
-
-	return result;
-}
-
-/*
- * getTempTablespace -- get the OID of the next temp tablespace to use
- *
- * This has exactly the API defined for GetDefaultTablespace(true),
- * in particular that caller is responsible for permissions checks.
- *
- * This exists to hide (and possibly optimize the use of) the
- * temp_tablespaces GUC variable.
- */
-static Oid
-getTempTablespace(void)
+void
+PrepareTempTablespaces(void)
 {
-	Oid			result;
 	char	   *rawname;
 	List	   *namelist;
-	int			nnames;
-	char	   *curname;
+	Oid		   *tblSpcs;
+	int			numSpcs;
+	ListCell   *l;
 
-	if (temp_tablespaces == NULL)
-		return InvalidOid;
+	/* No work if already done in current transaction */
+	if (TempTablespacesAreSet())
+		return;
 
 	/*
-	 * We re-parse the string on each call; this is a bit expensive, but
-	 * we don't expect this function will be called many times per query,
-	 * so it's probably not worth being tenser.
+	 * Can't do catalog access unless within a transaction.  This is just
+	 * a safety check in case this function is called by low-level code that
+	 * could conceivably execute outside a transaction.  Note that in such
+	 * a scenario, fd.c will fall back to using the current database's default
+	 * tablespace, which should always be OK.
 	 */
+	if (!IsTransactionState())
+		return;
 
 	/* Need a modifiable copy of string */
 	rawname = pstrdup(temp_tablespaces);
@@ -1073,51 +1095,60 @@ getTempTablespace(void)
 	if (!SplitIdentifierString(rawname, ',', &namelist))
 	{
 		/* syntax error in name list */
+		SetTempTablespaces(NULL, 0);
 		pfree(rawname);
 		list_free(namelist);
-		return InvalidOid;
+		return;
 	}
-	nnames = list_length(namelist);
 
-	/* Fast path for temp_tablespaces == "" */
-	if (nnames == 0)
+	/* Store tablespace OIDs in an array in TopTransactionContext */
+	tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext,
+									list_length(namelist) * sizeof(Oid));
+	numSpcs = 0;
+	foreach(l, namelist)
 	{
-		pfree(rawname);
-		list_free(namelist);
-		return InvalidOid;
-	}
+		char	   *curname = (char *) lfirst(l);
+		Oid			curoid;
+		AclResult	aclresult;
 
-	/* Select a random element */
-	if (nnames == 1)			/* no need for a random() call */
-		curname = (char *) linitial(namelist);
-	else
-		curname = (char *) list_nth(namelist, random() % nnames);
+		/* Allow an empty string (signifying database default) */
+		if (curname[0] == '\0')
+		{
+			tblSpcs[numSpcs++] = InvalidOid;
+			continue;
+		}
 
-	/*
-	 * Empty string means "database's default", else look up the tablespace.
-	 *
-	 * It is tempting to cache this lookup for more speed, but then we would
-	 * fail to detect the case where the tablespace was dropped since the GUC
-	 * variable was set.  Note also that we don't complain if the value fails
-	 * to refer to an existing tablespace; we just silently return InvalidOid,
-	 * causing the new object to be created in the database's tablespace.
-	 */
-	if (curname[0] == '\0')
-		result = InvalidOid;
-	else
-		result = get_tablespace_oid(curname);
+		/* Else verify that name is a valid tablespace name */
+		curoid = get_tablespace_oid(curname);
+		if (curoid == InvalidOid)
+		{
+			/* Silently ignore any bad list elements */
+			continue;
+		}
 
-	/*
-	 * Allow explicit specification of database's default tablespace in
-	 * temp_tablespaces without triggering permissions checks.
-	 */
-	if (result == MyDatabaseTableSpace)
-		result = InvalidOid;
+		/*
+		 * Allow explicit specification of database's default tablespace
+		 * in temp_tablespaces without triggering permissions checks.
+		 */
+		if (curoid == MyDatabaseTableSpace)
+		{
+			tblSpcs[numSpcs++] = InvalidOid;
+			continue;
+		}
+
+		/* Check permissions similarly */
+		aclresult = pg_tablespace_aclcheck(curoid, GetUserId(),
+										   ACL_CREATE);
+		if (aclresult != ACLCHECK_OK)
+			continue;
+
+		tblSpcs[numSpcs++] = curoid;
+	}
+
+	SetTempTablespaces(tblSpcs, numSpcs);
 
 	pfree(rawname);
 	list_free(namelist);
-
-	return result;
 }
 
 
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 348606e88bbfc338a39076e633427d3e5623e4ae..b5cabd81a4f28b33194f6ecaf2e0291f90b46c94 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.113 2007/06/03 17:07:14 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeHash.c,v 1.114 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -267,7 +267,6 @@ ExecHashTableCreate(Hash *node, List *hashOperators)
 	hashtable->totalTuples = 0;
 	hashtable->innerBatchFile = NULL;
 	hashtable->outerBatchFile = NULL;
-	hashtable->hashTblSpc = InvalidOid;
 	hashtable->spaceUsed = 0;
 	hashtable->spaceAllowed = work_mem * 1024L;
 
@@ -327,8 +326,8 @@ ExecHashTableCreate(Hash *node, List *hashOperators)
 		hashtable->outerBatchFile = (BufFile **)
 			palloc0(nbatch * sizeof(BufFile *));
 		/* The files will not be opened until needed... */
-		/* ... but we want to choose the tablespace only once */
-		hashtable->hashTblSpc = GetTempTablespace();
+		/* ... but make sure we have temp tablespaces established for them */
+		PrepareTempTablespaces();
 	}
 
 	/*
@@ -510,8 +509,8 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 			palloc0(nbatch * sizeof(BufFile *));
 		hashtable->outerBatchFile = (BufFile **)
 			palloc0(nbatch * sizeof(BufFile *));
-		/* time to choose the tablespace, too */
-		hashtable->hashTblSpc = GetTempTablespace();
+		/* time to establish the temp tablespaces, too */
+		PrepareTempTablespaces();
 	}
 	else
 	{
@@ -564,8 +563,7 @@ ExecHashIncreaseNumBatches(HashJoinTable hashtable)
 			{
 				/* dump it out */
 				Assert(batchno > curbatch);
-				ExecHashJoinSaveTuple(hashtable,
-									  HJTUPLE_MINTUPLE(tuple),
+				ExecHashJoinSaveTuple(HJTUPLE_MINTUPLE(tuple),
 									  tuple->hashvalue,
 									  &hashtable->innerBatchFile[batchno]);
 				/* and remove from hash table */
@@ -657,8 +655,7 @@ ExecHashTableInsert(HashJoinTable hashtable,
 		 * put the tuple into a temp file for later batches
 		 */
 		Assert(batchno > hashtable->curbatch);
-		ExecHashJoinSaveTuple(hashtable,
-							  tuple,
+		ExecHashJoinSaveTuple(tuple,
 							  hashvalue,
 							  &hashtable->innerBatchFile[batchno]);
 	}
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 495548ca82104ca344ed95f3dbf4f6c8743e1294..a07024585e9a857a4c07d2410928045b23b466fe 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.90 2007/06/03 17:07:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.91 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -223,8 +223,7 @@ ExecHashJoin(HashJoinState *node)
 				 * in the corresponding outer-batch file.
 				 */
 				Assert(batchno > hashtable->curbatch);
-				ExecHashJoinSaveTuple(hashtable,
-									  ExecFetchSlotMinimalTuple(outerTupleSlot),
+				ExecHashJoinSaveTuple(ExecFetchSlotMinimalTuple(outerTupleSlot),
 									  hashvalue,
 									  &hashtable->outerBatchFile[batchno]);
 				node->hj_NeedNewOuter = true;
@@ -755,8 +754,7 @@ start_over:
  * will get messed up.
  */
 void
-ExecHashJoinSaveTuple(HashJoinTable hashtable,
-					  MinimalTuple tuple, uint32 hashvalue,
+ExecHashJoinSaveTuple(MinimalTuple tuple, uint32 hashvalue,
 					  BufFile **fileptr)
 {
 	BufFile    *file = *fileptr;
@@ -765,7 +763,7 @@ ExecHashJoinSaveTuple(HashJoinTable hashtable,
 	if (file == NULL)
 	{
 		/* First write to this batch file, so open it. */
-		file = BufFileCreateTemp(false, hashtable->hashTblSpc);
+		file = BufFileCreateTemp(false);
 		*fileptr = file;
 	}
 
diff --git a/src/backend/storage/file/buffile.c b/src/backend/storage/file/buffile.c
index 6bb40558961b44db5d2ec235bd29bfa6e9c31a72..663b498745c862cfc0ab8c4e0f4172ac1c1e42d7 100644
--- a/src/backend/storage/file/buffile.c
+++ b/src/backend/storage/file/buffile.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/file/buffile.c,v 1.27 2007/06/03 17:07:30 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/file/buffile.c,v 1.28 2007/06/07 19:19:57 tgl Exp $
  *
  * NOTES:
  *
@@ -41,6 +41,8 @@
  * The maximum safe file size is presumed to be RELSEG_SIZE * BLCKSZ.
  * Note we adhere to this limit whether or not LET_OS_MANAGE_FILESIZE
  * is defined, although md.c ignores it when that symbol is defined.
+ * The reason for doing this is that we'd like large temporary BufFiles
+ * to be spread across multiple tablespaces when available.
  */
 #define MAX_PHYSICAL_FILESIZE  (RELSEG_SIZE * BLCKSZ)
 
@@ -60,7 +62,6 @@ struct BufFile
 	 * offsets[i] is the current seek position of files[i].  We use this to
 	 * avoid making redundant FileSeek calls.
 	 */
-	Oid			tblspcOid;		/* tablespace to use (InvalidOid = default) */
 
 	bool		isTemp;			/* can only add files if this is TRUE */
 	bool		isInterXact;	/* keep open over transactions? */
@@ -86,7 +87,7 @@ static int	BufFileFlush(BufFile *file);
 
 /*
  * Create a BufFile given the first underlying physical file.
- * NOTE: caller must set tblspcOid, isTemp, isInterXact if appropriate.
+ * NOTE: caller must set isTemp and isInterXact if appropriate.
  */
 static BufFile *
 makeBufFile(File firstfile)
@@ -98,7 +99,6 @@ makeBufFile(File firstfile)
 	file->files[0] = firstfile;
 	file->offsets = (long *) palloc(sizeof(long));
 	file->offsets[0] = 0L;
-	file->tblspcOid = InvalidOid;
 	file->isTemp = false;
 	file->isInterXact = false;
 	file->dirty = false;
@@ -119,7 +119,7 @@ extendBufFile(BufFile *file)
 	File		pfile;
 
 	Assert(file->isTemp);
-	pfile = OpenTemporaryFile(file->isInterXact, file->tblspcOid);
+	pfile = OpenTemporaryFile(file->isInterXact);
 	Assert(pfile >= 0);
 
 	file->files = (File *) repalloc(file->files,
@@ -137,23 +137,21 @@ extendBufFile(BufFile *file)
  * written to it).
  *
  * If interXact is true, the temp file will not be automatically deleted
- * at end of transaction.  If tblspcOid is not InvalidOid, the temp file
- * is created in the specified tablespace instead of the default one.
+ * at end of transaction.
  *
  * Note: if interXact is true, the caller had better be calling us in a
  * memory context that will survive across transaction boundaries.
  */
 BufFile *
-BufFileCreateTemp(bool interXact, Oid tblspcOid)
+BufFileCreateTemp(bool interXact)
 {
 	BufFile    *file;
 	File		pfile;
 
-	pfile = OpenTemporaryFile(interXact, tblspcOid);
+	pfile = OpenTemporaryFile(interXact);
 	Assert(pfile >= 0);
 
 	file = makeBufFile(pfile);
-	file->tblspcOid = tblspcOid;
 	file->isTemp = true;
 	file->isInterXact = interXact;
 
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 5c1be83ed04ea114c9e05731dfa58788434a5ed0..f024c8e23fe34c1d3e79cd2d8b8cd6f1c9a6a53c 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.138 2007/06/03 17:07:31 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.139 2007/06/07 19:19:57 tgl Exp $
  *
  * NOTES:
  *
@@ -185,6 +185,14 @@ static AllocateDesc allocatedDescs[MAX_ALLOCATED_DESCS];
  */
 static long tempFileCounter = 0;
 
+/*
+ * Array of OIDs of temp tablespaces.  When numTempTableSpaces is -1,
+ * this has not been set in the current transaction.
+ */
+static Oid *tempTableSpaces = NULL;
+static int	numTempTableSpaces = -1;
+static int	nextTempTableSpace = 0;
+
 
 /*--------------------
  *
@@ -840,21 +848,28 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
  * that created them, so this should be false -- but if you need
  * "somewhat" temporary storage, this might be useful. In either case,
  * the file is removed when the File is explicitly closed.
- *
- * tblspcOid: the Oid of the tablespace where the temp file should be created.
- * If InvalidOid, or if the tablespace can't be found, we silently fall back
- * to the database's default tablespace.
  */
 File
-OpenTemporaryFile(bool interXact, Oid tblspcOid)
+OpenTemporaryFile(bool interXact)
 {
 	File		file = 0;
 
 	/*
-	 * If caller specified a tablespace, try to create there.
+	 * If some temp tablespace(s) have been given to us, try to use the next
+	 * one.  If a given tablespace can't be found, we silently fall back
+	 * to the database's default tablespace.
+	 *
+	 * BUT: if the temp file is slated to outlive the current transaction,
+	 * force it into the database's default tablespace, so that it will
+	 * not pose a threat to possible tablespace drop attempts.
 	 */
-	if (OidIsValid(tblspcOid))
-		file = OpenTemporaryFileInTablespace(tblspcOid, false);
+	if (numTempTableSpaces > 0 && !interXact)
+	{
+		Oid		tblspcOid = GetNextTempTableSpace();
+
+		if (OidIsValid(tblspcOid))
+			file = OpenTemporaryFileInTablespace(tblspcOid, false);
+	}
 
 	/*
 	 * If not, or if tablespace is bad, create in database's default
@@ -1530,6 +1545,69 @@ closeAllVfds(void)
 	}
 }
 
+
+/*
+ * SetTempTablespaces
+ *
+ * Define a list (actually an array) of OIDs of tablespaces to use for
+ * temporary files.  This list will be used until end of transaction,
+ * unless this function is called again before then.  It is caller's
+ * responsibility that the passed-in array has adequate lifespan (typically
+ * it'd be allocated in TopTransactionContext).
+ */
+void
+SetTempTablespaces(Oid *tableSpaces, int numSpaces)
+{
+	Assert(numSpaces >= 0);
+	tempTableSpaces = tableSpaces;
+	numTempTableSpaces = numSpaces;
+	/*
+	 * Select a random starting point in the list.  This is to minimize
+	 * conflicts between backends that are most likely sharing the same
+	 * list of temp tablespaces.  Note that if we create multiple temp
+	 * files in the same transaction, we'll advance circularly through
+	 * the list --- this ensures that large temporary sort files are
+	 * nicely spread across all available tablespaces.
+	 */
+	if (numSpaces > 1)
+		nextTempTableSpace = random() % numSpaces;
+	else
+		nextTempTableSpace = 0;
+}
+
+/*
+ * TempTablespacesAreSet
+ *
+ * Returns TRUE if SetTempTablespaces has been called in current transaction.
+ * (This is just so that tablespaces.c doesn't need its own per-transaction
+ * state.)
+ */
+bool
+TempTablespacesAreSet(void)
+{
+	return (numTempTableSpaces >= 0);
+}
+
+/*
+ * GetNextTempTableSpace
+ *
+ * Select the next temp tablespace to use.  A result of InvalidOid means
+ * to use the current database's default tablespace.
+ */
+Oid
+GetNextTempTableSpace(void)
+{
+	if (numTempTableSpaces > 0)
+	{
+		/* Advance nextTempTableSpace counter with wraparound */
+		if (++nextTempTableSpace >= numTempTableSpaces)
+			nextTempTableSpace = 0;
+		return tempTableSpaces[nextTempTableSpace];
+	}
+	return InvalidOid;
+}
+
+
 /*
  * AtEOSubXact_Files
  *
@@ -1583,11 +1661,14 @@ AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
  * particularly care which).  All still-open per-transaction temporary file
  * VFDs are closed, which also causes the underlying files to be
  * deleted. Furthermore, all "allocated" stdio files are closed.
+ * We also forget any transaction-local temp tablespace list.
  */
 void
 AtEOXact_Files(void)
 {
 	CleanupTempFiles(false);
+	tempTableSpaces = NULL;
+	numTempTableSpaces = -1;
 }
 
 /*
diff --git a/src/backend/utils/sort/logtape.c b/src/backend/utils/sort/logtape.c
index 8ded3ccf4fd4127b767d32a0ad317216fdd21109..39962405bf5c8ed9bb783badae94986728a2ce27 100644
--- a/src/backend/utils/sort/logtape.c
+++ b/src/backend/utils/sort/logtape.c
@@ -70,14 +70,13 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/sort/logtape.c,v 1.24 2007/06/03 17:08:23 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/sort/logtape.c,v 1.25 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "commands/tablespace.h"
 #include "storage/buffile.h"
 #include "utils/logtape.h"
 
@@ -529,7 +528,7 @@ LogicalTapeSetCreate(int ntapes)
 	Assert(ntapes > 0);
 	lts = (LogicalTapeSet *) palloc(sizeof(LogicalTapeSet) +
 									(ntapes - 1) *sizeof(LogicalTape));
-	lts->pfile = BufFileCreateTemp(false, GetTempTablespace());
+	lts->pfile = BufFileCreateTemp(false);
 	lts->nFileBlocks = 0L;
 	lts->forgetFreeSpace = false;
 	lts->blocksSorted = true;	/* a zero-length array is sorted ... */
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 707112d5fb1cb8356f359f9f427b970784056ffa..18e4967d8c660ecfef41724181279e86a460bb29 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -91,7 +91,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.76 2007/05/04 21:29:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.77 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -104,6 +104,7 @@
 #include "access/nbtree.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_operator.h"
+#include "commands/tablespace.h"
 #include "miscadmin.h"
 #include "utils/datum.h"
 #include "utils/logtape.h"
@@ -1479,6 +1480,12 @@ inittapes(Tuplesortstate *state)
 	if (tapeSpace + GetMemoryChunkSpace(state->memtuples) < state->allowedMem)
 		USEMEM(state, tapeSpace);
 
+	/*
+	 * Make sure that the temp file(s) underlying the tape set are created in
+	 * suitable temp tablespaces.
+	 */
+	PrepareTempTablespaces();
+
 	/*
 	 * Create the tape set and allocate the per-tape data arrays.
 	 */
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 6370232bc14147dbaa0d9277c8b91a72268487d7..5fb7af13167fdcf64c400de16add281895f71766 100644
--- a/src/backend/utils/sort/tuplestore.c
+++ b/src/backend/utils/sort/tuplestore.c
@@ -38,7 +38,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.32 2007/06/03 17:08:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/sort/tuplestore.c,v 1.33 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -424,15 +424,11 @@ tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
 				return;
 
 			/*
-			 * Nope; time to switch to tape-based operation.
-			 *
-			 * If the temp table is slated to outlive the current transaction,
-			 * force it into my database's default tablespace, so that it will
-			 * not pose a threat to possible tablespace drop attempts.
+			 * Nope; time to switch to tape-based operation.  Make sure that
+			 * the temp file(s) are created in suitable temp tablespaces.
 			 */
-			state->myfile = BufFileCreateTemp(state->interXact,
-											  state->interXact ? InvalidOid :
-											  GetTempTablespace());
+			PrepareTempTablespaces();
+			state->myfile = BufFileCreateTemp(state->interXact);
 			state->status = TSS_WRITEFILE;
 			dumptuples(state);
 			break;
diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h
index 78b86ffb6899411c9f88f295a01191cf82af82ca..f6f74c9fcb96cb9786809339ad7633700ddf763a 100644
--- a/src/include/commands/tablespace.h
+++ b/src/include/commands/tablespace.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.17 2007/06/03 17:08:29 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.18 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,8 @@ extern void AlterTableSpaceOwner(const char *name, Oid newOwnerId);
 extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo);
 
 extern Oid	GetDefaultTablespace(bool forTemp);
-extern Oid	GetTempTablespace(void);
+
+extern void PrepareTempTablespaces(void);
 
 extern Oid	get_tablespace_oid(const char *tablespacename);
 extern char *get_tablespace_name(Oid spc_oid);
diff --git a/src/include/executor/hashjoin.h b/src/include/executor/hashjoin.h
index 70db576ebeeb9b9e5e4eba88f3f7b19ac60a7d57..5613ca38702f6db409c995fd448dfccbaa9206a3 100644
--- a/src/include/executor/hashjoin.h
+++ b/src/include/executor/hashjoin.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/hashjoin.h,v 1.46 2007/06/03 17:08:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/hashjoin.h,v 1.47 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -102,8 +102,6 @@ typedef struct HashJoinTableData
 	BufFile   **innerBatchFile; /* buffered virtual temp file per batch */
 	BufFile   **outerBatchFile; /* buffered virtual temp file per batch */
 
-	Oid			hashTblSpc;		/* tablespace to put temp files in */
-
 	/*
 	 * Info about the datatype-specific hash functions for the datatypes being
 	 * hashed. These are arrays of the same length as the number of hash join
diff --git a/src/include/executor/nodeHashjoin.h b/src/include/executor/nodeHashjoin.h
index 2de661b8c1242d0c48e66912dc4f497d38ff167b..77efa1d8d1e397d888cb04850932c6859ca1757e 100644
--- a/src/include/executor/nodeHashjoin.h
+++ b/src/include/executor/nodeHashjoin.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/executor/nodeHashjoin.h,v 1.35 2007/06/03 17:08:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/executor/nodeHashjoin.h,v 1.36 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,8 +23,7 @@ extern TupleTableSlot *ExecHashJoin(HashJoinState *node);
 extern void ExecEndHashJoin(HashJoinState *node);
 extern void ExecReScanHashJoin(HashJoinState *node, ExprContext *exprCtxt);
 
-extern void ExecHashJoinSaveTuple(HashJoinTable hashtable,
-								  MinimalTuple tuple, uint32 hashvalue,
-								  BufFile **fileptr);
+extern void ExecHashJoinSaveTuple(MinimalTuple tuple, uint32 hashvalue,
+					  BufFile **fileptr);
 
 #endif   /* NODEHASHJOIN_H */
diff --git a/src/include/storage/buffile.h b/src/include/storage/buffile.h
index cee7eeb934bf7b46484d5756a09503f17982bfce..8408bfa0564acbdbff2fa209d25066e2e9a03b71 100644
--- a/src/include/storage/buffile.h
+++ b/src/include/storage/buffile.h
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/buffile.h,v 1.21 2007/06/03 17:08:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/buffile.h,v 1.22 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@ typedef struct BufFile BufFile;
  * prototypes for functions in buffile.c
  */
 
-extern BufFile *BufFileCreateTemp(bool interXact, Oid tblspcOid);
+extern BufFile *BufFileCreateTemp(bool interXact);
 extern void BufFileClose(BufFile *file);
 extern size_t BufFileRead(BufFile *file, void *ptr, size_t size);
 extern size_t BufFileWrite(BufFile *file, void *ptr, size_t size);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index bb5772f64d3072ee88e04cd0e8f3aa8b31b20f8f..6b5ecd0cf5f376520a894ca7c693badf102e1d24 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.58 2007/06/03 17:08:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/fd.h,v 1.59 2007/06/07 19:19:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,7 +60,7 @@ extern int	max_files_per_process;
 
 /* Operations on virtual Files --- equivalent to Unix kernel file ops */
 extern File PathNameOpenFile(FileName fileName, int fileFlags, int fileMode);
-extern File OpenTemporaryFile(bool interXact, Oid tblspcOid);
+extern File OpenTemporaryFile(bool interXact);
 extern void FileClose(File file);
 extern void FileUnlink(File file);
 extern int	FileRead(File file, char *buffer, int amount);
@@ -85,10 +85,14 @@ extern int	BasicOpenFile(FileName fileName, int fileFlags, int fileMode);
 extern void InitFileAccess(void);
 extern void set_max_safe_fds(void);
 extern void closeAllVfds(void);
+extern void SetTempTablespaces(Oid *tableSpaces, int numSpaces);
+extern bool TempTablespacesAreSet(void);
+extern Oid	GetNextTempTableSpace(void);
 extern void AtEOXact_Files(void);
 extern void AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
 				  SubTransactionId parentSubid);
 extern void RemovePgTempFiles(void);
+
 extern int	pg_fsync(int fd);
 extern int	pg_fsync_no_writethrough(int fd);
 extern int	pg_fsync_writethrough(int fd);