Hi,

Sorry for the delay, I missed this thread.
Here is a new version of this patch considering Andreas' comments.

On 30/12/2014 03:48, Andreas Karlsson wrote:
> - A test fails in create_view.out. I looked some into it and did not see 
> how this could happen.
> 
>      *** 
> /home/andreas/dev/postgresql/src/test/regress/expected/create_view.out 
> 2014-08-09 01:35:50.008886776 +0200
>      --- 
> /home/andreas/dev/postgresql/src/test/regress/results/create_view.out 
> 2014-12-30 00:41:17.966525339 +0100
>      ***************
>      *** 283,289 ***
>          relname   | relkind |        reloptions
>        ------------+---------+--------------------------
>         mysecview1 | v       |
>      !  mysecview2 | v       |
>         mysecview3 | v       | {security_barrier=true}
>         mysecview4 | v       | {security_barrier=false}
>        (4 rows)
>      --- 283,289 ----
>          relname   | relkind |        reloptions
>        ------------+---------+--------------------------
>         mysecview1 | v       |
>      !  mysecview2 | v       | {security_barrier=true}
>         mysecview3 | v       | {security_barrier=true}
>         mysecview4 | v       | {security_barrier=false}
>        (4 rows)

I can't reproduce this issue.

> - pg_dump is broken
> 
>      pg_dump: [archiver (db)] query failed: ERROR:  syntax error at or 
> near "("
>      LINE 1: ...nest(tc.reloptions) x), ', ') AS toast_reloptions 
> (SELECT sp...

Fixed.

> - "ALTER INDEX foo_idx SET TABLE TABLESPACE ..." should not be allowed, 
> currently it changes the tablespace of the index. I do not think "ALTER 
> INDEX foo_idx SET (TABLE|TOAST) TABLESPACE ..." should even be allowed 
> in the grammar.

Fixed.

> - You should add a link to 
> http://www.postgresql.org/docs/current/interactive/storage-toast.html to 
> the manual page of ALTER TABLE.

Added.

> - I do not like how \d handles the toast tablespace. Having TOAST in 
> pg_default and the table in another space looks the same as if there was 
> no TOAST table at all. I think we should always print both tablespaces 
> if either of them are not pg_default.

If we do it that way, we should always show the tablespace even if it's
pg_default in any case to be consistent. I'm pretty sure that we don't
want that.

> - Would it be interesting to add syntax for moving the toast index to a 
> separate tablespace?

-1, we just want to be able to move TOAST heap, which is supposed to
contain a lot of data and we want to move on a different storage device
/ filesystem.

> - There is no warning if you set the toast table space of a table 
> lacking toast. I think there should be one.

A notice is raised now in that case.

> - No support for materialized views as pointed out by Alex.

Support, documentation and regression tests added.

> - I think the code would be cleaner if ATPrepSetTableSpace and 
> ATPrepSetToastTableSpace were merged into one function or split into 
> two, one setting the toast and one setting the tablespace of the table 
> and one setting it on the TOAST table.

Done.

> - I think a couple of tests for the error cases would be nice.

2 more tests added.

> - Missing periods on the ALTER TABLE manual page after "See also CREATE 
> TABLESPACE" (in two places).
> 
> - Missing period last in the new paragraph added to the storage manual page.

I don't understand those 2 last points ?

> - Double spaces in src/backend/catalog/toasting.c after "if (new_toast".

Fixed.

> - The comment "and if this is not a TOAST re-creation case (through 
> CLUSTER)." incorrectly implies that CLUSTER is the only case where the 
> TOAST table is recreated.

Fixed.

> - The local variables ToastTableSpace and ToastRel do not follow the 
> naming conventions.

Fixed (I hope so).

> - Remove the parentheses around "(is_system_catalog)".

Fixed.

> - Why was the "Save info for Phase 3 to do the real work" comment 
> changed to "XXX: Save info for Phase 3 to do the real work"?

Fixed.

> - Incorrect indentation for new code added last in ATExecSetTableSpace.

Fixed.

> - The patch adds commented out code in src/include/commands/tablecmds.h.

Fixed.

Thank you for your review.

--
Julien
diff --git a/doc/src/sgml/ref/alter_materialized_view.sgml b/doc/src/sgml/ref/alter_materialized_view.sgml
index b0759fc..321ffc9 100644
--- a/doc/src/sgml/ref/alter_materialized_view.sgml
+++ b/doc/src/sgml/ref/alter_materialized_view.sgml
@@ -44,6 +44,8 @@ ALTER MATERIALIZED VIEW ALL IN TABLESPACE <replaceable class="parameter">name</r
     RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
     OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
     SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
+    SET TABLE TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
+    SET TOAST TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
 </synopsis>
  </refsynopsisdiv>
 
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index b3a4970..777ba57 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -66,6 +66,8 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
     SET WITH OIDS
     SET WITHOUT OIDS
     SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
+    SET TABLE TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
+    SET TOAST TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
     SET {LOGGED | UNLOGGED}
     SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
     RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
@@ -501,7 +503,8 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
     <term><literal>SET TABLESPACE</literal></term>
     <listitem>
      <para>
-      This form changes the table's tablespace to the specified tablespace and
+      This form changes the table's (and TOAST table's, if any) tablespace to
+      the specified tablespace and
       moves the data file(s) associated with the table to the new tablespace.
       Indexes on the table, if any, are not moved; but they can be moved
       separately with additional <literal>SET TABLESPACE</literal> commands.
@@ -523,6 +526,31 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
    </varlistentry>
 
    <varlistentry>
+    <term><literal>SET TABLE TABLESPACE</literal></term>
+    <listitem>
+     <para>
+      This form changes only table's tablespace (but not associated TOAST
+      table's tablespace, if any) to the specified tablespace and moves the
+      data file(s) associated to the new tablespace.  See also
+      <xref linkend="SQL-CREATETABLESPACE">
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>SET TOAST TABLESPACE</literal></term>
+    <listitem>
+     <para>
+      This form changes the TOAST table's tablespace to the specified
+      tablespace and moves the data file(s) associated with the TOAST table to
+      the new tablespace.  See also
+      See <xref linkend="SQL-CREATETABLESPACE"> and <xref linkend="storage-toast"> 
+	  for more information.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>SET {LOGGED | UNLOGGED}</literal></term>
     <listitem>
      <para>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index d8c5287..0515967 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -571,6 +571,11 @@ The <xref linkend="pgfreespacemap"> module
 can be used to examine the information stored in free space maps.
 </para>
 
