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 namestorage_parameter [, ... ] )
OWNER TO new_owner
SET TABLESPACE new_tablespace
+ SET TABLE TABLESPACE new_tablespace
+ SET TOAST TABLESPACE new_tablespace
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 name
SET WITH OIDS
SET WITHOUT OIDS
SET TABLESPACE new_tablespace
+ SET TABLE TABLESPACE new_tablespace
+ SET TOAST TABLESPACE new_tablespace
SET {LOGGED | UNLOGGED}
SET ( storage_parameter = value [, ... ] )
RESET ( storage_parameter [, ... ] )
@@ -501,7 +503,8 @@ ALTER TABLE ALL IN TABLESPACE name
SET TABLESPACE
- 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 SET TABLESPACE commands.
@@ -523,6 +526,31 @@ ALTER TABLE ALL IN TABLESPACE name
+ SET TABLE TABLESPACE
+
+
+ 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
+
+
+
+
+
+
+ SET TOAST TABLESPACE
+
+
+ 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 and
+ for more information.
+
+
+
+
+
SET {LOGGED | UNLOGGED}
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 module
can be used to examine the information stored in free space maps.
+
+TOAST table can be moved to a different tablespace with
+ALTER TABLE SET TOAST TABLESPACE>
+
+
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 SET TOAST TABLESPACE */
+ | SET TOAST TABLESPACE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetToastTableSpace;
+ n->name = $4;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE SET TABLE TABLESPACE */
+ | SET TABLE TABLESPACE name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetTableTableSpace;
+ n->name = $4;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE 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 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 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 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 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 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 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);