*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 71,76 **** static relopt_bool boolRelOpts[] =
--- 71,84 ----
  	},
  	{
  		{
+ 			"only_temp_files",
+ 			"Allow only temporary files to be created on this tablespace",
+ 			RELOPT_KIND_TABLESPACE
+ 		},
+ 		false
+ 	},
+ 	{
+ 		{
  			"fastupdate",
  			"Enables \"fast update\" feature for this GIN index",
  			RELOPT_KIND_GIN
***************
*** 1337,1343 **** tablespace_reloptions(Datum reloptions, bool validate)
  	int			numoptions;
  	static const relopt_parse_elt tab[] = {
  		{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
! 		{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)}
  	};
  
  	options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
--- 1345,1352 ----
  	int			numoptions;
  	static const relopt_parse_elt tab[] = {
  		{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
! 		{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
! 		{"only_temp_files", RELOPT_TYPE_BOOL, offsetof(TableSpaceOpts, only_temp_files)}
  	};
  
  	options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 394,399 **** createdb(const CreatedbStmt *stmt)
--- 394,405 ----
  					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				  errmsg("pg_global cannot be used as default tablespace")));
  
+ 		/* can't create a database on temporary tablespace */
+ 		if (is_tablespace_temp_only(dst_deftablespace))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				  errmsg("this tablespace only allows temporary files")));
+ 
  		/*
  		 * If we are trying to change the default tablespace of the template,
  		 * we require that the template not have any files in the new default
***************
*** 1083,1088 **** movedb(const char *dbname, const char *tblspcname)
--- 1089,1100 ----
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("pg_global cannot be used as default tablespace")));
  
+ 	/* can't create a database on temporary tablespace */
+ 	if (is_tablespace_temp_only(dst_tblspcoid))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("this tablespace only allows temporary files")));
+ 
  	/*
  	 * No-op if same tablespace
  	 */
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
***************
*** 432,437 **** DefineIndex(Oid relationId,
--- 432,446 ----
  						   get_tablespace_name(tablespaceId));
  	}
  
+ 	/* Can't save relations on temporary tablespace */
+ 	if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+ 		is_tablespace_temp_only(OidIsValid(tablespaceId) ? tablespaceId : MyDatabaseTableSpace))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("this tablespace only allows temporary files")));
+ 	}
+ 
  	/*
  	 * Force shared indexes into the pg_global tablespace.  This is a bit of a
  	 * hack but seems simpler than marking them in the BKI commands.  On the
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 523,528 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 523,537 ----
  				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
  				 errmsg("only shared relations can be placed in pg_global tablespace")));
  
+ 	/* Can't save relations on temporary tablespace */
+ 	if (stmt->relation->relpersistence != RELPERSISTENCE_TEMP &&
+ 		is_tablespace_temp_only(OidIsValid(tablespaceId) ? tablespaceId : MyDatabaseTableSpace))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("this tablespace only allows temporary files")));
+ 	}
+ 
  	/* Identify user ID that will own the table */
  	if (!OidIsValid(ownerId))
  		ownerId = GetUserId();
***************
*** 8824,8829 **** ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, L
--- 8833,8847 ----
  			aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
  	}
  