+<para>
+TOAST table can be moved to a different tablespace with
+<command>ALTER TABLE SET TOAST TABLESPACE</>
+</para>
+
 </sect1>
 
 <sect1 id="storage-vm">
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index a1efddb..6a06f9e 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,9 +36,11 @@
 Oid			binary_upgrade_next_toast_pg_type_oid = InvalidOid;
 
 static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
-						 LOCKMODE lockmode, bool check);
+						 LOCKMODE lockmode, bool check,
+						 Oid toastTableSpace, bool new_toast);
 static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
-				   Datum reloptions, LOCKMODE lockmode, bool check);
+				   Datum reloptions, LOCKMODE lockmode, bool check,
+				   Oid toastTableSpace, bool new_toast);
 static bool needs_toast_table(Relation rel);
 
 
@@ -55,32 +57,39 @@ static bool needs_toast_table(Relation rel);
  * to end with CommandCounterIncrement if it makes any changes.
  */
 void
-AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
+AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode,
+						   Oid toastTableSpace, bool new_toast)
 {
-	CheckAndCreateToastTable(relOid, reloptions, lockmode, true);
+	CheckAndCreateToastTable(relOid, reloptions, lockmode, true,
+							 toastTableSpace, new_toast);
 }
 
 void
-NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
+NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode,
+						Oid toastTableSpace)
 {
-	CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
+	CheckAndCreateToastTable(relOid, reloptions, lockmode, false,
+							 toastTableSpace, false);
 }
 
 void
-NewRelationCreateToastTable(Oid relOid, Datum reloptions)
+NewRelationCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace)
 {
-	CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
+	CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false,
+							 toastTableSpace, true);
 }
 
 static void
-CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
+CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check,
+						 Oid toastTableSpace, bool new_toast)
 {
 	Relation	rel;
 
 	rel = heap_open(relOid, lockmode);
 
 	/* create_toast_table does all the work */
-	(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
+	(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check,
+							  toastTableSpace, new_toast);
 
 	heap_close(rel, NoLock);
 }
@@ -106,7 +115,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
 
 	/* create_toast_table does all the work */
 	if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,
-							AccessExclusiveLock, false))
+							AccessExclusiveLock, false, InvalidOid, true))
 		elog(ERROR, "\"%s\" does not require a toast table",
 			 relName);
 
@@ -123,7 +132,8 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
  */
 static bool
 create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
-				   Datum reloptions, LOCKMODE lockmode, bool check)
+				   Datum reloptions, LOCKMODE lockmode, bool check,
+				   Oid toastTableSpace, bool new_toast)
 {
 	Oid			relOid = RelationGetRelid(rel);
 	HeapTuple	reltup;
@@ -270,9 +280,16 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 		binary_upgrade_next_toast_pg_type_oid = InvalidOid;
 	}
 
+	/*
+	 * Use table's tablespace if toastTableSpace is invalid
+	 * and if this is not a TOAST re-creation case.
+	 */
+	if (new_toast && !OidIsValid(toastTableSpace))
+		toastTableSpace = rel->rd_rel->reltablespace;
+
 	toast_relid = heap_create_with_catalog(toast_relname,
 										   namespaceid,
-										   rel->rd_rel->reltablespace,
+										   toastTableSpace,
 										   toastOid,
 										   toast_typid,
 										   InvalidOid,
@@ -339,7 +356,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 				 indexInfo,
 				 list_make2("chunk_id", "chunk_seq"),
 				 BTREE_AM_OID,
-				 rel->rd_rel->reltablespace,
+				 toastTableSpace,
 				 collationObjectId, classObjectId, coloptions, (Datum) 0,
 				 true, false, false, false,
 				 true, false, false, true, false);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index dc1b37c..c5b578b 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -563,6 +563,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
 	bool		swap_toast_by_content;
 	TransactionId frozenXid;
 	MultiXactId cutoffMulti;
+	Oid			toastTablespace;
+	Relation	toastRel;
 
 	/* Mark the correct index as clustered */
 	if (OidIsValid(indexOid))
@@ -571,13 +573,27 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
 	/* Remember if it's a system catalog */
 	is_system_catalog = IsSystemRelation(OldHeap);
 
+	/*
+	 * Verifiy if a TOASTed relation exists and is a valid relation.
+	 * If true, keep its previous tablespace in memory to rebuild it in
+	 * the same tablespace.
+	 */
+	if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
+	{
+		toastRel = relation_open(OldHeap->rd_rel->reltoastrelid, NoLock);
+		toastTablespace = toastRel->rd_rel->reltablespace;
+		relation_close(toastRel, NoLock);
+	}
+	else
+		toastTablespace = is_system_catalog ? tableSpace : InvalidOid;
+
 	/* Close relcache entry, but keep lock until transaction commit */
 	heap_close(OldHeap, NoLock);
 
 	/* Create the transient table that will receive the re-ordered data */
 	OIDNewHeap = make_new_heap(tableOid, tableSpace,
 							   OldHeap->rd_rel->relpersistence,
-							   AccessExclusiveLock);
+							   AccessExclusiveLock, toastTablespace);
 
 	/* Copy the heap data into the new table in the desired order */
 	copy_heap_data(OIDNewHeap, tableOid, indexOid, verbose,
@@ -606,7 +622,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
  */
 Oid
 make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
-			  LOCKMODE lockmode)
+			  LOCKMODE lockmode, Oid NewToastTableSpace)
 {
 	TupleDesc	OldHeapDesc;
 	char		NewHeapName[NAMEDATALEN];
@@ -711,7 +727,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
 		if (isNull)
 			reloptions = (Datum) 0;
 
-		NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode);
+		NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode,
+								NewToastTableSpace);
 
 		ReleaseSysCache(tuple);
 	}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index c961429..f26d388 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -403,7 +403,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 
 	(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
 
-	NewRelationCreateToastTable(intoRelationId, toast_options);
+	NewRelationCreateToastTable(intoRelationId, toast_options, InvalidOid);
 
 	/* Create the "view" part of a materialized view. */
 	if (is_matview)
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 92d9032..f9aae7d 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -268,7 +268,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
 	 * will be gone).
 	 */
 	OIDNewHeap = make_new_heap(matviewOid, tableSpace, relpersistence,
-							   ExclusiveLock);
+							   ExclusiveLock, tableSpace);
 	LockRelationOid(OIDNewHeap, AccessExclusiveLock);
 	dest = CreateTransientRelDestReceiver(OIDNewHeap);
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7455020..e8ad43e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -157,6 +157,7 @@ typedef struct AlteredTableInfo
 	bool		new_notnull;	/* T if we added new NOT NULL constraints */
 	int			rewrite;		/* Reason for forced rewrite, if any */
 	Oid			newTableSpace;	/* new tablespace; 0 means no change */