+ 	/* Can't save relations on temporary tablespace */
+ 	if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+ 		is_tablespace_temp_only(OidIsValid(tablespaceId) ? tablespaceId : MyDatabaseTableSpace))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("this tablespace only allows temporary files")));
+ 	}
+ 
  	/* Save info for Phase 3 to do the real work */
  	if (OidIsValid(tab->newTableSpace))
  		ereport(ERROR,
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
***************
*** 81,86 ****
--- 81,87 ----
  #include "utils/memutils.h"
  #include "utils/rel.h"
  #include "utils/tqual.h"
+ #include "utils/spccache.h"
  
  
  /* GUC variables */
***************
*** 154,170 **** TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
  				{
  					char	   *parentdir;
  
! 					/* Failure other than not exists or not in WAL replay? */
! 					if (errno != ENOENT || !isRedo)
  						ereport(ERROR,
  								(errcode_for_file_access(),
  							  errmsg("could not create directory \"%s\": %m",
  									 dir)));
  
  					/*
! 					 * Parent directories are missing during WAL replay, so
! 					 * continue by creating simple parent directories rather
! 					 * than a symlink.
  					 */
  
  					/* create two parents up if not exist */
--- 155,172 ----
  				{
  					char	   *parentdir;
  
! 					/* Failure other than not exists or not in WAL replay with a non-temp tablespace? */
! 					if (errno != ENOENT || !( isRedo || is_tablespace_temp_only(spcNode) ) )
  						ereport(ERROR,
  								(errcode_for_file_access(),
  							  errmsg("could not create directory \"%s\": %m",
  									 dir)));
  
  					/*
! 					 * Parent directories are missing during WAL replay, and
! 					 * they can be missing for temp tablespaces, so continue
! 					 * by creating simple parent directories rather than a
! 					 * symlink.
  					 */
  
  					/* create two parents up if not exist */
***************
*** 929,934 **** AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
--- 931,938 ----
  	Oid			tablespaceoid;
  	Datum		datum;
  	Datum		newOptions;
+ 	TableSpaceOpts *tsopts;
+ 	TableSpaceOpts *tsoptsOld;
  	Datum		repl_val[Natts_pg_tablespace];
  	bool		isnull;
  	bool		repl_null[Natts_pg_tablespace];
***************
*** 960,969 **** AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
  	/* Generate new proposed spcoptions (text array) */
  	datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
  						 RelationGetDescr(rel), &isnull);
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
  									 stmt->options, NULL, NULL, false,
  									 stmt->isReset);
! 	(void) tablespace_reloptions(newOptions, true);
  
  	/* Build new tuple. */
  	memset(repl_null, false, sizeof(repl_null));
--- 964,983 ----
  	/* Generate new proposed spcoptions (text array) */
  	datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
  						 RelationGetDescr(rel), &isnull);
+ 	tsoptsOld = (TableSpaceOpts *) tablespace_reloptions(datum, false);
  	newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
  									 stmt->options, NULL, NULL, false,
  									 stmt->isReset);
! 	tsopts = (TableSpaceOpts *) tablespace_reloptions(newOptions, true);
! 
! 	/* Can't save relations on temporary tablespace */
! 	if (tsopts->only_temp_files)
! 	{
! 		if (!tsoptsOld->only_temp_files)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! 				  errmsg("cannot alter a tablespace to become temporary")));
! 	}
  
  	/* Build new tuple. */
  	memset(repl_null, false, sizeof(repl_null));
*** a/src/backend/storage/file/fd.c
--- b/src/backend/storage/file/fd.c
***************
*** 76,81 ****
--- 76,82 ----
  #include "storage/ipc.h"
  #include "utils/guc.h"
  #include "utils/resowner_private.h"
+ #include "utils/spccache.h"
  
  
  /*
***************
*** 1132,1137 **** OpenTemporaryFileInTablespace(Oid tblspcOid, bool rejectError)
--- 1133,1156 ----
  		file = PathNameOpenFile(tempfilepath,
  								O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
  								0600);
+ 		/* On a temporary tablespace, we need to recreate its structure */
+ 		if (file <= 0 && is_tablespace_temp_only(tblspcOid))
+ 		{
+ 			/*
+ 			 * XXX: Should we only do that for temp tablespace? Or blindly do for
+ 			 * any tablespace?
+ 			 */
+ 			char		*parentdir;
+ 			parentdir = pstrdup(tempdirpath);
+ 			get_parent_directory(parentdir);
+ 			/* As above, don't check error for mkdir */
+ 			mkdir(parentdir, S_IRWXU);
+ 			pfree(parentdir);
+ 
+ 			file = PathNameOpenFile(tempfilepath,
+ 									O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
+ 									0600);
+ 		}
  		if (file <= 0 && rejectError)
  			elog(ERROR, "could not create temporary file \"%s\": %m",
  				 tempfilepath);
*** a/src/backend/utils/cache/spccache.c
--- b/src/backend/utils/cache/spccache.c
***************
*** 199,201 **** get_tablespace_page_costs(Oid spcid,
--- 199,230 ----
  			*spc_seq_page_cost = spc->opts->seq_page_cost;
  	}
  }
+ 
+ /*
+  * is_tablespace_temp_only
+  *		Return true if the tablespace only allows temporary files
+  */
+ bool
+ is_tablespace_temp_only(Oid spcid)
+ {
+ 	TableSpaceCacheEntry *spc;
+ 
+ 	/*
+ 	 * pg_global and pg_default are never temporary, so no need to
+ 	 * check the cache
+ 	 */
+ 	if (spcid == GLOBALTABLESPACE_OID || spcid == DEFAULTTABLESPACE_OID)
+ 		return false;
+ 
+ 	spc = get_tablespace(spcid);
+ 
+ 	Assert(spc != NULL);
+ 
+ 	if (spc->opts == NULL)
+ 	{
+ 		/* no options, so this tablespace can't be considered temporary */
+ 		return false;
+ 	}
+ 
+ 	return spc->opts->only_temp_files;
+ }
*** a/src/include/commands/tablespace.h
--- b/src/include/commands/tablespace.h
***************
*** 37,42 **** typedef struct TableSpaceOpts
--- 37,43 ----
  	int32		vl_len_;		/* varlena header (do not touch directly!) */
  	float8		random_page_cost;
  	float8		seq_page_cost;
+ 	bool		only_temp_files;
  } TableSpaceOpts;
  
  extern Oid	CreateTableSpace(CreateTableSpaceStmt *stmt);
*** a/src/include/utils/spccache.h
--- b/src/include/utils/spccache.h
***************
*** 15,19 ****
--- 15,20 ----
  
  void get_tablespace_page_costs(Oid spcid, float8 *spc_random_page_cost,
  						  float8 *spc_seq_page_cost);
+ bool is_tablespace_temp_only(Oid spcid);
  
  #endif   /* SPCCACHE_H */
*** a/src/test/regress/input/tablespace.source
--- b/src/test/regress/input/tablespace.source
***************
*** 81,89 **** ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
--- 81,133 ----
  -- Should show notice that nothing was done
  ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
  
+ -- Try changing only_temp_files
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = true); --fail
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = on); --fail
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = false); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = off); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ ALTER TABLESPACE testspace_renamed RESET (only_temp_files); --ok
+ 
  -- Should succeed
  DROP TABLESPACE testspace_renamed;
  
+ -- Now, let's create temporary tablespace
+ CREATE TABLESPACE testspace LOCATION '@testtablespace@' WITH (only_temp_files = true); -- ok
+ CREATE TABLE testschema.nontemp(a int) TABLESPACE testspace; -- fail
+ CREATE TABLE testschema.nontemp(a int); -- ok
+ ALTER TABLE testschema.nontemp SET TABLESPACE testspace; -- fail
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a) TABLESPACE testspace; -- fail
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a); -- ok
+ ALTER INDEX testschema.nontemp_idx SET TABLESPACE testspace; -- fail
+ 
+ -- Explicit created (on CREATE and ALTER)
+ CREATE TEMP TABLE temptbl(a int) TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl(a);
+ CREATE TEMP TABLE temptbl2(a int);
+ CREATE INDEX temptbl2_idx ON temptbl2(a);
+ ALTER TABLE temptbl2 SET TABLESPACE testspace;
+ ALTER INDEX temptbl2_idx SET TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+     where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx', 'temptbl2', 'temptbl2_idx');
+ DROP TABLE temptbl;
+ DROP TABLE temptbl2;
+ 
+ -- Use temp_tablespaces
+ SET temp_tablespaces TO 'testspace';
+ CREATE TEMP TABLE temptbl(a int);
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+     where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx');
+ 
+ -- Use this tablespace in a sort operation, and check if any files on pgsql_tmp has been used
+ SET work_mem TO '1MB';
+ WITH o AS (SELECT i, md5(random()::text) FROM generate_series(1, 100000) i ORDER BY md5) SELECT count(*) > 0 FROM (SELECT pg_ls_dir('pg_tblspc/' || oid || '/' || pg_ls_dir('pg_tblspc/' || oid || '/') || '/pgsql_tmp/') FROM pg_tablespace, (SELECT count(*) FROM o) t1 WHERE spcname = 'testspace') t2;
+ 
+ -- Should succeed
  DROP SCHEMA testschema CASCADE;
  
  DROP ROLE tablespace_testuser1;
*** a/src/test/regress/output/tablespace.source
--- b/src/test/regress/output/tablespace.source
***************
*** 97,109 **** ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
  -- Should show notice that nothing was done
  ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
  NOTICE:  no matching relations in tablespace "testspace_renamed" found
  -- Should succeed
  DROP TABLESPACE testspace_renamed;
  DROP SCHEMA testschema CASCADE;