+	Oid			newToastTableSpace;	/* new TOAST tablespace; 0 means no change */
 	bool		chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
 	char		newrelpersistence;		/* if above is true */
 	/* Objects to rebuild after completing ALTER TYPE operations */
@@ -397,8 +398,9 @@ static void ATExecClusterOn(Relation rel, const char *indexName,
 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
-					char *tablespacename, LOCKMODE lockmode);
-static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+					char *tablespacename, LOCKMODE lockmode, AlterTableType subtype);
+static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode, Oid newToastTableSpace);
+static void ATExecSetToastTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
 static void ATExecSetRelOptions(Relation rel, List *defList,
 					AlterTableType operation,
 					LOCKMODE lockmode);
@@ -425,6 +427,7 @@ static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
 								Oid oldRelOid, void *arg);
 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
 								 Oid oldrelid, void *arg);
+static void RelationIsMoveableToNewTablespace(Relation rel, Oid NewTableSpaceOid);
 
 
 /* ----------------------------------------------------------------
@@ -2807,6 +2810,8 @@ AlterTableGetLockLevel(List *cmds)
 			case AT_AddColumn:	/* may rewrite heap, in some cases and visible
 								 * to SELECT */
 			case AT_SetTableSpace:		/* must rewrite heap */
+			case AT_SetTableTableSpace:	/* must rewrite heap */
+			case AT_SetToastTableSpace:	/* must rewrite heap */
 			case AT_AlterColumnType:	/* must rewrite heap */
 			case AT_AddOids:	/* must rewrite heap */
 				cmd_lockmode = AccessExclusiveLock;
@@ -3247,7 +3252,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 		case AT_SetTableSpace:	/* SET TABLESPACE */
 			ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX);
 			/* This command never recurses */
-			ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
+			ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, cmd->subtype);
+			pass = AT_PASS_MISC;	/* doesn't actually matter */
+			break;
+		case AT_SetTableTableSpace:	/* SET TABLE TABLESPACE */
+		case AT_SetToastTableSpace:	/* SET TOAST TABLESPACE */
+			ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW);
+			/* This command never recurses */
+			ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, cmd->subtype);
 			pass = AT_PASS_MISC;	/* doesn't actually matter */
 			break;
 		case AT_SetRelOptions:	/* SET (...) */
@@ -3382,9 +3394,32 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
 	{
 		AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
 
+		Relation	toast_rel;
+		Oid			toast_tablespace_oid;
+		Relation	rel;
+		bool		new_toast;
+
 		if (tab->relkind == RELKIND_RELATION ||
 			tab->relkind == RELKIND_MATVIEW)
-			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
+		{
+			rel = relation_open(tab->relid, NoLock);
+			if (OidIsValid(rel->rd_rel->reltoastrelid))
+			{
+				toast_rel = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+				toast_tablespace_oid = toast_rel->rd_rel->reltablespace;
+				relation_close(toast_rel, NoLock);
+				new_toast = false;
+			}
+			else
+			{
+				toast_tablespace_oid = InvalidOid;
+				new_toast = true;
+			}
+			relation_close(rel, NoLock);
+
+			AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode,
+									   toast_tablespace_oid, new_toast);
+		}
 	}
 }
 
@@ -3523,6 +3558,18 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			 * Nothing to do here; Phase 3 does the work
 			 */
 			break;
+		case AT_SetTableTableSpace:	/* SET TABLE TABLESPACE */
+
+			/*
+			 * Nothing to do here; Phase 3 does the work
+			 */
+			break;
+		case AT_SetToastTableSpace:	/* SET TOAST TABLESPACE */
+
+			/*
+			 * Nothing to do here; Phase 3 does the work
+			 */
+			break;
 		case AT_SetRelOptions:	/* SET (...) */
 		case AT_ResetRelOptions:		/* RESET (...) */
 		case AT_ReplaceRelOptions:		/* replace entire option list */
@@ -3673,6 +3720,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 			Relation	OldHeap;
 			Oid			OIDNewHeap;
 			Oid			NewTableSpace;
+			Oid			NewToastTableSpace;
 			char		persistence;
 
 			OldHeap = heap_open(tab->relid, NoLock);
@@ -3712,6 +3760,22 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 			else
 				NewTableSpace = OldHeap->rd_rel->reltablespace;
 
+			if (tab->newToastTableSpace)
+				NewToastTableSpace = tab->newToastTableSpace;
+			else
+			{
+				if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
+				{
+					Relation OldToastRel;
+
+					OldToastRel = relation_open(OldHeap->rd_rel->reltoastrelid, NoLock);
+					NewToastTableSpace = OldToastRel->rd_rel->reltablespace;
+					relation_close(OldToastRel, NoLock);
+				}
+				else
+					NewToastTableSpace = InvalidOid;
+			}
+
 			/*
 			 * Select persistence of transient table (same as original unless
 			 * user requested a change)
@@ -3752,7 +3816,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 			 * unlogged anyway.
 			 */
 			OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
-									   lockmode);
+									   lockmode, NewToastTableSpace);
 
 			/*
 			 * Copy the heap data into the new table with the desired
@@ -3790,7 +3854,9 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
 			 * just do a block-by-block copy.
 			 */
 			if (tab->newTableSpace)
-				ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
+				ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode, tab->newToastTableSpace);
+			if (tab->newToastTableSpace)
+				ATExecSetToastTableSpace(tab->relid, tab->newToastTableSpace, lockmode);
 		}
 	}
 
@@ -8954,33 +9020,64 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
 }
 
 /*
- * ALTER TABLE SET TABLESPACE
+ * Check tablespace's permissions & no multiple SET TABLESPACE subcommands.
  */
 static void
-ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
+CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid,
+						  Oid NewTableSpaceOid)
 {
-	Oid			tablespaceId;
-
-	/* Check that the tablespace exists */
-	tablespaceId = get_tablespace_oid(tablespacename, false);
-
 	/* Check permissions except when moving to database's default */
-	if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
+	if (OidIsValid(TableSpaceOid) && TableSpaceOid != MyDatabaseTableSpace)
 	{
 		AclResult	aclresult;
 
-		aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE);
+		aclresult = pg_tablespace_aclcheck(TableSpaceOid, GetUserId(), ACL_CREATE);
 		if (aclresult != ACLCHECK_OK)
-			aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
+			aclcheck_error(aclresult, ACL_KIND_TABLESPACE, TableSpaceName);
 	}
 
 	/* Save info for Phase 3 to do the real work */
-	if (OidIsValid(tab->newTableSpace))
+	if (OidIsValid(NewTableSpaceOid))
 		ereport(ERROR,
 				(errcode(ERRCODE_SYNTAX_ERROR),
 				 errmsg("cannot have multiple SET TABLESPACE subcommands")));
+}
 
-	tab->newTableSpace = tablespaceId;
+/*
+ * ALTER TABLE SET [TABLE|TOAST] TABLESPACE
+ */
+static void
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename,
+					LOCKMODE lockmode, AlterTableType subtype)
+{
+	Oid			tablespaceId;
+	/* Check that the tablespace exists */
+	tablespaceId = get_tablespace_oid(tablespacename, false);
+	switch (subtype)
+	{
+		case AT_SetTableSpace:	/* SET TABLESPACE */
+			CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace);
+			tab->newTableSpace = tablespaceId;
+			tab->newToastTableSpace = tablespaceId;
+			break;
+		case AT_SetTableTableSpace:	/* SET TABLE TABLESPACE */
+			CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace);
+			tab->newTableSpace = tablespaceId;
+			tab->newToastTableSpace = InvalidOid;
+			break;
+		case AT_SetToastTableSpace:	/* SET TOAST TABLESPACE */
+			CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newToastTableSpace);
+			tab->newTableSpace = InvalidOid;
+			tab->newToastTableSpace = tablespaceId;
+			if (!rel->rd_rel->reltoastrelid || !OidIsValid(rel->rd_rel->reltoastrelid))
+				ereport(NOTICE,
+					(errmsg("table \"%s\" does not have any TOAST table.",
+						RelationGetRelationName(rel))));
+			break;
+		default:
+			/* Should not happen. */
+			break;
+	}
 }
 
 /*
@@ -9184,12 +9281,43 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
 	heap_close(pgclass, RowExclusiveLock);
 }
 
+
+static void
+RelationIsMoveableToNewTablespace(Relation rel, Oid NewTableSpaceOid)
+{
+	/*
+	 * We cannot support moving mapped relations into different tablespaces.
+	 * (In particular this eliminates all shared catalogs.)
+	 */
+	if (RelationIsMapped(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot move system relation \"%s\"",
+						RelationGetRelationName(rel))));
+
+	/* Can't move a non-shared relation into pg_global */
+	if (NewTableSpaceOid == GLOBALTABLESPACE_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("only shared relations can be placed in pg_global tablespace")));
+
+	/*
+	 * Don't allow moving temp tables of other backends ... their local buffer
+	 * manager is not going to cope.
+	 */
+	if (RELATION_IS_OTHER_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot move temporary tables of other sessions")));
+}
+
 /*
- * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
+ * Execute ALTER TABLE SET [TABLE] TABLESPACE for cases where there is no tuple
  * rewriting to be done, so we just want to copy the data as fast as possible.
  */
 static void
-ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
+ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode,
+					Oid newToastTableSpace)
 {
 	Relation	rel;
 	Oid			oldTableSpace;
@@ -9223,30 +9351,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 		return;
 	}
 
-	/*
-	 * We cannot support moving mapped relations into different tablespaces.
-	 * (In particular this eliminates all shared catalogs.)
-	 */
-	if (RelationIsMapped(rel))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot move system relation \"%s\"",
-						RelationGetRelationName(rel))));
-
-	/* Can't move a non-shared relation into pg_global */
-	if (newTableSpace == GLOBALTABLESPACE_OID)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("only shared relations can be placed in pg_global tablespace")));
-
-	/*
-	 * Don't allow moving temp tables of other backends ... their local buffer
-	 * manager is not going to cope.
-	 */
-	if (RELATION_IS_OTHER_TEMP(rel))
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot move temporary tables of other sessions")));
+	RelationIsMoveableToNewTablespace(rel, newTableSpace);
 
 	reltoastrelid = rel->rd_rel->reltoastrelid;
 	/* Fetch the list of indexes on toast relation if necessary */
@@ -9335,10 +9440,73 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 	CommandCounterIncrement();
 
 	/* Move associated toast relation and/or indexes, too */
+	if (OidIsValid(newToastTableSpace))
+	{
+		if (OidIsValid(reltoastrelid))
+			ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode,
+								InvalidOid);
+
+		foreach(lc, reltoastidxids)
+			ATExecSetTableSpace(lfirst_oid(lc), newToastTableSpace, lockmode,
+								InvalidOid);
+	}
+
+	/* Clean up */
+	list_free(reltoastidxids);
+}
+
+/*
+ * Execute ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATExecSetToastTableSpace(Oid tableOid, Oid newToastTableSpace, LOCKMODE lockmode)
+{
+	Relation	rel;
+	Oid			oldToastTableSpace;
+	Oid			reltoastrelid;
+	List	   *reltoastidxids = NIL;
+	ListCell   *lc;
+	Relation 	relToast;
+
+	/*
+	 * Need lock here in case we are recursing to toast table or index
+	 */
+	rel = relation_open(tableOid, lockmode);
+
+	/*
+	 * Need to know old TOAST tablespace
+	 */
+	reltoastrelid = rel->rd_rel->reltoastrelid;
 	if (OidIsValid(reltoastrelid))
-		ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
+	{
+		relToast = relation_open(reltoastrelid, NoLock);
+
+		oldToastTableSpace = relToast->rd_rel->reltablespace;
+		if (newToastTableSpace == oldToastTableSpace ||
+			(newToastTableSpace == MyDatabaseTableSpace && oldToastTableSpace == 0))
+		{
+			relation_close(rel, NoLock);
+			relation_close(relToast, NoLock);
+			return;
+		}
+
+		reltoastidxids = RelationGetIndexList(relToast);
+		relation_close(relToast, NoLock);
+	}
+	else
+	{
+		relation_close(rel, NoLock);
+		return;
+	}
+
+	RelationIsMoveableToNewTablespace(rel, newToastTableSpace);
+	relation_close(rel, NoLock);
+
+	ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, InvalidOid);
+
 	foreach(lc, reltoastidxids)
-		ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
+		ATExecSetTableSpace(lfirst_oid(lc), newToastTableSpace, lockmode,
+							InvalidOid);
 
 	/* Clean up */
 	list_free(reltoastidxids);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 581f7a1..79f4ec4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -612,7 +612,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	SYMMETRIC SYSID SYSTEM_P
 
 	TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
-	TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
+	TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
 	TRUNCATE TRUSTED TYPE_P TYPES_P
 
 	UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
@@ -2275,6 +2275,22 @@ alter_table_cmd:
 					n->name = $3;
 					$$ = (Node *)n;
 				}