! NOTICE:  drop cascades to 4 other objects
  DETAIL:  drop cascades to table testschema.foo
  drop cascades to table testschema.asselect
  drop cascades to table testschema.asexecute
  drop cascades to table testschema.atable
  DROP ROLE tablespace_testuser1;
  DROP ROLE tablespace_testuser2;
--- 97,189 ----
  -- Should show notice that nothing was done
  ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
  NOTICE:  no matching relations in tablespace "testspace_renamed" found
+ -- Try changing only_temp_files
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = true); --fail
+ ERROR:  cannot alter a tablespace to become temporary
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = on); --fail
+ ERROR:  cannot alter a tablespace to become temporary
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+  spcoptions 
+ ------------
+  
+ (1 row)
+ 
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = false); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+        spcoptions        
+ -------------------------
+  {only_temp_files=false}
+ (1 row)
+ 
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = off); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+       spcoptions       
+ -----------------------
+  {only_temp_files=off}
+ (1 row)
+ 
+ ALTER TABLESPACE testspace_renamed RESET (only_temp_files); --ok
  -- Should succeed
  DROP TABLESPACE testspace_renamed;
+ -- Now, let's create temporary tablespace
+ CREATE TABLESPACE testspace LOCATION '@testtablespace@' WITH (only_temp_files = true); -- ok
+ CREATE TABLE testschema.nontemp(a int) TABLESPACE testspace; -- fail
+ ERROR:  this tablespace only allows temporary files
+ CREATE TABLE testschema.nontemp(a int); -- ok
+ ALTER TABLE testschema.nontemp SET TABLESPACE testspace; -- fail
+ ERROR:  this tablespace only allows temporary files
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a) TABLESPACE testspace; -- fail
+ ERROR:  this tablespace only allows temporary files
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a); -- ok
+ ALTER INDEX testschema.nontemp_idx SET TABLESPACE testspace; -- fail
+ ERROR:  this tablespace only allows temporary files
+ -- Explicit created (on CREATE and ALTER)
+ CREATE TEMP TABLE temptbl(a int) TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl(a);
+ CREATE TEMP TABLE temptbl2(a int);
+ CREATE INDEX temptbl2_idx ON temptbl2(a);
+ ALTER TABLE temptbl2 SET TABLESPACE testspace;
+ ALTER INDEX temptbl2_idx SET TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ ERROR:  relation "temptbl_idx" already exists
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+     where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx', 'temptbl2', 'temptbl2_idx');
+    relname    |  spcname  
+ --------------+-----------
+  temptbl      | testspace
+  temptbl2_idx | testspace
+  temptbl2     | testspace
+ (3 rows)
+ 
+ DROP TABLE temptbl;
+ DROP TABLE temptbl2;
+ -- Use temp_tablespaces
+ SET temp_tablespaces TO 'testspace';
+ CREATE TEMP TABLE temptbl(a int);
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+     where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx');
+    relname   |  spcname  
+ -------------+-----------
+  temptbl     | testspace
+  temptbl_idx | testspace
+ (2 rows)
+ 
+ -- Use this tablespace in a sort operation, and check if any files on pgsql_tmp has been used
+ SET work_mem TO '1MB';
+ WITH o AS (SELECT i, md5(random()::text) FROM generate_series(1, 100000) i ORDER BY md5) SELECT count(*) > 0 FROM (SELECT pg_ls_dir('pg_tblspc/' || oid || '/' || pg_ls_dir('pg_tblspc/' || oid || '/') || '/pgsql_tmp/') FROM pg_tablespace, (SELECT count(*) FROM o) t1 WHERE spcname = 'testspace') t2;
+  ?column? 
+ ----------
+  t
+ (1 row)
+ 
+ -- Should succeed
  DROP SCHEMA testschema CASCADE;
! NOTICE:  drop cascades to 5 other objects
  DETAIL:  drop cascades to table testschema.foo
  drop cascades to table testschema.asselect
  drop cascades to table testschema.asexecute
  drop cascades to table testschema.atable
+ drop cascades to table testschema.nontemp
  DROP ROLE tablespace_testuser1;
  DROP ROLE tablespace_testuser2;