+			/* ALTER TABLE <name> SET TOAST TABLESPACE <tablespacename> */
+			| SET TOAST TABLESPACE name
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_SetToastTableSpace;
+					n->name = $4;
+					$$ = (Node *)n;
+				}
+			/* ALTER TABLE <name> SET TABLE TABLESPACE <tablespacename> */
+			| SET TABLE TABLESPACE name
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_SetTableTableSpace;
+					n->name = $4;
+					$$ = (Node *)n;
+				}
 			/* ALTER TABLE <name> SET (...) */
 			| SET reloptions
 				{
@@ -13414,6 +13430,7 @@ unreserved_keyword:
 			| TEMPLATE
 			| TEMPORARY
 			| TEXT_P
+			| TOAST
 			| TRANSACTION
 			| TRIGGER
 			| TRUNCATE
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 6d26986..a6859cf 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -952,7 +952,8 @@ ProcessUtilitySlow(Node *parsetree,
 												   toast_options,
 												   true);
 
-							NewRelationCreateToastTable(relOid, toast_options);
+							NewRelationCreateToastTable(relOid, toast_options,
+														InvalidOid);
 						}
 						else if (IsA(stmt, CreateForeignTableStmt))
 						{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2b53c72..9b84b12 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -4577,6 +4577,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	int			i_owning_tab;
 	int			i_owning_col;
 	int			i_reltablespace;
+	int			i_reltoasttablespace;
 	int			i_reloptions;
 	int			i_checkoption;
 	int			i_toastreloptions;
@@ -4631,7 +4632,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "array_to_string(array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded'), ', ') AS reloptions, "
 						  "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
 						  "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, "
-						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, "
+						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = tc.reltablespace) AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4752,7 +4754,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "d.refobjsubid AS owning_col, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
-						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4791,7 +4794,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "d.refobjsubid AS owning_col, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
-						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4829,7 +4833,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "d.refobjsubid AS owning_col, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
-						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+						  "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4867,7 +4872,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "d.refobjsubid AS owning_col, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						"array_to_string(c.reloptions, ', ') AS reloptions, "
-						  "NULL AS toast_reloptions "
+						  "NULL AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4905,7 +4911,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "d.refobjsubid AS owning_col, "
 						  "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
 						  "NULL AS reloptions, "
-						  "NULL AS toast_reloptions "
+						  "NULL AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4942,7 +4949,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "d.refobjsubid AS owning_col, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
-						  "NULL AS toast_reloptions "
+						  "NULL AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "LEFT JOIN pg_depend d ON "
 						  "(c.relkind = '%c' AND "
@@ -4975,7 +4983,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL::int4 AS owning_col, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
-						  "NULL AS toast_reloptions "
+						  "NULL AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class "
 						  "WHERE relkind IN ('%c', '%c', '%c') "
 						  "ORDER BY oid",
@@ -5003,7 +5012,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL::int4 AS owning_col, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
-						  "NULL AS toast_reloptions "
+						  "NULL AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class "
 						  "WHERE relkind IN ('%c', '%c', '%c') "
 						  "ORDER BY oid",
@@ -5041,7 +5051,8 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 						  "NULL::int4 AS owning_col, "
 						  "NULL AS reltablespace, "
 						  "NULL AS reloptions, "
-						  "NULL AS toast_reloptions "
+						  "NULL AS toast_reloptions, "
+						  "NULL AS reltoasttablespace "
 						  "FROM pg_class c "
 						  "WHERE relkind IN ('%c', '%c') "
 						  "ORDER BY oid",
@@ -5096,6 +5107,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 	i_checkoption = PQfnumber(res, "checkoption");
 	i_toastreloptions = PQfnumber(res, "toast_reloptions");
 	i_reloftype = PQfnumber(res, "reloftype");
+	i_reltoasttablespace = PQfnumber(res, "reltoasttablespace");
 
 	if (dopt->lockWaitTimeout && fout->remoteVersion >= 70300)
 	{
@@ -5162,6 +5174,7 @@ getTables(Archive *fout, DumpOptions *dopt, int *numTables)
 		else
 			tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
 		tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
+		tblinfo[i].reltoasttablespace = pg_strdup(PQgetvalue(res, i, i_reltoasttablespace));
 
 		/* other fields were zeroed above */
 
@@ -13774,6 +13787,16 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 			appendPQExpBuffer(q, " AS\n%s\n  WITH NO DATA;\n",
 							  result->data);
 			destroyPQExpBuffer(result);
+
+			/* Change TOAST tablespace */
+			if (strlen(tbinfo->reltoasttablespace) > 0)
+			{
+				appendPQExpBuffer(q, "ALTER MATERIALIZED VIEW %s ",
+				fmtId(tbinfo->dobj.name));
+				appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n",
+					tbinfo->reltoasttablespace);
+			}
+
 		}
 		else
 			appendPQExpBufferStr(q, ";\n");
@@ -14047,6 +14070,15 @@ dumpTableSchema(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo)
 		}
 	}
 
+	/* Change TOAST tablespace */
+	if (strlen(tbinfo->reltoasttablespace) > 0 && tbinfo->relkind == RELKIND_RELATION)
+	{
+		appendPQExpBuffer(q, "ALTER TABLE %s ",
+			fmtId(tbinfo->dobj.name));
+		appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n",
+			tbinfo->reltoasttablespace);
+	}
+
 	if (dopt->binary_upgrade)
 		binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data);
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index f42c42d..6cb7362 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -204,6 +204,7 @@ typedef struct _tableInfo
 	bool		relispopulated; /* relation is populated */
 	bool		relreplident;	/* replica identifier */
 	char	   *reltablespace;	/* relation tablespace */
+	char	   *reltoasttablespace;	/* TOAST relation tablespace */
 	char	   *reloptions;		/* options specified by WITH (...) */
 	char	   *checkoption;	/* WITH CHECK OPTION */
 	char	   *toast_reloptions;		/* WITH options for the TOAST table */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index c44e447..2048f00 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -31,6 +31,7 @@ static bool describeOneTableDetails(const char *schemaname,
 						bool verbose);
 static void add_tablespace_footer(printTableContent *const cont, char relkind,
 					  Oid tablespace, const bool newline);
+static void add_toasttablespace_footer(printTableContent *const cont, Oid toasttablespace);
 static void add_role_attribute(PQExpBuffer buf, const char *const str);
 static bool listTSParsersVerbose(const char *pattern);
 static bool describeOneTSParser(const char *oid, const char *nspname,
@@ -1230,6 +1231,7 @@ describeOneTableDetails(const char *schemaname,
 		bool		rowsecurity;
 		bool		hasoids;
 		Oid			tablespace;
+		Oid			toasttablespace;
 		char	   *reloptions;
 		char	   *reloftype;
 		char		relpersistence;
@@ -1254,7 +1256,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
 						  "c.relhastriggers, c.relrowsecurity, c.relhasoids, "
-						  "%s, c.reltablespace, "
+						  "%s, c.reltablespace, tc.reltablespace, "
 						  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
 						  "c.relpersistence, c.relreplident\n"
 						  "FROM pg_catalog.pg_class c\n "
@@ -1271,7 +1273,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
 						  "c.relhastriggers, false, c.relhasoids, "
-						  "%s, c.reltablespace, "
+						  "%s, c.reltablespace, tc.reltablespace, "
 						  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
 						  "c.relpersistence, c.relreplident\n"
 						  "FROM pg_catalog.pg_class c\n "
@@ -1288,7 +1290,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
 						  "c.relhastriggers, false, c.relhasoids, "
-						  "%s, c.reltablespace, "
+						  "%s, c.reltablespace, tc.reltablespace, "
 						  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, "
 						  "c.relpersistence\n"
 						  "FROM pg_catalog.pg_class c\n "
@@ -1305,7 +1307,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
 						  "c.relhastriggers, false, c.relhasoids, "
-						  "%s, c.reltablespace, "
+						  "%s, c.reltablespace, tc.reltablespace, "
 						  "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n"
 						  "FROM pg_catalog.pg_class c\n "
 		   "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
@@ -1321,7 +1323,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 			  "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
 						  "c.relhastriggers, false, c.relhasoids, "
-						  "%s, c.reltablespace\n"
+						  "%s, c.reltablespace, tc.reltablespace\n"
 						  "FROM pg_catalog.pg_class c\n "
 		   "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n"
 						  "WHERE c.oid = '%s';",
@@ -1336,7 +1338,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 					  "SELECT relchecks, relkind, relhasindex, relhasrules, "
 						  "reltriggers <> 0, false, relhasoids, "
-						  "%s, reltablespace\n"
+						  "%s, reltablespace, ''\n"
 						  "FROM pg_catalog.pg_class WHERE oid = '%s';",
 						  (verbose ?
 					 "pg_catalog.array_to_string(reloptions, E', ')" : "''"),
@@ -1347,7 +1349,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 					  "SELECT relchecks, relkind, relhasindex, relhasrules, "
 						  "reltriggers <> 0, false, relhasoids, "
-						  "'', reltablespace\n"
+						  "'', reltablespace, ''\n"
 						  "FROM pg_catalog.pg_class WHERE oid = '%s';",
 						  oid);
 	}
@@ -1356,7 +1358,7 @@ describeOneTableDetails(const char *schemaname,
 		printfPQExpBuffer(&buf,
 					  "SELECT relchecks, relkind, relhasindex, relhasrules, "
 						  "reltriggers <> 0, false, relhasoids, "
-						  "'', ''\n"
+						  "'', '', ''\n"
 						  "FROM pg_catalog.pg_class WHERE oid = '%s';",
 						  oid);
 	}
@@ -1384,13 +1386,15 @@ describeOneTableDetails(const char *schemaname,
 		pg_strdup(PQgetvalue(res, 0, 7)) : NULL;
 	tableinfo.tablespace = (pset.sversion >= 80000) ?
 		atooid(PQgetvalue(res, 0, 8)) : 0;
+	tableinfo.toasttablespace = (pset.sversion >= 80400) ?
+		atooid(PQgetvalue(res, 0, 9)) : 0;
 	tableinfo.reloftype = (pset.sversion >= 90000 &&
-						   strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
-		pg_strdup(PQgetvalue(res, 0, 9)) : NULL;
+						   strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
+		pg_strdup(PQgetvalue(res, 0, 10)) : NULL;
 	tableinfo.relpersistence = (pset.sversion >= 90100) ?
-		*(PQgetvalue(res, 0, 10)) : 0;
+		*(PQgetvalue(res, 0, 11)) : 0;
 	tableinfo.relreplident = (pset.sversion >= 90400) ?
-		*(PQgetvalue(res, 0, 11)) : 'd';
+		*(PQgetvalue(res, 0, 12)) : 'd';
 	PQclear(res);
 	res = NULL;
 
@@ -1771,6 +1775,7 @@ describeOneTableDetails(const char *schemaname,
 			printTableAddFooter(&cont, tmpbuf.data);
 			add_tablespace_footer(&cont, tableinfo.relkind,
 								  tableinfo.tablespace, true);
+			add_toasttablespace_footer(&cont, tableinfo.toasttablespace);
 		}
 
 		PQclear(result);
@@ -2520,6 +2525,7 @@ describeOneTableDetails(const char *schemaname,
 		/* Tablespace info */
 		add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
 							  true);
+		add_toasttablespace_footer(&cont, tableinfo.toasttablespace);
 	}
 
 	/* reloptions, if verbose */
@@ -2628,6 +2634,37 @@ add_tablespace_footer(printTableContent *const cont, char relkind,
 }
 
 /*
+ * Add a TOAST tablespace description to a footer.
+ */
+static void
+add_toasttablespace_footer(printTableContent *const cont, Oid toasttablespace)
+{
+	if (toasttablespace != 0)
+	{
+		PGresult   *result = NULL;
+		PQExpBufferData buf;
+
+		initPQExpBuffer(&buf);
+		printfPQExpBuffer(&buf,
+			"SELECT spcname FROM pg_catalog.pg_tablespace\n"
+			"WHERE oid = '%u';", toasttablespace);
+		result = PSQLexec(buf.data);
+		if (!result)
+			return;
+		/* Should always be the case, but.... */
+		if (PQntuples(result) > 0)
+		{
+			/* Add the TOAST tablespace as a new footer */
+			printfPQExpBuffer(&buf, _("TOAST Tablespace: \"%s\""),
+					PQgetvalue(result, 0, 0));
+			printTableAddFooter(cont, buf.data);
+		}
+		PQclear(result);
+		termPQExpBuffer(&buf);
+	}
+}
+
+/*
  * \du or \dg
  *
  * Describes roles.  Any schema portion of the pattern is ignored.
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index e39a07c..5015476 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1396,11 +1396,37 @@ psql_completion(const char *text, int start, int end)
 			 pg_strcasecmp(prev2_wd, "VIEW") == 0)
 	{
 		static const char *const list_ALTERMATVIEW[] =
-		{"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET SCHEMA", NULL};
+		{"ALTER COLUMN", "OWNER TO", "RENAME TO", "SET", NULL};
+
+		COMPLETE_WITH_LIST(list_ALTERMATVIEW);
+	}
+	/* ALTER MATERIALIZED VIEW <name> SET */
+	else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev4_wd, "MATERIALIZED") == 0 &&
+			 pg_strcasecmp(prev3_wd, "VIEW") == 0 &&
+			 pg_strcasecmp(prev_wd, "SET") == 0)
+	{
+		static const char *const list_ALTERMATVIEW[] =
+		{"SCHEMA", "TOAST TABLESPACE", "TABLESPACE", "TABLE TABLESPACE", NULL};
+
+		COMPLETE_WITH_LIST(list_ALTERMATVIEW);
+	}
+	/* ALTER MATERIALIZED VIEW <name> SET (TABLE|TOAST) */
+	else if (pg_strcasecmp(prev6_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev5_wd, "MATERIALIZED") == 0 &&
+			 pg_strcasecmp(prev4_wd, "VIEW") == 0 &&
+			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
+			 (pg_strcasecmp(prev_wd, "TABLE") == 0 ||
+			  pg_strcasecmp(prev_wd, "TOAST") == 0))
+	{
+		static const char *const list_ALTERMATVIEW[] =
+		{"TABLESPACE", NULL};
 
 		COMPLETE_WITH_LIST(list_ALTERMATVIEW);
 	}
 
+
+
 	/* ALTER POLICY <name> ON */
 	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
 			 pg_strcasecmp(prev2_wd, "POLICY") == 0)
@@ -1743,7 +1769,7 @@ psql_completion(const char *text, int start, int end)
 			 pg_strcasecmp(prev_wd, "SET") == 0)
 	{
 		static const char *const list_TABLESET[] =
-		{"(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT", NULL};
+		{"(", "LOGGED", "SCHEMA", "TABLESPACE", "TABLE TABLESPACE", "TOAST TABLESPACE", "UNLOGGED", "WITH", "WITHOUT", NULL};
 
 		COMPLETE_WITH_LIST(list_TABLESET);
 	}
@@ -1752,6 +1778,26 @@ psql_completion(const char *text, int start, int end)
 			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
 			 pg_strcasecmp(prev_wd, "TABLESPACE") == 0)
 		COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces);
+	/* If we have ALTER TABLE <sth> SET TABLE provide TABLESPACE */
+	else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
+			 pg_strcasecmp(prev_wd, "TABLE") == 0)
+	{
+		static const char *const list_TABLETABLESPACE[] =
+		{"TABLESPACE", NULL};
+		COMPLETE_WITH_LIST(list_TABLETABLESPACE);
+	}
+	/* If we have ALTER TABLE <sth> SET TOAST provide TABLESPACE */
+	else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 &&
+			 pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
+			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
+			 pg_strcasecmp(prev_wd, "TOAST") == 0)
+	{
+		static const char *const list_TOASTTABLESPACE[] =
+		{"TABLESPACE", NULL};
+		COMPLETE_WITH_LIST(list_TOASTTABLESPACE);
+	}
 	/* If we have TABLE <sth> SET WITHOUT provide CLUSTER or OIDS */
 	else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 &&
 			 pg_strcasecmp(prev2_wd, "SET") == 0 &&
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index cba4ae7..39f1975 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -19,11 +19,12 @@
 /*
  * toasting.c prototypes
  */
-extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
+extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions,
+							Oid toastTableSpace);
 extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
-						LOCKMODE lockmode);
+						LOCKMODE lockmode, Oid toastTableSpace);
 extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
-						   LOCKMODE lockmode);
+						   LOCKMODE lockmode, Oid toastTableSpace, bool new_toast);
 extern void BootstrapToastTable(char *relName,
 					Oid toastOid, Oid toastIndexOid);
 
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 098d09b..bcccd87 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -26,7 +26,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
 extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);
 
 extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
-			  LOCKMODE lockmode);
+			  LOCKMODE lockmode, Oid NewToastTableSpace);
 extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
 				 bool is_system_catalog,
 				 bool swap_toast_by_content,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac13302..8a34ba6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1325,6 +1325,8 @@ typedef enum AlterTableType
 	AT_AddOidsRecurse,			/* internal to commands/tablecmds.c */
 	AT_DropOids,				/* SET WITHOUT OIDS */
 	AT_SetTableSpace,			/* SET TABLESPACE */
+	AT_SetTableTableSpace,		/* SET TABLE TABLESPACE */
+	AT_SetToastTableSpace,		/* SET TOAST TABLESPACE */
 	AT_SetRelOptions,			/* SET (...) -- AM specific parameters */
 	AT_ResetRelOptions,			/* RESET (...) -- AM specific parameters */
 	AT_ReplaceRelOptions,		/* replace reloption list in its entirety */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7c243ec..ce907f0 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -375,6 +375,7 @@ PG_KEYWORD("then", THEN, RESERVED_KEYWORD)
 PG_KEYWORD("time", TIME, COL_NAME_KEYWORD)
 PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD)
 PG_KEYWORD("to", TO, RESERVED_KEYWORD)
+PG_KEYWORD("toast", TOAST, UNRESERVED_KEYWORD)
 PG_KEYWORD("trailing", TRAILING, RESERVED_KEYWORD)
 PG_KEYWORD("transaction", TRANSACTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("treat", TREAT, COL_NAME_KEYWORD)
diff --git a/src/test/regress/GNUmakefile b/src/test/regress/GNUmakefile
index 110eb80..49ea024 100644
--- a/src/test/regress/GNUmakefile
+++ b/src/test/regress/GNUmakefile
@@ -125,7 +125,9 @@ submake-contrib-spi:
 .PHONY: tablespace-setup
 tablespace-setup:
 	rm -rf ./testtablespace
+	rm -rf ./testtablespace2
 	mkdir ./testtablespace
+	mkdir ./testtablespace2
 
 
 ##
@@ -176,4 +178,5 @@ clean distclean maintainer-clean: clean-lib
 # things created by various check targets
 	rm -f $(output_files) $(input_files)
 	rm -rf testtablespace
+	rm -rf testtablespace2
 	rm -rf $(pg_regress_clean_files)
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 75ec689..f05b95f 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -21,7 +21,7 @@ ALTER TABLESPACE testspace RESET (random_page_cost, seq_page_cost); -- ok
 CREATE SCHEMA testschema;
 
 -- try a table
-CREATE TABLE testschema.foo (i int) TABLESPACE testspace;
+CREATE TABLE testschema.foo (i int, label varchar) TABLESPACE testspace;
 SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
     where c.reltablespace = t.oid AND c.relname = 'foo';
 
@@ -64,6 +64,38 @@ CREATE TABLE bar (i int) TABLESPACE nosuchspace;
 -- Fail, not empty
 DROP TABLESPACE testspace;
 
+-- ALTER TABLE SET TOAST TABLESPACE
+CREATE TABLESPACE testspace2 LOCATION '@testtablespace2@';
+ALTER TABLE testschema.foo SET TOAST TABLESPACE nosuchspace;
+ALTER TABLE testschema.foo SET TABLE TABLESPACE nosuchspace;
+ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo';
+
+ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace2;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo';
+
+ALTER TABLE testschema.foo SET TABLESPACE testspace2;
+ALTER TABLE testschema.foo SET TABLE TABLESPACE testspace;
+SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo';
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo';
+
+CREATE TABLE testschema.foo2 (id SERIAL PRIMARY KEY, l text) TABLESPACE testspace2;
+ALTER TABLE testschema.foo2 SET TOAST TABLESPACE pg_default;
+CREATE MATERIALIZED VIEW testschema.foo_mv AS SELECT * FROM testschema.foo;
+ALTER MATERIALIZED VIEW testschema.foo_mv SET TOAST TABLESPACE testspace2;
+ALTER MATERIALIZED VIEW testschema.foo_mv SET TABLE TABLESPACE testspace;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo_mv';
+
+SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo_mv';
+
+ALTER MATERIALIZED VIEW testschema.foo_mv SET TABLESPACE pg_default;
+INSERT INTO testschema.foo2 (l) VALUES ('foo');
+UPDATE testschema.foo2 SET l = l||l;
+CLUSTER testschema.foo2 USING foo2_pkey;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo2';
+
+SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo2';
+
 CREATE ROLE tablespace_testuser1 login;
 CREATE ROLE tablespace_testuser2 login;
 
@@ -85,6 +117,7 @@ ALTER TABLE ALL IN TABLESPACE testspace_renamed SET TABLESPACE pg_default;
 DROP TABLESPACE testspace_renamed;
 
 DROP SCHEMA testschema CASCADE;
+DROP TABLESPACE testspace2;
 
 DROP ROLE tablespace_testuser1;
 DROP ROLE tablespace_testuser2;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index ca60650..96cb111 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -23,7 +23,7 @@ ALTER TABLESPACE testspace RESET (random_page_cost, seq_page_cost); -- ok
 -- create a schema we can use
 CREATE SCHEMA testschema;
 -- try a table
-CREATE TABLE testschema.foo (i int) TABLESPACE testspace;
+CREATE TABLE testschema.foo (i int, label varchar) TABLESPACE testspace;
 SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
     where c.reltablespace = t.oid AND c.relname = 'foo';
  relname |  spcname  
@@ -85,6 +85,72 @@ ERROR:  tablespace "nosuchspace" does not exist
 -- Fail, not empty
 DROP TABLESPACE testspace;
 ERROR:  tablespace "testspace" is not empty
+-- ALTER TABLE SET TOAST TABLESPACE
+CREATE TABLESPACE testspace2 LOCATION '@testtablespace2@';
+ALTER TABLE testschema.foo SET TOAST TABLESPACE nosuchspace;
+ERROR:  tablespace "nosuchspace" does not exist
+ALTER TABLE testschema.foo SET TABLE TABLESPACE nosuchspace;
+ERROR:  tablespace "nosuchspace" does not exist
+ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo';
+  spcname  
+-----------
+ testspace
+(1 row)
+
+ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace2;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo';
+  spcname   
+------------
+ testspace2
+(1 row)
+
+ALTER TABLE testschema.foo SET TABLESPACE testspace2;
+ALTER TABLE testschema.foo SET TABLE TABLESPACE testspace;
+SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo';
+  spcname  
+-----------
+ testspace
+(1 row)
+
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo';
+  spcname   
+------------
+ testspace2
+(1 row)
+
+CREATE TABLE testschema.foo2 (id SERIAL PRIMARY KEY, l text) TABLESPACE testspace2;
+ALTER TABLE testschema.foo2 SET TOAST TABLESPACE pg_default;
+CREATE MATERIALIZED VIEW testschema.foo_mv AS SELECT * FROM testschema.foo;
+ALTER MATERIALIZED VIEW testschema.foo_mv SET TOAST TABLESPACE testspace2;
+ALTER MATERIALIZED VIEW testschema.foo_mv SET TABLE TABLESPACE testspace;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo_mv';
+  spcname   
+------------
+ testspace2
+(1 row)
+
+SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo_mv';
+  spcname  
+-----------
+ testspace
+(1 row)
+
+ALTER MATERIALIZED VIEW testschema.foo_mv SET TABLESPACE pg_default;
+INSERT INTO testschema.foo2 (l) VALUES ('foo');
+UPDATE testschema.foo2 SET l = l||l;
+CLUSTER testschema.foo2 USING foo2_pkey;
+SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo2';
+ spcname 
+---------
+(0 rows)
+
+SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo2';
+  spcname   
+------------
+ testspace2
+(1 row)
+
 CREATE ROLE tablespace_testuser1 login;
 CREATE ROLE tablespace_testuser2 login;
 ALTER TABLESPACE testspace OWNER TO tablespace_testuser1;
@@ -101,10 +167,13 @@ 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
+NOTICE:  drop cascades to 6 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.foo2
+drop cascades to materialized view testschema.foo_mv
+DROP TABLESPACE testspace2;
 DROP ROLE tablespace_testuser1;
 DROP ROLE tablespace_testuser2;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 3af0e57..89ce698 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -495,6 +495,7 @@ static void
 convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix)
 {
 	char		testtablespace[MAXPGPATH];
+	char		testtablespace2[MAXPGPATH];
 	char		indir[MAXPGPATH];
 	struct stat st;
 	int			ret;
@@ -521,6 +522,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, c
 		exit(2);
 
 	snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
+	snprintf(testtablespace2, MAXPGPATH, "%s/testtablespace2", outputdir);
 
 #ifdef WIN32
 
@@ -542,6 +544,9 @@ convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, c
 			exit(2);
 		}
 	make_directory(testtablespace);
+	if (directory_exists(testtablespace2))
+		rmtree(testtablespace2, true);
+	make_directory(testtablespace2);
 #endif
 
 	/* finally loop on each file and do the replacement */
@@ -587,6 +592,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, c
 			replace_string(line, "@abs_srcdir@", inputdir);
 			replace_string(line, "@abs_builddir@", outputdir);
 			replace_string(line, "@testtablespace@", testtablespace);
+			replace_string(line, "@testtablespace2@", testtablespace2);
 			replace_string(line, "@libdir@", dlpath);
 			replace_string(line, "@DLSUFFIX@", DLSUFFIX);
 			fputs(line, outfile);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to