patch : Allow toast tables to be moved to a different tablespace
Hi,
Here's a patch to allow TOAST tables to be moved to a different
tablespace. This item has been picked up from the TODO list.
Main idea is to consider that a TOAST table can have its own tablespace.
Regards,
--
JT
Attachments:
set_toast_tablespace_v0.6.patchtext/x-patch; name=set_toast_tablespace_v0.6.patchDownload
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 00a477e..a2360f4 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -66,6 +66,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
NOT OF
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>
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
@@ -549,6 +551,30 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET TABLE TABLESPACE</literal></term>
+ <listitem>
+ <para>
+ This form changes only table's tablespace (not associated TOAST table's tablespace)
+ 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
+ <xref linkend="SQL-CREATETABLESPACE">
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><literal>RENAME</literal></term>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 0a133bb..d7d4235 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -422,6 +422,11 @@ pages). There was no run time difference compared to an un-<acronym>TOAST</>ed
comparison table, in which all the HTML pages were cut down to 7 kB to fit.
</para>
+<para>
+TOAST table can be moved to a different tablespace with
+<command>ALTER TABLE SET TOAST TABLESPACE</>
+</para>
+
</sect1>
<sect1 id="storage-fsm">
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index a938c98..7ad965e 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@ extern Oid binary_upgrade_next_toast_pg_class_oid;
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
- Datum reloptions);
+ Datum reloptions, Oid toastTableSpace);
static bool needs_toast_table(Relation rel);
@@ -53,19 +53,30 @@ static bool needs_toast_table(Relation rel);
* to end with CommandCounterIncrement if it makes any changes.
*/
void
-AlterTableCreateToastTable(Oid relOid, Datum reloptions)
+AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace)
{
Relation rel;
-
+ Relation toast_rel;
/*
* Grab a DDL-exclusive lock on the target table, since we'll update the
* pg_class tuple. This is redundant for all present users. Tuple
* toasting behaves safely in the face of a concurrent TOAST table add.
*/
rel = heap_open(relOid, ShareUpdateExclusiveLock);
+
+ /*
+ * if NewToastTableSpace is null then try to find old TOAST table's tablespace
+ */
+ if (!OidIsValid(toastTableSpace) && OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ toast_rel = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+ if (OidIsValid(toast_rel->rd_rel->reltablespace))
+ toastTableSpace = toast_rel->rd_rel->reltablespace;
+ relation_close(toast_rel, NoLock);
+ }
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
+ (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, toastTableSpace);
heap_close(rel, NoLock);
}
@@ -91,7 +102,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
relName)));
/* create_toast_table does all the work */
- if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
+ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,InvalidOid))
elog(ERROR, "\"%s\" does not require a toast table",
relName);
@@ -107,7 +118,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
* bootstrap they can be nonzero to specify hand-assigned OIDs
*/
static bool
-create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
+create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, Oid toastTableSpace)
{
Oid relOid = RelationGetRelid(rel);
HeapTuple reltup;
@@ -207,10 +218,15 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
toast_typid = binary_upgrade_next_toast_pg_type_oid;
binary_upgrade_next_toast_pg_type_oid = InvalidOid;
}
+
+ /* Use table's tablespace if toastTableSpace is null */
+ if (!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,
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 8200d20..bd23c8b 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -543,6 +543,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
bool is_system_catalog;
bool swap_toast_by_content;
TransactionId frozenXid;
+ Oid ToastTableSpace;
+ Relation ToastRel;
/* Mark the correct index as clustered */
if (OidIsValid(indexOid))
@@ -551,11 +553,25 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
/* 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 = tableSpace;
+
/* 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);
+ OIDNewHeap = make_new_heap(tableOid, tableSpace, ToastTableSpace);
/* Copy the heap data into the new table in the desired order */
copy_heap_data(OIDNewHeap, tableOid, indexOid,
@@ -581,7 +597,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
* data, then call finish_heap_swap to complete the operation.
*/
Oid
-make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
+make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewToastTableSpace)
{
TupleDesc OldHeapDesc;
char NewHeapName[NAMEDATALEN];
@@ -679,7 +695,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
if (isNull)
reloptions = (Datum) 0;
- AlterTableCreateToastTable(OIDNewHeap, reloptions);
+ AlterTableCreateToastTable(OIDNewHeap, reloptions, NewToastTableSpace);
ReleaseSysCache(tuple);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1e8ad2b..fa6d1a8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -147,7 +147,8 @@ typedef struct AlteredTableInfo
List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */
bool rewrite; /* T if a rewrite is forced */
- Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newToastTableSpace; /* new TOAST tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
List *changedConstraintDefs; /* string definitions of same */
@@ -356,8 +357,11 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
- char *tablespacename, LOCKMODE lockmode);
-static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+ char *tablespacename, LOCKMODE lockmode, bool table_only);
+static void ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel,
+ char *tablespacename, LOCKMODE lockmode);
+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, bool isReset, LOCKMODE lockmode);
static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
char fires_when, bool skip_system, LOCKMODE lockmode);
@@ -2980,7 +2984,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
/* This command never recurses */
- ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, false);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetTableTableSpace: /* SET TABLE TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
+ /* This command never recurses */
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, true);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetToastTableSpace: /* SET TOAST TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
+ /* This command never recurses */
+ ATPrepSetToastTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
@@ -3099,7 +3115,7 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
if (tab->relkind == RELKIND_RELATION)
- AlterTableCreateToastTable(tab->relid, (Datum) 0);
+ AlterTableCreateToastTable(tab->relid, (Datum) 0, InvalidOid);
}
}
@@ -3227,6 +3243,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 (...) */
ATExecSetRelOptions(rel, (List *) cmd->def, false, lockmode);
break;
@@ -3362,6 +3390,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
Relation OldHeap;
Oid OIDNewHeap;
Oid NewTableSpace;
+ Oid NewToastTableSpace;
OldHeap = heap_open(tab->relid, NoLock);
@@ -3393,11 +3422,16 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
NewTableSpace = tab->newTableSpace;
else
NewTableSpace = OldHeap->rd_rel->reltablespace;
+
+ if (tab->newToastTableSpace)
+ NewToastTableSpace = tab->newToastTableSpace;
+ else
+ NewToastTableSpace = InvalidOid;
heap_close(OldHeap, NoLock);
/* Create transient table that will receive the modified data */
- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace);
+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewToastTableSpace);
/*
* Copy the heap data into the new table with the desired
@@ -3431,7 +3465,9 @@ ATRewriteTables(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);
}
}
@@ -8027,30 +8063,66 @@ 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)
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid)
{
- Oid tablespaceId;
AclResult aclresult;
-
- /* Check that the tablespace exists */
- tablespaceId = get_tablespace_oid(tablespacename, false);
-
/* Check its permissions */
- 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")));
+}
+
+/*
+ * ALTER TABLE SET [TABLE] TABLESPACE
+ */
+static void
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode, bool table_only)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace);
+
+ /* Save tablespace Oid */
tab->newTableSpace = tablespaceId;
+
+ /* The case when we want to move only table location not its TOAST table */
+ if (table_only)
+ tab->newToastTableSpace = 0;
+ else
+ tab->newToastTableSpace = tablespaceId;
+
+}
+
+/*
+ * ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newToastTableSpace);
+
+ /* Save TOAST tablespace Oid */
+ tab->newToastTableSpace = tablespaceId;
}
+
/*
* ALTER TABLE/INDEX SET (...) or RESET (...)
*/
@@ -8178,12 +8250,42 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
heap_close(pgclass, RowExclusiveLock);
}
+
+extern void
+RelationIsMoveableToNewTablespace(Relation rel, Oid newTableSpace)
+{
+ /*
+ * 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")));
+}
+
/*
- * 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;
@@ -8212,31 +8314,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
relation_close(rel, NoLock);
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;
reltoastidxid = rel->rd_rel->reltoastidxid;
@@ -8316,12 +8395,69 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
CommandCounterIncrement();
/* Move associated toast relation and/or index, too */
- if (OidIsValid(reltoastrelid))
- ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
+ if (newToastTableSpace != 0)
+ {
+ if (OidIsValid(reltoastrelid))
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
+ if (OidIsValid(reltoastidxid))
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+ }
+}
+
+
+/*
+ * Execute ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATExecSetToastTableSpace(Oid tableOid, Oid newToastTableSpace, LOCKMODE lockmode)
+{
+ Relation rel;
+ Oid oldToastTableSpace;
+ Oid reltoastrelid;
+ Oid reltoastidxid;
+ 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
+ */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ relToast = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+
+ oldToastTableSpace = relToast->rd_rel->reltablespace;
+ if (newToastTableSpace == oldToastTableSpace ||
+ (newToastTableSpace == MyDatabaseTableSpace && oldToastTableSpace == 0))
+ {
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+ return;
+ }
+ }
+ else
+ {
+ relation_close(rel, NoLock);
+ return;
+ }
+
+ reltoastrelid = rel->rd_rel->reltoastrelid;
+ reltoastidxid = rel->rd_rel->reltoastidxid;
+
+ RelationIsMoveableToNewTablespace(rel, newToastTableSpace);
+
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
if (OidIsValid(reltoastidxid))
- ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+
}
+
/*
* Copy data, block by block
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fd7a9ed..a5e78bd 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2509,7 +2509,7 @@ OpenIntoRel(QueryDesc *queryDesc)
(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
- AlterTableCreateToastTable(intoRelationId, reloptions);
+ AlterTableCreateToastTable(intoRelationId, reloptions, InvalidOid);
/*
* And open the constructed table for writing.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e9f3896..47f8905 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -558,7 +558,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
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
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
@@ -1985,6 +1985,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
{
@@ -12057,6 +12073,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 0749227..0e65f8e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -552,7 +552,7 @@ standard_ProcessUtility(Node *parsetree,
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
true);
- AlterTableCreateToastTable(relOid, toast_options);
+ AlterTableCreateToastTable(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 b4ab19d..86ca6fa 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3832,6 +3832,7 @@ getTables(int *numTables)
int i_owning_tab;
int i_owning_col;
int i_reltablespace;
+ int i_reltoasttablespace;
int i_reloptions;
int i_toastreloptions;
int i_reloftype;
@@ -3859,7 +3860,44 @@ getTables(int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (g_fout->remoteVersion >= 90100)
+ if (g_fout->remoteVersion >= 90200)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any (note this dependency is AUTO as of 8.2)
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.relname, "
+ "c.relacl, c.relkind, c.relnamespace, "
+ "(%s c.relowner) AS rolname, "
+ "c.relchecks, c.relhastriggers, "
+ "c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relfrozenxid, tc.oid AS toid, "
+ "tc.relfrozenxid AS tfrozenxid, "
+ "c.relpersistence, "
+ "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+ "d.refobjid AS owning_tab, "
+ "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, "
+ "(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 "
+ "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.objsubid = 0 AND "
+ "d.refclassid = c.tableoid AND d.deptype = 'a') "
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
+ "ORDER BY c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_FOREIGN_TABLE);
+ }
+ else if (g_fout->remoteVersion >= 90100)
{
/*
* Left join to pick up dependency info linking sequences to their
@@ -3879,7 +3917,8 @@ getTables(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 "
@@ -3915,7 +3954,8 @@ getTables(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 "
@@ -3950,7 +3990,8 @@ getTables(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 "
@@ -3985,7 +4026,8 @@ getTables(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 "
@@ -4021,7 +4063,8 @@ getTables(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 "
@@ -4056,7 +4099,8 @@ getTables(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 "
@@ -4087,7 +4131,8 @@ getTables(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",
@@ -4113,7 +4158,8 @@ getTables(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",
@@ -4149,7 +4195,8 @@ getTables(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",
@@ -4198,6 +4245,7 @@ getTables(int *numTables)
i_reloptions = PQfnumber(res, "reloptions");
i_toastreloptions = PQfnumber(res, "toast_reloptions");
i_reloftype = PQfnumber(res, "reloftype");
+ i_reltoasttablespace = PQfnumber(res, "reltoasttablespace");
if (lockWaitTimeout && g_fout->remoteVersion >= 70300)
{
@@ -4252,6 +4300,7 @@ getTables(int *numTables)
tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions));
tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions));
+ tblinfo[i].reltoasttablespace = strdup(PQgetvalue(res, i, i_reltoasttablespace));
/* other fields were zeroed above */
@@ -12518,7 +12567,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
}
-
+ if (strlen(tbinfo->reltoasttablespace) > 0)
+ {
+ appendPQExpBuffer(q, "ALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n",
+ tbinfo->reltoasttablespace);
+ }
+
if (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 3d5d534..dd9cf4c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -241,6 +241,7 @@ typedef struct _tableInfo
char relkind;
char relpersistence; /* relation persistence */
char *reltablespace; /* relation tablespace */
+ char *reltoasttablespace; /* TOAST relation tablespace */
char *reloptions; /* options specified by WITH (...) */
char *toast_reloptions; /* ditto, for the TOAST table */
bool hasindex; /* does it have any indexes? */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d5466f8..d4e8d2c 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -29,6 +29,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,
@@ -1101,6 +1102,7 @@ describeOneTableDetails(const char *schemaname,
bool hastriggers;
bool hasoids;
Oid tablespace;
+ Oid toasttablespace;
char *reloptions;
char *reloftype;
char relpersistence;
@@ -1123,7 +1125,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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 "
@@ -1140,7 +1142,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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"
@@ -1156,7 +1158,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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';",
@@ -1171,7 +1173,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "%s, reltablespace\n"
+ "%s, reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(reloptions, E', ')" : "''"),
@@ -1182,7 +1184,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', reltablespace\n"
+ "'', reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1191,7 +1193,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', ''\n"
+ "'', '', ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1219,10 +1221,12 @@ describeOneTableDetails(const char *schemaname,
strdup(PQgetvalue(res, 0, 6)) : 0;
tableinfo.tablespace = (pset.sversion >= 80000) ?
atooid(PQgetvalue(res, 0, 7)) : 0;
- tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
- strdup(PQgetvalue(res, 0, 8)) : 0;
- tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
- PQgetvalue(res, 0, 9)[0] : 0;
+ tableinfo.toasttablespace = (pset.sversion >= 80400) ?
+ atooid(PQgetvalue(res, 0, 8)) : 0;
+ tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+ strdup(PQgetvalue(res, 0, 9)) : 0;
+ tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
+ PQgetvalue(res, 0, 10)[0] : 0;
PQclear(res);
res = NULL;
@@ -1567,6 +1571,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);
@@ -2171,6 +2176,7 @@ describeOneTableDetails(const char *schemaname,
add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
true);
+ add_toasttablespace_footer(&cont, tableinfo.toasttablespace);
}
printTable(&cont, pset.queryFout, pset.logfile);
@@ -2270,6 +2276,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, false);
+ 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 4f7df36..c5f183b 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -811,7 +811,7 @@ psql_completion(char *text, int start, int end)
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
"ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
- "USER", "USER MAPPING FOR", "VIEW", NULL};
+ "USER", "USER MAPPING FOR", "VIEW", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
@@ -1285,12 +1285,15 @@ psql_completion(char *text, int start, int end)
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
- /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
+ /*
+ * If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE, TOAST TABLESPACE,
+ * TABLE TABLESPACE and SCHEMA
+ */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
+ {"(", "WITHOUT", "TABLESPACE", "SCHEMA", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
@@ -1299,6 +1302,16 @@ psql_completion(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 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 de3623a..388ce5f 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -17,7 +17,7 @@
/*
* toasting.c prototypes
*/
-extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions);
+extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace);
extern void BootstrapToastTable(char *relName,
Oid toastOid, Oid toastIndexOid);
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 518e896..08b6181 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -25,7 +25,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck, LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid);
-extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace);
+extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, 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/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 333e303..d4aa99c 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,6 +24,10 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
extern void RemoveRelations(DropStmt *drop);
+extern void RelationIsMoveableToNewTablespace(Relation rel, Oid newTableSpace);
+
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid);
+
extern void AlterTable(AlterTableStmt *stmt);
extern LOCKMODE AlterTableGetLockLevel(List *cmds);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9998e2f..89a4f7d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1224,7 +1224,9 @@ typedef enum AlterTableType
AT_DropInherit, /* NO INHERIT parent */
AT_AddOf, /* OF <type_name> */
AT_DropOf, /* NOT OF */
- AT_GenericOptions /* OPTIONS (...) */
+ AT_GenericOptions, /* OPTIONS (...) */
+ AT_SetToastTableSpace, /* SET TOAST TABLESPACE */
+ AT_SetTableTableSpace /* SET TABLE TABLESPACE */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 12c2faf..1e37f97 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -363,6 +363,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 90aea6c..d8904db 100644
--- a/src/test/regress/GNUmakefile
+++ b/src/test/regress/GNUmakefile
@@ -125,7 +125,9 @@ $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX): $(top_builddir
.PHONY: tablespace-setup
tablespace-setup:
rm -rf ./testtablespace
+ rm -rf ./testtablespace2
mkdir ./testtablespace
+ mkdir ./testtablespace2
##
@@ -170,4 +172,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 dba96f4..230acaa 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -11,7 +11,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';
@@ -54,7 +54,21 @@ 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 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';
+
DROP SCHEMA testschema CASCADE;
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 1260c96..324cb07 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -10,7 +10,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
@@ -72,6 +72,36 @@ 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 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)
+
DROP SCHEMA testschema CASCADE;
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table testschema.foo
@@ -80,3 +110,4 @@ drop cascades to table testschema.asexecute
drop cascades to table testschema.atable
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index d9cd053..5ea6099 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -421,6 +421,7 @@ static void
convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
{
char testtablespace[MAXPGPATH];
+ char testtablespace2[MAXPGPATH];
char indir[MAXPGPATH];
struct stat st;
int ret;
@@ -447,6 +448,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
exit_nicely(2);
snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
+ snprintf(testtablespace2, MAXPGPATH, "%s/testtablespace2", outputdir);
#ifdef WIN32
@@ -463,6 +465,9 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
if (directory_exists(testtablespace))
rmtree(testtablespace, true);
make_directory(testtablespace);
+ if (directory_exists(testtablespace2))
+ rmtree(testtablespace2, true);
+ make_directory(testtablespace2);
#endif
/* finally loop on each file and do the replacement */
@@ -507,6 +512,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
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);
Julien Tachoires 10/07/11 10:17 AM >>>
Here's a patch to allow TOAST tables to be moved to a different
tablespace. This item has been picked up from the TODO list.
Main idea is to consider that a TOAST table can have its own
tablespace.
Thanks for the patch. Please add this to the open CommitFest at:
https://commitfest.postgresql.org/action/commitfest_view/open
That will help ensure we don't lose track of it before the next
review cycle. For more information on the review and commit process,
see this page:
http://wiki.postgresql.org/wiki/CommitFest
We are currently well in to a CF and still have patches which nobody
has yet volunteered to review. If you could help with that, it would
be great!
https://commitfest.postgresql.org/action/commitfest_view/inprogress
-Kevin
Import Notes
Resolved by subject fallback
On Fri, Oct 7, 2011 at 10:10 AM, Julien Tachoires <julmon@gmail.com> wrote:
Hi,
Here's a patch to allow TOAST tables to be moved to a different tablespace.
This item has been picked up from the TODO list.
Main idea is to consider that a TOAST table can have its own tablespace.
Hi,
This patch doesn't apply cleanly to head now... can you send a new
version against head?
about the patch itself. i don't like the fact that now the normal case
needs to include the word TABLE. IMHO, it should be optional and if
ommited TABLE should be assumed
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
2011/11/15 Jaime Casanova <jaime@2ndquadrant.com>:
On Fri, Oct 7, 2011 at 10:10 AM, Julien Tachoires <julmon@gmail.com> wrote:
Hi,
Here's a patch to allow TOAST tables to be moved to a different tablespace.
This item has been picked up from the TODO list.
Main idea is to consider that a TOAST table can have its own tablespace.Hi,
This patch doesn't apply cleanly to head now... can you send a new
version against head?
Hi Jaime,
New patch is attached.
about the patch itself. i don't like the fact that now the normal case
needs to include the word TABLE. IMHO, it should be optional and if
ommited TABLE should be assumed
Maybe I'd missed something, but the normal case is :
ALTER TABLE ... SET TABLESPACE => moves Table + moves associated TOAST Table
ALTER TABLE ... SET TABLE TABLESPACE => moves Table & keeps associated
TOAST Table at its place
ALTER TABLE ... SET TOAST TABLESPACE => keeps Table at its place &
moves associated TOAST Table
Regards,
--
JT
Attachments:
set_toast_tablespace_v0.7.patchtext/x-patch; charset=US-ASCII; name=set_toast_tablespace_v0.7.patchDownload
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 00a477e..a2360f4 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -66,6 +66,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
NOT OF
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>
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
@@ -549,6 +551,30 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>SET TABLE TABLESPACE</literal></term>
+ <listitem>
+ <para>
+ This form changes only table's tablespace (not associated TOAST table's tablespace)
+ 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
+ <xref linkend="SQL-CREATETABLESPACE">
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><literal>RENAME</literal></term>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index cb2f60e..e307422 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -427,6 +427,11 @@ pages). There was no run time difference compared to an un-<acronym>TOAST</>ed
comparison table, in which all the HTML pages were cut down to 7 kB to fit.
</para>
+<para>
+TOAST table can be moved to a different tablespace with
+<command>ALTER TABLE SET TOAST TABLESPACE</>
+</para>
+
</sect1>
<sect1 id="storage-fsm">
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3a40e8b..801767b 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@ extern Oid binary_upgrade_next_toast_pg_class_oid;
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
- Datum reloptions);
+ Datum reloptions, Oid toastTableSpace);
static bool needs_toast_table(Relation rel);
@@ -53,10 +53,10 @@ static bool needs_toast_table(Relation rel);
* to end with CommandCounterIncrement if it makes any changes.
*/
void
-AlterTableCreateToastTable(Oid relOid, Datum reloptions)
+AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace)
{
Relation rel;
-
+ Relation toast_rel;
/*
* Grab an exclusive lock on the target table, since we'll update its
* pg_class tuple. This is redundant for all present uses, since caller
@@ -65,9 +65,20 @@ AlterTableCreateToastTable(Oid relOid, Datum reloptions)
* so let's be safe.
*/
rel = heap_open(relOid, AccessExclusiveLock);
+
+ /*
+ * if NewToastTableSpace is null then try to find old TOAST table's tablespace
+ */
+ if (!OidIsValid(toastTableSpace) && OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ toast_rel = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+ if (OidIsValid(toast_rel->rd_rel->reltablespace))
+ toastTableSpace = toast_rel->rd_rel->reltablespace;
+ relation_close(toast_rel, NoLock);
+ }
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
+ (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, toastTableSpace);
heap_close(rel, NoLock);
}
@@ -93,7 +104,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
relName)));
/* create_toast_table does all the work */
- if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
+ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,InvalidOid))
elog(ERROR, "\"%s\" does not require a toast table",
relName);
@@ -109,7 +120,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
* bootstrap they can be nonzero to specify hand-assigned OIDs
*/
static bool
-create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
+create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, Oid toastTableSpace)
{
Oid relOid = RelationGetRelid(rel);
HeapTuple reltup;
@@ -209,10 +220,15 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
toast_typid = binary_upgrade_next_toast_pg_type_oid;
binary_upgrade_next_toast_pg_type_oid = InvalidOid;
}
+
+ /* Use table's tablespace if toastTableSpace is null */
+ if (!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,
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index edec44d..00327bf 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -543,6 +543,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
bool is_system_catalog;
bool swap_toast_by_content;
TransactionId frozenXid;
+ Oid ToastTableSpace;
+ Relation ToastRel;
/* Mark the correct index as clustered */
if (OidIsValid(indexOid))
@@ -551,11 +553,25 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
/* 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 = tableSpace;
+
/* 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);
+ OIDNewHeap = make_new_heap(tableOid, tableSpace, ToastTableSpace);
/* Copy the heap data into the new table in the desired order */
copy_heap_data(OIDNewHeap, tableOid, indexOid,
@@ -581,7 +597,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
* data, then call finish_heap_swap to complete the operation.
*/
Oid
-make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
+make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewToastTableSpace)
{
TupleDesc OldHeapDesc;
char NewHeapName[NAMEDATALEN];
@@ -679,7 +695,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
if (isNull)
reloptions = (Datum) 0;
- AlterTableCreateToastTable(OIDNewHeap, reloptions);
+ AlterTableCreateToastTable(OIDNewHeap, reloptions, NewToastTableSpace);
ReleaseSysCache(tuple);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c4622c0..35f3b25 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -147,7 +147,8 @@ typedef struct AlteredTableInfo
List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */
bool rewrite; /* T if a rewrite is forced */
- Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newToastTableSpace; /* new TOAST tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
List *changedConstraintDefs; /* string definitions of same */
@@ -356,8 +357,11 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
- char *tablespacename, LOCKMODE lockmode);
-static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+ char *tablespacename, LOCKMODE lockmode, bool table_only);
+static void ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel,
+ char *tablespacename, LOCKMODE lockmode);
+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, bool isReset, LOCKMODE lockmode);
static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
char fires_when, bool skip_system, LOCKMODE lockmode);
@@ -2980,7 +2984,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
/* This command never recurses */
- ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, false);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetTableTableSpace: /* SET TABLE TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
+ /* This command never recurses */
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, true);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetToastTableSpace: /* SET TOAST TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
+ /* This command never recurses */
+ ATPrepSetToastTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
@@ -3099,7 +3115,7 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
if (tab->relkind == RELKIND_RELATION)
- AlterTableCreateToastTable(tab->relid, (Datum) 0);
+ AlterTableCreateToastTable(tab->relid, (Datum) 0, InvalidOid);
}
}
@@ -3227,6 +3243,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 (...) */
ATExecSetRelOptions(rel, (List *) cmd->def, false, lockmode);
break;
@@ -3362,6 +3390,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
Relation OldHeap;
Oid OIDNewHeap;
Oid NewTableSpace;
+ Oid NewToastTableSpace;
OldHeap = heap_open(tab->relid, NoLock);
@@ -3393,11 +3422,16 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
NewTableSpace = tab->newTableSpace;
else
NewTableSpace = OldHeap->rd_rel->reltablespace;
+
+ if (tab->newToastTableSpace)
+ NewToastTableSpace = tab->newToastTableSpace;
+ else
+ NewToastTableSpace = InvalidOid;
heap_close(OldHeap, NoLock);
/* Create transient table that will receive the modified data */
- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace);
+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewToastTableSpace);
/*
* Copy the heap data into the new table with the desired
@@ -3431,7 +3465,9 @@ ATRewriteTables(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);
}
}
@@ -8036,30 +8072,66 @@ 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)
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid)
{
- Oid tablespaceId;
AclResult aclresult;
-
- /* Check that the tablespace exists */
- tablespaceId = get_tablespace_oid(tablespacename, false);
-
/* Check its permissions */
- 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")));
+}
+
+/*
+ * ALTER TABLE SET [TABLE] TABLESPACE
+ */
+static void
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode, bool table_only)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace);
+
+ /* Save tablespace Oid */
tab->newTableSpace = tablespaceId;
+
+ /* The case when we want to move only table location not its TOAST table */
+ if (table_only)
+ tab->newToastTableSpace = 0;
+ else
+ tab->newToastTableSpace = tablespaceId;
+
+}
+
+/*
+ * ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newToastTableSpace);
+
+ /* Save TOAST tablespace Oid */
+ tab->newToastTableSpace = tablespaceId;
}
+
/*
* ALTER TABLE/INDEX SET (...) or RESET (...)
*/
@@ -8187,12 +8259,42 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
heap_close(pgclass, RowExclusiveLock);
}
+
+extern void
+RelationIsMoveableToNewTablespace(Relation rel, Oid newTableSpace)
+{
+ /*
+ * 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")));
+}
+
/*
- * 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;
@@ -8221,31 +8323,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
relation_close(rel, NoLock);
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;
reltoastidxid = rel->rd_rel->reltoastidxid;
@@ -8325,12 +8404,69 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
CommandCounterIncrement();
/* Move associated toast relation and/or index, too */
- if (OidIsValid(reltoastrelid))
- ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
+ if (newToastTableSpace != 0)
+ {
+ if (OidIsValid(reltoastrelid))
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
+ if (OidIsValid(reltoastidxid))
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+ }
+}
+
+
+/*
+ * Execute ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATExecSetToastTableSpace(Oid tableOid, Oid newToastTableSpace, LOCKMODE lockmode)
+{
+ Relation rel;
+ Oid oldToastTableSpace;
+ Oid reltoastrelid;
+ Oid reltoastidxid;
+ 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
+ */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ relToast = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+
+ oldToastTableSpace = relToast->rd_rel->reltablespace;
+ if (newToastTableSpace == oldToastTableSpace ||
+ (newToastTableSpace == MyDatabaseTableSpace && oldToastTableSpace == 0))
+ {
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+ return;
+ }
+ }
+ else
+ {
+ relation_close(rel, NoLock);
+ return;
+ }
+
+ reltoastrelid = rel->rd_rel->reltoastrelid;
+ reltoastidxid = rel->rd_rel->reltoastidxid;
+
+ RelationIsMoveableToNewTablespace(rel, newToastTableSpace);
+
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
if (OidIsValid(reltoastidxid))
- ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+
}
+
/*
* Copy data, block by block
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index fd7a9ed..a5e78bd 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2509,7 +2509,7 @@ OpenIntoRel(QueryDesc *queryDesc)
(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
- AlterTableCreateToastTable(intoRelationId, reloptions);
+ AlterTableCreateToastTable(intoRelationId, reloptions, InvalidOid);
/*
* And open the constructed table for writing.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c135465..46e4458 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -558,7 +558,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
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
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
@@ -1994,6 +1994,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
{
@@ -12073,6 +12089,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 5b06333..0a09a23 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -553,7 +553,7 @@ standard_ProcessUtility(Node *parsetree,
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
true);
- AlterTableCreateToastTable(relOid, toast_options);
+ AlterTableCreateToastTable(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 973f0b3..9a2a740 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3875,6 +3875,7 @@ getTables(int *numTables)
int i_owning_tab;
int i_owning_col;
int i_reltablespace;
+ int i_reltoasttablespace;
int i_reloptions;
int i_toastreloptions;
int i_reloftype;
@@ -3902,7 +3903,7 @@ getTables(int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (g_fout->remoteVersion >= 90100)
+ if (g_fout->remoteVersion >= 90200)
{
/*
* Left join to pick up dependency info linking sequences to their
@@ -3922,7 +3923,45 @@ getTables(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, "
+ "(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 "
+ "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.objsubid = 0 AND "
+ "d.refclassid = c.tableoid AND d.deptype = 'a') "
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
+ "ORDER BY c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_FOREIGN_TABLE);
+ }
+ else if (g_fout->remoteVersion >= 90100)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any (note this dependency is AUTO as of 8.2)
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.relname, "
+ "c.relacl, c.relkind, c.relnamespace, "
+ "(%s c.relowner) AS rolname, "
+ "c.relchecks, c.relhastriggers, "
+ "c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relfrozenxid, tc.oid AS toid, "
+ "tc.relfrozenxid AS tfrozenxid, "
+ "c.relpersistence, "
+ "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+ "d.refobjid AS owning_tab, "
+ "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, "
+ "NULL AS reltoasttablespace "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
@@ -3958,7 +3997,8 @@ getTables(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 "
@@ -3993,7 +4033,8 @@ getTables(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 "
@@ -4028,7 +4069,8 @@ getTables(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 "
@@ -4064,7 +4106,8 @@ getTables(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 "
@@ -4099,7 +4142,8 @@ getTables(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 "
@@ -4130,7 +4174,8 @@ getTables(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",
@@ -4156,7 +4201,8 @@ getTables(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",
@@ -4192,7 +4238,8 @@ getTables(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",
@@ -4241,6 +4288,7 @@ getTables(int *numTables)
i_reloptions = PQfnumber(res, "reloptions");
i_toastreloptions = PQfnumber(res, "toast_reloptions");
i_reloftype = PQfnumber(res, "reloftype");
+ i_reltoasttablespace = PQfnumber(res, "reltoasttablespace");
if (lockWaitTimeout && g_fout->remoteVersion >= 70300)
{
@@ -4295,6 +4343,7 @@ getTables(int *numTables)
tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions));
tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions));
+ tblinfo[i].reltoasttablespace = strdup(PQgetvalue(res, i, i_reltoasttablespace));
/* other fields were zeroed above */
@@ -12719,7 +12768,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
}
-
+ if (strlen(tbinfo->reltoasttablespace) > 0)
+ {
+ appendPQExpBuffer(q, "ALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n",
+ tbinfo->reltoasttablespace);
+ }
+
if (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 3d5d534..dd9cf4c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -241,6 +241,7 @@ typedef struct _tableInfo
char relkind;
char relpersistence; /* relation persistence */
char *reltablespace; /* relation tablespace */
+ char *reltoasttablespace; /* TOAST relation tablespace */
char *reloptions; /* options specified by WITH (...) */
char *toast_reloptions; /* ditto, for the TOAST table */
bool hasindex; /* does it have any indexes? */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 2ff6d7d..308f2f5 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -29,6 +29,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,
@@ -1107,6 +1108,7 @@ describeOneTableDetails(const char *schemaname,
bool hastriggers;
bool hasoids;
Oid tablespace;
+ Oid toasttablespace;
char *reloptions;
char *reloftype;
char relpersistence;
@@ -1129,7 +1131,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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 "
@@ -1146,7 +1148,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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"
@@ -1162,7 +1164,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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';",
@@ -1177,7 +1179,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "%s, reltablespace\n"
+ "%s, reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(reloptions, E', ')" : "''"),
@@ -1188,7 +1190,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', reltablespace\n"
+ "'', reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1197,7 +1199,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', ''\n"
+ "'', '', ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1225,10 +1227,12 @@ describeOneTableDetails(const char *schemaname,
strdup(PQgetvalue(res, 0, 6)) : 0;
tableinfo.tablespace = (pset.sversion >= 80000) ?
atooid(PQgetvalue(res, 0, 7)) : 0;
- tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
- strdup(PQgetvalue(res, 0, 8)) : 0;
- tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
- PQgetvalue(res, 0, 9)[0] : 0;
+ tableinfo.toasttablespace = (pset.sversion >= 80400) ?
+ atooid(PQgetvalue(res, 0, 8)) : 0;
+ tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+ strdup(PQgetvalue(res, 0, 9)) : 0;
+ tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
+ PQgetvalue(res, 0, 10)[0] : 0;
PQclear(res);
res = NULL;
@@ -1584,6 +1588,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);
@@ -2225,6 +2230,7 @@ describeOneTableDetails(const char *schemaname,
add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
true);
+ add_toasttablespace_footer(&cont, tableinfo.toasttablespace);
}
printTable(&cont, pset.queryFout, pset.logfile);
@@ -2324,6 +2330,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, false);
+ 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 bb0fa09..2161be1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -814,7 +814,7 @@ psql_completion(char *text, int start, int end)
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
"ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
- "USER", "USER MAPPING FOR", "VIEW", NULL};
+ "USER", "USER MAPPING FOR", "VIEW", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
@@ -1288,12 +1288,15 @@ psql_completion(char *text, int start, int end)
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
- /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
+ /*
+ * If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE, TOAST TABLESPACE,
+ * TABLE TABLESPACE and SCHEMA
+ */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
+ {"(", "WITHOUT", "TABLESPACE", "SCHEMA", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
@@ -1302,6 +1305,16 @@ psql_completion(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 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 de3623a..388ce5f 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -17,7 +17,7 @@
/*
* toasting.c prototypes
*/
-extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions);
+extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace);
extern void BootstrapToastTable(char *relName,
Oid toastOid, Oid toastIndexOid);
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 518e896..08b6181 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -25,7 +25,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck, LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid);
-extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace);
+extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, 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/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 333e303..d4aa99c 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,6 +24,10 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
extern void RemoveRelations(DropStmt *drop);
+extern void RelationIsMoveableToNewTablespace(Relation rel, Oid newTableSpace);
+
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid);
+
extern void AlterTable(AlterTableStmt *stmt);
extern LOCKMODE AlterTableGetLockLevel(List *cmds);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index af6565e..70e13d8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1224,7 +1224,9 @@ typedef enum AlterTableType
AT_DropInherit, /* NO INHERIT parent */
AT_AddOf, /* OF <type_name> */
AT_DropOf, /* NOT OF */
- AT_GenericOptions /* OPTIONS (...) */
+ AT_GenericOptions, /* OPTIONS (...) */
+ AT_SetToastTableSpace, /* SET TOAST TABLESPACE */
+ AT_SetTableTableSpace /* SET TABLE TABLESPACE */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 3d170bc..645d77d 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -364,6 +364,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 90aea6c..d8904db 100644
--- a/src/test/regress/GNUmakefile
+++ b/src/test/regress/GNUmakefile
@@ -125,7 +125,9 @@ $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX): $(top_builddir
.PHONY: tablespace-setup
tablespace-setup:
rm -rf ./testtablespace
+ rm -rf ./testtablespace2
mkdir ./testtablespace
+ mkdir ./testtablespace2
##
@@ -170,4 +172,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 dba96f4..230acaa 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -11,7 +11,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';
@@ -54,7 +54,21 @@ 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 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';
+
DROP SCHEMA testschema CASCADE;
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 1260c96..324cb07 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -10,7 +10,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
@@ -72,6 +72,36 @@ 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 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)
+
DROP SCHEMA testschema CASCADE;
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table testschema.foo
@@ -80,3 +110,4 @@ drop cascades to table testschema.asexecute
drop cascades to table testschema.atable
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index d9cd053..5ea6099 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -421,6 +421,7 @@ static void
convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
{
char testtablespace[MAXPGPATH];
+ char testtablespace2[MAXPGPATH];
char indir[MAXPGPATH];
struct stat st;
int ret;
@@ -447,6 +448,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
exit_nicely(2);
snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
+ snprintf(testtablespace2, MAXPGPATH, "%s/testtablespace2", outputdir);
#ifdef WIN32
@@ -463,6 +465,9 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
if (directory_exists(testtablespace))
rmtree(testtablespace, true);
make_directory(testtablespace);
+ if (directory_exists(testtablespace2))
+ rmtree(testtablespace2, true);
+ make_directory(testtablespace2);
#endif
/* finally loop on each file and do the replacement */
@@ -507,6 +512,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
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);
On Tue, Nov 15, 2011 at 11:08 AM, Julien Tachoires <julmon@gmail.com> wrote:
Maybe I'd missed something, but the normal case is :
ALTER TABLE ... SET TABLESPACE => moves Table + moves associated TOAST Table
ALTER TABLE ... SET TABLE TABLESPACE => moves Table & keeps associated
TOAST Table at its place
ALTER TABLE ... SET TOAST TABLESPACE => keeps Table at its place &
moves associated TOAST Table
oh! i didn't test the patch just read it... and maybe i misunderstood,
will see it again.
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
On Tue, Nov 15, 2011 at 11:08 AM, Julien Tachoires <julmon@gmail.com> wrote:
Maybe I'd missed something, but the normal case is :
ALTER TABLE ... SET TABLESPACE => moves Table + moves associated TOAST Table
ALTER TABLE ... SET TABLE TABLESPACE => moves Table & keeps associated
TOAST Table at its place
ALTER TABLE ... SET TOAST TABLESPACE => keeps Table at its place &
moves associated TOAST Table
it has docs, and pg_dump support which is good.
but i found a few problems with the behaviour:
1) it accepts the sintax ALTER INDEX ... SET TOAST TABLESPACE ...;
which does nothing
2) after CLUSTER the index of the toast table gets moved to the same
tablespace as the main table
3) after ALTER TABLE ... ALTER ... TYPE ...; the toast table gets
moved to the same tablespace as the main table
now, if we are now supporting this variants
ALTER TABLE SET TABLE TABLESPACE
ALTER TABLE SET TOAST TABLESPACE
why not also support ALTER TABLE SET INDEX TABLESPACE which should
have the same behaviour as ALTER INDEX SET TABLESPACE... just an idea,
and of course not necessary for this patch
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
Hi Jaime,
Please find a new version.
2011/11/16 Jaime Casanova <jaime@2ndquadrant.com>:
On Tue, Nov 15, 2011 at 11:08 AM, Julien Tachoires <julmon@gmail.com> wrote:
Maybe I'd missed something, but the normal case is :
ALTER TABLE ... SET TABLESPACE => moves Table + moves associated TOAST Table
ALTER TABLE ... SET TABLE TABLESPACE => moves Table & keeps associated
TOAST Table at its place
ALTER TABLE ... SET TOAST TABLESPACE => keeps Table at its place &
moves associated TOAST Tableit has docs, and pg_dump support which is good.
but i found a few problems with the behaviour:
1) it accepts the sintax ALTER INDEX ... SET TOAST TABLESPACE ...;
which does nothing
Fixed.
2) after CLUSTER the index of the toast table gets moved to the same
tablespace as the main table
3) after ALTER TABLE ... ALTER ... TYPE ...; the toast table gets
moved to the same tablespace as the main table
Fixed.
Show quoted text
now, if we are now supporting this variants
ALTER TABLE SET TABLE TABLESPACE
ALTER TABLE SET TOAST TABLESPACEwhy not also support ALTER TABLE SET INDEX TABLESPACE which should
have the same behaviour as ALTER INDEX SET TABLESPACE... just an idea,
and of course not necessary for this patch--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
Attachments:
set_toast_tablespace_v0.9.patchtext/x-patch; charset=US-ASCII; name=set_toast_tablespace_v0.9.patchDownload
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 00a477e..4290dae 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -66,6 +66,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
NOT OF
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>
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
@@ -551,6 +553,30 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</varlistentry>
<varlistentry>
+ <term><literal>SET TABLE TABLESPACE</literal></term>
+ <listitem>
+ <para>
+ This form changes only table's tablespace (not associated TOAST table's tablespace)
+ 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
+ <xref linkend="SQL-CREATETABLESPACE">
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>RENAME</literal></term>
<listitem>
<para>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index cb2f60e..e307422 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -427,6 +427,11 @@ pages). There was no run time difference compared to an un-<acronym>TOAST</>ed
comparison table, in which all the HTML pages were cut down to 7 kB to fit.
</para>
+<para>
+TOAST table can be moved to a different tablespace with
+<command>ALTER TABLE SET TOAST TABLESPACE</>
+</para>
+
</sect1>
<sect1 id="storage-fsm">
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 3a40e8b..18191cf 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@ extern Oid binary_upgrade_next_toast_pg_class_oid;
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
- Datum reloptions);
+ Datum reloptions, Oid toastTableSpace);
static bool needs_toast_table(Relation rel);
@@ -53,10 +53,10 @@ static bool needs_toast_table(Relation rel);
* to end with CommandCounterIncrement if it makes any changes.
*/
void
-AlterTableCreateToastTable(Oid relOid, Datum reloptions)
+AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace)
{
Relation rel;
-
+ Relation toast_rel;
/*
* Grab an exclusive lock on the target table, since we'll update its
* pg_class tuple. This is redundant for all present uses, since caller
@@ -65,9 +65,20 @@ AlterTableCreateToastTable(Oid relOid, Datum reloptions)
* so let's be safe.
*/
rel = heap_open(relOid, AccessExclusiveLock);
+
+ /*
+ * if NewToastTableSpace is null then try to find old TOAST table's tablespace
+ */
+ if (!OidIsValid(toastTableSpace) && OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ toast_rel = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+ if (OidIsValid(toast_rel->rd_rel->reltablespace))
+ toastTableSpace = toast_rel->rd_rel->reltablespace;
+ relation_close(toast_rel, NoLock);
+ }
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
+ (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, toastTableSpace);
heap_close(rel, NoLock);
}
@@ -93,7 +104,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
relName)));
/* create_toast_table does all the work */
- if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
+ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0,InvalidOid))
elog(ERROR, "\"%s\" does not require a toast table",
relName);
@@ -109,7 +120,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
* bootstrap they can be nonzero to specify hand-assigned OIDs
*/
static bool
-create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
+create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, Oid toastTableSpace)
{
Oid relOid = RelationGetRelid(rel);
HeapTuple reltup;
@@ -209,10 +220,15 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
toast_typid = binary_upgrade_next_toast_pg_type_oid;
binary_upgrade_next_toast_pg_type_oid = InvalidOid;
}
+
+ /* Use table's tablespace if toastTableSpace is null */
+ if (!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,
@@ -278,7 +294,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
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);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index edec44d..d108cb0 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -543,6 +543,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
bool is_system_catalog;
bool swap_toast_by_content;
TransactionId frozenXid;
+ Oid ToastTableSpace;
+ Relation ToastRel;
+
/* Mark the correct index as clustered */
if (OidIsValid(indexOid))
@@ -551,11 +554,25 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
/* 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 = tableSpace;
+
/* 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);
+ OIDNewHeap = make_new_heap(tableOid, tableSpace, ToastTableSpace);
/* Copy the heap data into the new table in the desired order */
copy_heap_data(OIDNewHeap, tableOid, indexOid,
@@ -581,7 +598,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
* data, then call finish_heap_swap to complete the operation.
*/
Oid
-make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
+make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewToastTableSpace)
{
TupleDesc OldHeapDesc;
char NewHeapName[NAMEDATALEN];
@@ -679,7 +696,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
if (isNull)
reloptions = (Datum) 0;
- AlterTableCreateToastTable(OIDNewHeap, reloptions);
+ AlterTableCreateToastTable(OIDNewHeap, reloptions, NewToastTableSpace);
ReleaseSysCache(tuple);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index c4622c0..52b4f04 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -147,7 +147,8 @@ typedef struct AlteredTableInfo
List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */
bool rewrite; /* T if a rewrite is forced */
- Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newToastTableSpace; /* new TOAST tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
List *changedConstraintDefs; /* string definitions of same */
@@ -356,8 +357,11 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
- char *tablespacename, LOCKMODE lockmode);
-static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+ char *tablespacename, LOCKMODE lockmode, bool table_only);
+static void ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel,
+ char *tablespacename, LOCKMODE lockmode);
+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, bool isReset, LOCKMODE lockmode);
static void ATExecEnableDisableTrigger(Relation rel, char *trigname,
char fires_when, bool skip_system, LOCKMODE lockmode);
@@ -2980,7 +2984,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
/* This command never recurses */
- ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, false);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetTableTableSpace: /* SET TABLE TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
+ /* This command never recurses */
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, true);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetToastTableSpace: /* SET TOAST TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* This command never recurses */
+ ATPrepSetToastTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
@@ -3097,9 +3113,28 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
foreach(ltab, *wqueue)
{
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
+
+ Relation toast_rel;
+ Oid toast_tablespace_oid;
+ Relation rel;
if (tab->relkind == RELKIND_RELATION)
- AlterTableCreateToastTable(tab->relid, (Datum) 0);
+ {
+ 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);
+ }
+ else
+ toast_tablespace_oid = InvalidOid;
+
+ relation_close(rel, NoLock);
+ AlterTableCreateToastTable(tab->relid, (Datum) 0, toast_tablespace_oid);
+
+ }
}
}
@@ -3227,6 +3262,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 (...) */
ATExecSetRelOptions(rel, (List *) cmd->def, false, lockmode);
break;
@@ -3360,8 +3407,10 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
{
/* Build a temporary relation and copy data */
Relation OldHeap;
+ Relation OldToastRel;
Oid OIDNewHeap;
Oid NewTableSpace;
+ Oid NewToastTableSpace;
OldHeap = heap_open(tab->relid, NoLock);
@@ -3393,11 +3442,24 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
NewTableSpace = tab->newTableSpace;
else
NewTableSpace = OldHeap->rd_rel->reltablespace;
-
+
+ if (tab->newToastTableSpace)
+ NewToastTableSpace = tab->newToastTableSpace;
+ else
+ {
+ if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
+ {
+ OldToastRel = relation_open(OldHeap->rd_rel->reltoastrelid, NoLock);
+ NewToastTableSpace = OldToastRel->rd_rel->reltablespace;
+ relation_close(OldToastRel, NoLock);
+ }
+ else
+ NewToastTableSpace = InvalidOid;
+ }
heap_close(OldHeap, NoLock);
/* Create transient table that will receive the modified data */
- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace);
+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewToastTableSpace);
/*
* Copy the heap data into the new table with the desired
@@ -3431,7 +3493,9 @@ ATRewriteTables(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);
}
}
@@ -8036,31 +8100,67 @@ 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)
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid)
{
- Oid tablespaceId;
AclResult aclresult;
-
- /* Check that the tablespace exists */
- tablespaceId = get_tablespace_oid(tablespacename, false);
-
/* Check its permissions */
- 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")));
+}
+
+/*
+ * ALTER TABLE SET [TABLE] TABLESPACE
+ */
+static void
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode, bool table_only)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace);
+
+ /* Save tablespace Oid */
tab->newTableSpace = tablespaceId;
+
+ /* The case when we want to move only table location not its TOAST table */
+ if (table_only)
+ tab->newToastTableSpace = 0;
+ else
+ tab->newToastTableSpace = tablespaceId;
+
}
/*
+ * ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newToastTableSpace);
+
+ /* Save TOAST tablespace Oid */
+ tab->newToastTableSpace = tablespaceId;
+}
+
+
+/*
* ALTER TABLE/INDEX SET (...) or RESET (...)
*/
static void
@@ -8187,12 +8287,42 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset, LOCKMODE lockmode
heap_close(pgclass, RowExclusiveLock);
}
+
+extern void
+RelationIsMoveableToNewTablespace(Relation rel, Oid newTableSpace)
+{
+ /*
+ * 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")));
+}
+
/*
- * 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;
@@ -8221,31 +8351,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
relation_close(rel, NoLock);
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;
reltoastidxid = rel->rd_rel->reltoastidxid;
@@ -8325,12 +8432,69 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
CommandCounterIncrement();
/* Move associated toast relation and/or index, too */
- if (OidIsValid(reltoastrelid))
- ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
+ if (newToastTableSpace != 0)
+ {
+ if (OidIsValid(reltoastrelid))
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
+ if (OidIsValid(reltoastidxid))
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+ }
+}
+
+
+/*
+ * Execute ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATExecSetToastTableSpace(Oid tableOid, Oid newToastTableSpace, LOCKMODE lockmode)
+{
+ Relation rel;
+ Oid oldToastTableSpace;
+ Oid reltoastrelid;
+ Oid reltoastidxid;
+ 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
+ */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ relToast = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+
+ oldToastTableSpace = relToast->rd_rel->reltablespace;
+ if (newToastTableSpace == oldToastTableSpace ||
+ (newToastTableSpace == MyDatabaseTableSpace && oldToastTableSpace == 0))
+ {
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+ return;
+ }
+ }
+ else
+ {
+ relation_close(rel, NoLock);
+ return;
+ }
+
+ reltoastrelid = rel->rd_rel->reltoastrelid;
+ reltoastidxid = rel->rd_rel->reltoastidxid;
+
+ RelationIsMoveableToNewTablespace(rel, newToastTableSpace);
+
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
if (OidIsValid(reltoastidxid))
- ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+
}
+
/*
* Copy data, block by block
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d19e097..4529cca 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2545,7 +2545,7 @@ OpenIntoRel(QueryDesc *queryDesc)
(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
- AlterTableCreateToastTable(intoRelationId, reloptions);
+ AlterTableCreateToastTable(intoRelationId, reloptions, InvalidOid);
/*
* And open the constructed table for writing.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 2a497d1..66798cd 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -557,7 +557,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
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
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
@@ -1993,6 +1993,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
{
@@ -12057,6 +12073,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 6f88c47..3b9239d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -545,7 +545,7 @@ standard_ProcessUtility(Node *parsetree,
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
true);
- AlterTableCreateToastTable(relOid, toast_options);
+ AlterTableCreateToastTable(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 ec932e4..f970ba3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3863,6 +3863,7 @@ getTables(int *numTables)
int i_owning_tab;
int i_owning_col;
int i_reltablespace;
+ int i_reltoasttablespace;
int i_reloptions;
int i_toastreloptions;
int i_reloftype;
@@ -3890,7 +3891,44 @@ getTables(int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (g_fout->remoteVersion >= 90100)
+ if (g_fout->remoteVersion >= 90200)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any (note this dependency is AUTO as of 8.2)
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.relname, "
+ "c.relacl, c.relkind, c.relnamespace, "
+ "(%s c.relowner) AS rolname, "
+ "c.relchecks, c.relhastriggers, "
+ "c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relfrozenxid, tc.oid AS toid, "
+ "tc.relfrozenxid AS tfrozenxid, "
+ "c.relpersistence, "
+ "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+ "d.refobjid AS owning_tab, "
+ "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, "
+ "(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 "
+ "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.objsubid = 0 AND "
+ "d.refclassid = c.tableoid AND d.deptype = 'a') "
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
+ "ORDER BY c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_FOREIGN_TABLE);
+ }
+ else if (g_fout->remoteVersion >= 90100)
{
/*
* Left join to pick up dependency info linking sequences to their
@@ -3910,7 +3948,8 @@ getTables(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 "
@@ -3946,7 +3985,8 @@ getTables(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 "
@@ -3981,7 +4021,8 @@ getTables(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 "
@@ -4016,7 +4057,8 @@ getTables(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 "
@@ -4052,7 +4094,8 @@ getTables(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 "
@@ -4087,7 +4130,8 @@ getTables(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 "
@@ -4118,7 +4162,8 @@ getTables(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",
@@ -4144,7 +4189,8 @@ getTables(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",
@@ -4180,7 +4226,8 @@ getTables(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",
@@ -4229,6 +4276,7 @@ getTables(int *numTables)
i_reloptions = PQfnumber(res, "reloptions");
i_toastreloptions = PQfnumber(res, "toast_reloptions");
i_reloftype = PQfnumber(res, "reloftype");
+ i_reltoasttablespace = PQfnumber(res, "reltoasttablespace");
if (lockWaitTimeout && g_fout->remoteVersion >= 70300)
{
@@ -4283,6 +4331,7 @@ getTables(int *numTables)
tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
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 */
@@ -12730,7 +12779,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
}
-
+ if (strlen(tbinfo->reltoasttablespace) > 0)
+ {
+ appendPQExpBuffer(q, "ALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n",
+ tbinfo->reltoasttablespace);
+ }
+
if (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 c248e75..3c0b0ca 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -241,6 +241,7 @@ typedef struct _tableInfo
char relkind;
char relpersistence; /* relation persistence */
char *reltablespace; /* relation tablespace */
+ char *reltoasttablespace; /* TOAST relation tablespace */
char *reloptions; /* options specified by WITH (...) */
char *toast_reloptions; /* ditto, for the TOAST table */
bool hasindex; /* does it have any indexes? */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 2ff6d7d..308f2f5 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -29,6 +29,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,
@@ -1107,6 +1108,7 @@ describeOneTableDetails(const char *schemaname,
bool hastriggers;
bool hasoids;
Oid tablespace;
+ Oid toasttablespace;
char *reloptions;
char *reloftype;
char relpersistence;
@@ -1129,7 +1131,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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 "
@@ -1146,7 +1148,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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"
@@ -1162,7 +1164,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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';",
@@ -1177,7 +1179,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "%s, reltablespace\n"
+ "%s, reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(reloptions, E', ')" : "''"),
@@ -1188,7 +1190,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', reltablespace\n"
+ "'', reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1197,7 +1199,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', ''\n"
+ "'', '', ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1225,10 +1227,12 @@ describeOneTableDetails(const char *schemaname,
strdup(PQgetvalue(res, 0, 6)) : 0;
tableinfo.tablespace = (pset.sversion >= 80000) ?
atooid(PQgetvalue(res, 0, 7)) : 0;
- tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
- strdup(PQgetvalue(res, 0, 8)) : 0;
- tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
- PQgetvalue(res, 0, 9)[0] : 0;
+ tableinfo.toasttablespace = (pset.sversion >= 80400) ?
+ atooid(PQgetvalue(res, 0, 8)) : 0;
+ tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+ strdup(PQgetvalue(res, 0, 9)) : 0;
+ tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
+ PQgetvalue(res, 0, 10)[0] : 0;
PQclear(res);
res = NULL;
@@ -1584,6 +1588,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);
@@ -2225,6 +2230,7 @@ describeOneTableDetails(const char *schemaname,
add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
true);
+ add_toasttablespace_footer(&cont, tableinfo.toasttablespace);
}
printTable(&cont, pset.queryFout, pset.logfile);
@@ -2324,6 +2330,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, false);
+ 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 bb0fa09..2161be1 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -814,7 +814,7 @@ psql_completion(char *text, int start, int end)
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
"ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
- "USER", "USER MAPPING FOR", "VIEW", NULL};
+ "USER", "USER MAPPING FOR", "VIEW", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
@@ -1288,12 +1288,15 @@ psql_completion(char *text, int start, int end)
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
- /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
+ /*
+ * If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE, TOAST TABLESPACE,
+ * TABLE TABLESPACE and SCHEMA
+ */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
+ {"(", "WITHOUT", "TABLESPACE", "SCHEMA", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
@@ -1302,6 +1305,16 @@ psql_completion(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 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 de3623a..388ce5f 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -17,7 +17,7 @@
/*
* toasting.c prototypes
*/
-extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions);
+extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace);
extern void BootstrapToastTable(char *relName,
Oid toastOid, Oid toastIndexOid);
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index 518e896..08b6181 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -25,7 +25,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck, LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid);
-extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace);
+extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, 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/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 333e303..d4aa99c 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,6 +24,10 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
extern void RemoveRelations(DropStmt *drop);
+extern void RelationIsMoveableToNewTablespace(Relation rel, Oid newTableSpace);
+
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid);
+
extern void AlterTable(AlterTableStmt *stmt);
extern LOCKMODE AlterTableGetLockLevel(List *cmds);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9e277c5..44b8969 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1224,7 +1224,9 @@ typedef enum AlterTableType
AT_DropInherit, /* NO INHERIT parent */
AT_AddOf, /* OF <type_name> */
AT_DropOf, /* NOT OF */
- AT_GenericOptions /* OPTIONS (...) */
+ AT_GenericOptions, /* OPTIONS (...) */
+ AT_SetToastTableSpace, /* SET TOAST TABLESPACE */
+ AT_SetTableTableSpace /* SET TABLE TABLESPACE */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 3d170bc..645d77d 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -364,6 +364,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 ec29391..a7a8175 100644
--- a/src/test/regress/GNUmakefile
+++ b/src/test/regress/GNUmakefile
@@ -125,7 +125,9 @@ $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX): $(top_builddir
.PHONY: tablespace-setup
tablespace-setup:
rm -rf ./testtablespace
+ rm -rf ./testtablespace2
mkdir ./testtablespace
+ mkdir ./testtablespace2
##
@@ -170,4 +172,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 dba96f4..230acaa 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -11,7 +11,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';
@@ -54,7 +54,21 @@ 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 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';
+
DROP SCHEMA testschema CASCADE;
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 1260c96..324cb07 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -10,7 +10,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
@@ -72,6 +72,36 @@ 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 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)
+
DROP SCHEMA testschema CASCADE;
NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table testschema.foo
@@ -80,3 +110,4 @@ drop cascades to table testschema.asexecute
drop cascades to table testschema.atable
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index d9cd053..5ea6099 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -421,6 +421,7 @@ static void
convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
{
char testtablespace[MAXPGPATH];
+ char testtablespace2[MAXPGPATH];
char indir[MAXPGPATH];
struct stat st;
int ret;
@@ -447,6 +448,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
exit_nicely(2);
snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
+ snprintf(testtablespace2, MAXPGPATH, "%s/testtablespace2", outputdir);
#ifdef WIN32
@@ -463,6 +465,9 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
if (directory_exists(testtablespace))
rmtree(testtablespace, true);
make_directory(testtablespace);
+ if (directory_exists(testtablespace2))
+ rmtree(testtablespace2, true);
+ make_directory(testtablespace2);
#endif
/* finally loop on each file and do the replacement */
@@ -507,6 +512,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
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);
On Mon, Nov 28, 2011 at 1:32 PM, Julien Tachoires <julmon@gmail.com> wrote:
Hi Jaime,
Please find a new version.
cool
2) after CLUSTER the index of the toast table gets moved to the same
tablespace as the main table
there is still a variant of this one, i created 3 tablespaces (datos_tblspc):
"""
create table t1 (
i serial primary key,
t text
) tablespace datos_tblspc;
ALTER TABLE t1 SET TOAST TABLESPACE pg_default;
CLUSTER t1 USING t1_pkey;
"""
now, if we are now supporting this variants
ALTER TABLE SET TABLE TABLESPACE
ALTER TABLE SET TOAST TABLESPACEwhy not also support ALTER TABLE SET INDEX TABLESPACE which should
have the same behaviour as ALTER INDEX SET TABLESPACE... just an idea,
and of course not necessary for this patch
any opinion about this? maybe i can make a patch for that if there is
consensus that it could be good for symettry
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
Hi,
2011/12/10 Jaime Casanova <jaime@2ndquadrant.com>:
On Mon, Nov 28, 2011 at 1:32 PM, Julien Tachoires <julmon@gmail.com> wrote:
2) after CLUSTER the index of the toast table gets moved to the same
tablespace as the main tablethere is still a variant of this one, i created 3 tablespaces (datos_tblspc):
"""
create table t1 (
i serial primary key,
t text
) tablespace datos_tblspc;ALTER TABLE t1 SET TOAST TABLESPACE pg_default;
CLUSTER t1 USING t1_pkey;
"""
I am not able to reproduce this case, could you show me exactly how to
reproduce it ?
now, if we are now supporting this variants
ALTER TABLE SET TABLE TABLESPACE
ALTER TABLE SET TOAST TABLESPACEwhy not also support ALTER TABLE SET INDEX TABLESPACE which should
have the same behaviour as ALTER INDEX SET TABLESPACE... just an idea,
and of course not necessary for this patchany opinion about this? maybe i can make a patch for that if there is
consensus that it could be good for symettry
Thanks,
On Sat, Dec 10, 2011 at 4:16 PM, Jaime Casanova <jaime@2ndquadrant.com> wrote:
now, if we are now supporting this variants
ALTER TABLE SET TABLE TABLESPACE
ALTER TABLE SET TOAST TABLESPACEwhy not also support ALTER TABLE SET INDEX TABLESPACE which should
have the same behaviour as ALTER INDEX SET TABLESPACE... just an idea,
and of course not necessary for this patchany opinion about this? maybe i can make a patch for that if there is
consensus that it could be good for symettry
I'm not really convinced we need it. I think it would end up just
being a shorthand for ALTER INDEX .. SET TABLESPACE for each index.
Most tables don't have more than a handful of indexes, so it doesn't
seem like we'd be gaining much (compare GRANT ... ON ALL TABLES IN
SCHEMA, which could easily be a shorthand for hundreds or perhaps even
thousands of individual GRANT statements).
Also, it seems that we haven't really discussed much why moving the
TOAST table to a different tablespace from the main table might be
useful. I'm not saying we shouldn't have it if it's good for
something, but what's the reason for wanting it?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mon, Dec 12, 2011 at 10:54 AM, Julien Tachoires <julmon@gmail.com> wrote:
Hi,
2011/12/10 Jaime Casanova <jaime@2ndquadrant.com>:
On Mon, Nov 28, 2011 at 1:32 PM, Julien Tachoires <julmon@gmail.com> wrote:
2) after CLUSTER the index of the toast table gets moved to the same
tablespace as the main tablethere is still a variant of this one, i created 3 tablespaces (datos_tblspc):
"""
create table t1 (
i serial primary key,
t text
) tablespace datos_tblspc;ALTER TABLE t1 SET TOAST TABLESPACE pg_default;
CLUSTER t1 USING t1_pkey;
"""I am not able to reproduce this case, could you show me exactly how to
reproduce it ?
just as that...
- create a table in a certain tablespace (diferent from pg_default),
the toast table will be in the same tablespace,
- then change the tablespace to pg_default and
- then cluster the table...
the toast table will be again in the same tablespace as the main table
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
2011/12/13 Jaime Casanova <jaime@2ndquadrant.com>:
On Mon, Dec 12, 2011 at 10:54 AM, Julien Tachoires <julmon@gmail.com> wrote:
2011/12/10 Jaime Casanova <jaime@2ndquadrant.com>:
On Mon, Nov 28, 2011 at 1:32 PM, Julien Tachoires <julmon@gmail.com> wrote:
2) after CLUSTER the index of the toast table gets moved to the same
tablespace as the main tablethere is still a variant of this one, i created 3 tablespaces (datos_tblspc):
"""
create table t1 (
i serial primary key,
t text
) tablespace datos_tblspc;ALTER TABLE t1 SET TOAST TABLESPACE pg_default;
CLUSTER t1 USING t1_pkey;
"""I am not able to reproduce this case, could you show me exactly how to
reproduce it ?just as that...
- create a table in a certain tablespace (diferent from pg_default),
the toast table will be in the same tablespace,
- then change the tablespace to pg_default and
- then cluster the table...
the toast table will be again in the same tablespace as the main table
Right, it seems to happen when the destination tablespace is the same
as the database's tbs, because, in this case, relation's tbs is set to
InvalidOid :
src/backend/commands/tablecmds.c line 8342
+ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ?
InvalidOid : newTableSpace;
Why don't just asign newTableSpace value here ?
Thanks,
On Tue, Dec 13, 2011 at 12:02 PM, Julien Tachoires <julmon@gmail.com> wrote:
Right, it seems to happen when the destination tablespace is the same
as the database's tbs, because, in this case, relation's tbs is set to
InvalidOid :
src/backend/commands/tablecmds.c line 8342+ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ?
InvalidOid : newTableSpace;Why don't just asign newTableSpace value here ?
When a relation is stored in the default tablespace, we always record
that in the system catalogs as InvalidOid. Otherwise, if the
database's default tablespace were changed, things would break.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2011/12/13 Robert Haas <robertmhaas@gmail.com>:
On Tue, Dec 13, 2011 at 12:02 PM, Julien Tachoires <julmon@gmail.com> wrote:
Right, it seems to happen when the destination tablespace is the same
as the database's tbs, because, in this case, relation's tbs is set to
InvalidOid :
src/backend/commands/tablecmds.c line 8342+ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ?
InvalidOid : newTableSpace;Why don't just asign newTableSpace value here ?
When a relation is stored in the default tablespace, we always record
that in the system catalogs as InvalidOid. Otherwise, if the
database's default tablespace were changed, things would break.
OK, considering that, I don't see any way to handle the case raised by Jaime :(
On 12/13/2011 12:29 PM, Julien Tachoires wrote:
2011/12/13 Robert Haas<robertmhaas@gmail.com>:
On Tue, Dec 13, 2011 at 12:02 PM, Julien Tachoires<julmon@gmail.com> wrote:
Right, it seems to happen when the destination tablespace is the same
as the database's tbs, because, in this case, relation's tbs is set to
InvalidOid :
src/backend/commands/tablecmds.c line 8342+ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ?
InvalidOid : newTableSpace;Why don't just asign newTableSpace value here ?
When a relation is stored in the default tablespace, we always record
that in the system catalogs as InvalidOid. Otherwise, if the
database's default tablespace were changed, things would break.OK, considering that, I don't see any way to handle the case raised by Jaime :(
So we have a problem here: there's a case that's messy to handle. And
there's really a large issue hanging over this whole patch, which is
that it needs a better explanation of what exactly it's going to get
used for. Especially if the implementation gets more complicated, we'd
want to see a clear reason to use this feature. And that's not really
clear.
If you can return with an update that perhaps finds a way to work around
this OID issue, please re-submit that. And if you can explain some more
about where you think this feature is useful, more information on that
would be helpful. Since this isn't going to get committed soon, I'm
going to mark it returned with feedback for now.
--
Greg Smith 2ndQuadrant US greg@2ndQuadrant.com Baltimore, MD
PostgreSQL Training, Services, and 24x7 Support www.2ndQuadrant.us
Excerpts from Julien Tachoires's message of mar dic 13 14:29:56 -0300 2011:
2011/12/13 Robert Haas <robertmhaas@gmail.com>:
On Tue, Dec 13, 2011 at 12:02 PM, Julien Tachoires <julmon@gmail.com> wrote:
Right, it seems to happen when the destination tablespace is the same
as the database's tbs, because, in this case, relation's tbs is set to
InvalidOid :
src/backend/commands/tablecmds.c line 8342+ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ?
InvalidOid : newTableSpace;Why don't just asign newTableSpace value here ?
When a relation is stored in the default tablespace, we always record
that in the system catalogs as InvalidOid. Otherwise, if the
database's default tablespace were changed, things would break.OK, considering that, I don't see any way to handle the case raised by Jaime :(
Uhm, surely you could compare the original toast tablespace to the heap
tablespace, and if they differ, handle appropriately when creating the
new toast table? Just pass down the toast tablespace into
AlterTableCreateToastTable, instead of having it assume that
rel->rd_rel->relnamespace is sufficient. This should be done in all
cases where a toast tablespace is created, which shouldn't be more than
a handful of them.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Tue, Dec 13, 2011 at 12:29 PM, Julien Tachoires <julmon@gmail.com> wrote:
OK, considering that, I don't see any way to handle the case raised by Jaime :(
Did you consider what Álvaro suggested? anyway, seems is too late for
this commitfest.
are you intending to resume work on this for the next cycle?
do we consider this as a useful thing?
--
Jaime Casanova www.2ndQuadrant.com
Professional PostgreSQL: Soporte 24x7 y capacitación
Excerpts from Jaime Casanova's message of lun ene 16 03:23:30 -0300 2012:
On Tue, Dec 13, 2011 at 12:29 PM, Julien Tachoires <julmon@gmail.com> wrote:
OK, considering that, I don't see any way to handle the case raised by Jaime :(
Did you consider what Álvaro suggested? anyway, seems is too late for
this commitfest.
are you intending to resume work on this for the next cycle?
do we consider this as a useful thing?
The remaining bits shouldn't be too hard. In case Julien is not
interested in the task, I have added a link to this discussion in the
TODO item.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Hi,
2012/1/16 Alvaro Herrera <alvherre@commandprompt.com>:
Excerpts from Jaime Casanova's message of lun ene 16 03:23:30 -0300 2012:
On Tue, Dec 13, 2011 at 12:29 PM, Julien Tachoires <julmon@gmail.com> wrote:
OK, considering that, I don't see any way to handle the case raised by Jaime :(
Did you consider what Álvaro suggested? anyway, seems is too late for
this commitfest.
Not yet.
are you intending to resume work on this for the next cycle?
do we consider this as a useful thing?
That's a good question.
If the answer is "yes", I'll continue on this work.
Show quoted text
The remaining bits shouldn't be too hard. In case Julien is not
interested in the task, I have added a link to this discussion in the
TODO item.
2011/12/15 Alvaro Herrera <alvherre@commandprompt.com>:
Uhm, surely you could compare the original toast tablespace to the heap
tablespace, and if they differ, handle appropriately when creating the
new toast table? Just pass down the toast tablespace into
AlterTableCreateToastTable, instead of having it assume that
rel->rd_rel->relnamespace is sufficient. This should be done in all
cases where a toast tablespace is created, which shouldn't be more than
a handful of them.
Thank you, that way seems right.
Now, I distinguish before each creation of a TOAST table with
AlterTableCreateToastTable() : if it will create a new one or recreate
an existing one.
Thus, in create_toast_table() when toastTableSpace is equal to
InvalidOid, we are able :
- to fallback to the main table tablespace in case of new TOAST table creation
- to keep it previous tablespace in case of recreation.
Here's a new version rebased against HEAD.
Regards,
--
JT
Attachments:
set_toast_tablespace_v0.10.patchtext/x-patch; charset=US-ASCII; name=set_toast_tablespace_v0.10.patchDownload
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 6f1917f..aca3749 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -66,6 +66,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
NOT OF
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>
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
@@ -555,6 +557,30 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
</varlistentry>
<varlistentry>
+ <term><literal>SET TABLE TABLESPACE</literal></term>
+ <listitem>
+ <para>
+ This form changes only table's tablespace (not associated TOAST table's tablespace)
+ 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
+ <xref linkend="SQL-CREATETABLESPACE">
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>RENAME</literal></term>
<listitem>
<para>
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index cb2f60e..e307422 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -427,6 +427,11 @@ pages). There was no run time difference compared to an un-<acronym>TOAST</>ed
comparison table, in which all the HTML pages were cut down to 7 kB to fit.
</para>
+<para>
+TOAST table can be moved to a different tablespace with
+<command>ALTER TABLE SET TOAST TABLESPACE</>
+</para>
+
</sect1>
<sect1 id="storage-fsm">
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 707ba7e..f01d689 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@ extern Oid binary_upgrade_next_toast_pg_class_oid;
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
- Datum reloptions);
+ Datum reloptions, Oid toastTableSpace, bool new_toast);
static bool needs_toast_table(Relation rel);
@@ -53,10 +53,10 @@ static bool needs_toast_table(Relation rel);
* to end with CommandCounterIncrement if it makes any changes.
*/
void
-AlterTableCreateToastTable(Oid relOid, Datum reloptions)
+AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace, bool new_toast)
{
Relation rel;
-
+
/*
* Grab an exclusive lock on the target table, since we'll update its
* pg_class tuple. This is redundant for all present uses, since caller
@@ -65,9 +65,9 @@ AlterTableCreateToastTable(Oid relOid, Datum reloptions)
* so let's be safe.
*/
rel = heap_open(relOid, AccessExclusiveLock);
-
+
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
+ (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, toastTableSpace, new_toast);
heap_close(rel, NoLock);
}
@@ -93,7 +93,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
relName)));
/* create_toast_table does all the work */
- if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
+ if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0, InvalidOid, true))
elog(ERROR, "\"%s\" does not require a toast table",
relName);
@@ -109,7 +109,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
* bootstrap they can be nonzero to specify hand-assigned OIDs
*/
static bool
-create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
+create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, Oid toastTableSpace, bool new_toast)
{
Oid relOid = RelationGetRelid(rel);
HeapTuple reltup;
@@ -209,10 +209,17 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
toast_typid = binary_upgrade_next_toast_pg_type_oid;
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 (through CLUSTER)
+ */
+ 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,
@@ -278,7 +285,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
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);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 9408f25..5553b8c 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -541,6 +541,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
bool is_system_catalog;
bool swap_toast_by_content;
TransactionId frozenXid;
+ Oid ToastTableSpace;
+ Relation ToastRel;
+
/* Mark the correct index as clustered */
if (OidIsValid(indexOid))
@@ -549,11 +552,25 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
/* 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);
+ OIDNewHeap = make_new_heap(tableOid, tableSpace, ToastTableSpace);
/* Copy the heap data into the new table in the desired order */
copy_heap_data(OIDNewHeap, tableOid, indexOid,
@@ -579,7 +596,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid,
* data, then call finish_heap_swap to complete the operation.
*/
Oid
-make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
+make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewToastTableSpace)
{
TupleDesc OldHeapDesc;
char NewHeapName[NAMEDATALEN];
@@ -676,8 +693,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
&isNull);
if (isNull)
reloptions = (Datum) 0;
-
- AlterTableCreateToastTable(OIDNewHeap, reloptions);
+
+ AlterTableCreateToastTable(OIDNewHeap, reloptions, NewToastTableSpace, false);
ReleaseSysCache(tuple);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cc210f0..1c9e525 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -147,7 +147,8 @@ typedef struct AlteredTableInfo
List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */
bool rewrite; /* T if a rewrite is forced */
- Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newTableSpace; /* new tablespace; 0 means no change */
+ Oid newToastTableSpace; /* new TOAST tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
List *changedConstraintDefs; /* string definitions of same */
@@ -364,8 +365,11 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
+ char *tablespacename, LOCKMODE lockmode, bool table_only);
+static void ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel,
char *tablespacename, LOCKMODE lockmode);
-static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
+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);
@@ -2971,7 +2975,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
/* This command never recurses */
- ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, false);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetTableTableSpace: /* SET TABLE TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX);
+ /* This command never recurses */
+ ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, true);
+ pass = AT_PASS_MISC; /* doesn't actually matter */
+ break;
+ case AT_SetToastTableSpace: /* SET TOAST TABLESPACE */
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* This command never recurses */
+ ATPrepSetToastTableSpace(tab, rel, cmd->name, lockmode);
pass = AT_PASS_MISC; /* doesn't actually matter */
break;
case AT_SetRelOptions: /* SET (...) */
@@ -3089,9 +3105,32 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
foreach(ltab, *wqueue)
{
AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
+
+ Relation toast_rel;
+ Oid toast_tablespace_oid;
+ Relation rel;
+ bool new_toast;
if (tab->relkind == RELKIND_RELATION)
- AlterTableCreateToastTable(tab->relid, (Datum) 0);
+ {
+ 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, toast_tablespace_oid, new_toast);
+
+ }
}
}
@@ -3219,6 +3258,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 */
@@ -3350,8 +3401,10 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
{
/* Build a temporary relation and copy data */
Relation OldHeap;
+ Relation OldToastRel;
Oid OIDNewHeap;
Oid NewTableSpace;
+ Oid NewToastTableSpace;
OldHeap = heap_open(tab->relid, NoLock);
@@ -3383,11 +3436,24 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
NewTableSpace = tab->newTableSpace;
else
NewTableSpace = OldHeap->rd_rel->reltablespace;
-
+
+ if (tab->newToastTableSpace)
+ NewToastTableSpace = tab->newToastTableSpace;
+ else
+ {
+ if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
+ {
+ OldToastRel = relation_open(OldHeap->rd_rel->reltoastrelid, NoLock);
+ NewToastTableSpace = OldToastRel->rd_rel->reltablespace;
+ relation_close(OldToastRel, NoLock);
+ }
+ else
+ NewToastTableSpace = InvalidOid;
+ }
heap_close(OldHeap, NoLock);
/* Create transient table that will receive the modified data */
- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace);
+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewToastTableSpace);
/*
* Copy the heap data into the new table with the desired
@@ -3421,7 +3487,9 @@ ATRewriteTables(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);
}
}
@@ -8126,30 +8194,65 @@ 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)
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid)
{
- Oid tablespaceId;
AclResult aclresult;
-
- /* Check that the tablespace exists */
- tablespaceId = get_tablespace_oid(tablespacename, false);
-
/* Check its permissions */
- 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")));
+}
+
+/*
+ * ALTER TABLE SET [TABLE] TABLESPACE
+ */
+static void
+ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode, bool table_only)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace);
+
+ /* Save tablespace Oid */
tab->newTableSpace = tablespaceId;
+
+ /* The case when we want to move only table location not its TOAST table */
+ if (table_only)
+ tab->newToastTableSpace = InvalidOid;
+ else
+ tab->newToastTableSpace = tablespaceId;
+
+}
+
+/*
+ * ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode)
+{
+ Oid tablespaceId;
+
+ /* Check that the tablespace exists */
+ tablespaceId = get_tablespace_oid(tablespacename, false);
+ /* Check it */
+ CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newToastTableSpace);
+
+ /* Save TOAST tablespace Oid */
+ tab->newToastTableSpace = tablespaceId;
}
+
/*
* Set, reset, or replace reloptions.
*/
@@ -8309,12 +8412,42 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
heap_close(pgclass, RowExclusiveLock);
}
+
+extern 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;
@@ -8343,31 +8476,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
relation_close(rel, NoLock);
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;
reltoastidxid = rel->rd_rel->reltoastidxid;
@@ -8433,6 +8543,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
/* update the pg_class row */
rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
+
rd_rel->relfilenode = newrelfilenode;
simple_heap_update(pg_class, &tuple->t_self, tuple);
CatalogUpdateIndexes(pg_class, tuple);
@@ -8447,12 +8558,69 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
CommandCounterIncrement();
/* Move associated toast relation and/or index, too */
- if (OidIsValid(reltoastrelid))
- ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
+ if (OidIsValid(newToastTableSpace))
+ {
+ if (OidIsValid(reltoastrelid))
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
+ if (OidIsValid(reltoastidxid))
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+ }
+}
+
+
+/*
+ * Execute ALTER TABLE SET TOAST TABLESPACE
+ */
+static void
+ATExecSetToastTableSpace(Oid tableOid, Oid newToastTableSpace, LOCKMODE lockmode)
+{
+ Relation rel;
+ Oid oldToastTableSpace;
+ Oid reltoastrelid;
+ Oid reltoastidxid;
+ 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
+ */
+ if (OidIsValid(rel->rd_rel->reltoastrelid))
+ {
+ relToast = relation_open(rel->rd_rel->reltoastrelid, NoLock);
+
+ oldToastTableSpace = relToast->rd_rel->reltablespace;
+ if (newToastTableSpace == oldToastTableSpace ||
+ (newToastTableSpace == MyDatabaseTableSpace && oldToastTableSpace == 0))
+ {
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+ return;
+ }
+ }
+ else
+ {
+ relation_close(rel, NoLock);
+ return;
+ }
+
+ reltoastrelid = rel->rd_rel->reltoastrelid;
+ reltoastidxid = rel->rd_rel->reltoastidxid;
+
+ RelationIsMoveableToNewTablespace(rel, newToastTableSpace);
+
+ relation_close(rel, NoLock);
+ relation_close(relToast, NoLock);
+
+ ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace);
if (OidIsValid(reltoastidxid))
- ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode);
+ ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace);
+
}
+
/*
* Copy data, block by block
*/
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 422f737..59c89cb 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2629,7 +2629,7 @@ OpenIntoRel(QueryDesc *queryDesc)
(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
- AlterTableCreateToastTable(intoRelationId, reloptions);
+ AlterTableCreateToastTable(intoRelationId, reloptions, InvalidOid, true);
/*
* And open the constructed table for writing.
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0ec039b..d8649bb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -557,7 +557,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
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
@@ -1993,6 +1993,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
{
@@ -12104,6 +12120,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 de16a61..793c36b 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -545,7 +545,7 @@ standard_ProcessUtility(Node *parsetree,
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
true);
- AlterTableCreateToastTable(relOid, toast_options);
+ AlterTableCreateToastTable(relOid, toast_options, InvalidOid, true);
}
else if (IsA(stmt, CreateForeignTableStmt))
{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 13fc667..6e9c096 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3926,6 +3926,7 @@ getTables(int *numTables)
int i_owning_tab;
int i_owning_col;
int i_reltablespace;
+ int i_reltoasttablespace;
int i_reloptions;
int i_toastreloptions;
int i_reloftype;
@@ -3953,7 +3954,44 @@ getTables(int *numTables)
* we cannot correctly identify inherited columns, owned sequences, etc.
*/
- if (g_fout->remoteVersion >= 90100)
+ if (g_fout->remoteVersion >= 90200)
+ {
+ /*
+ * Left join to pick up dependency info linking sequences to their
+ * owning column, if any (note this dependency is AUTO as of 8.2)
+ */
+ appendPQExpBuffer(query,
+ "SELECT c.tableoid, c.oid, c.relname, "
+ "c.relacl, c.relkind, c.relnamespace, "
+ "(%s c.relowner) AS rolname, "
+ "c.relchecks, c.relhastriggers, "
+ "c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relfrozenxid, tc.oid AS toid, "
+ "tc.relfrozenxid AS tfrozenxid, "
+ "c.relpersistence, "
+ "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
+ "d.refobjid AS owning_tab, "
+ "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, "
+ "(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 "
+ "d.classid = c.tableoid AND d.objid = c.oid AND "
+ "d.objsubid = 0 AND "
+ "d.refclassid = c.tableoid AND d.deptype = 'a') "
+ "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) "
+ "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') "
+ "ORDER BY c.oid",
+ username_subquery,
+ RELKIND_SEQUENCE,
+ RELKIND_RELATION, RELKIND_SEQUENCE,
+ RELKIND_VIEW, RELKIND_COMPOSITE_TYPE,
+ RELKIND_FOREIGN_TABLE);
+ }
+ else if (g_fout->remoteVersion >= 90100)
{
/*
* Left join to pick up dependency info linking sequences to their
@@ -3973,7 +4011,8 @@ getTables(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 "
@@ -4009,7 +4048,8 @@ getTables(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 "
@@ -4044,7 +4084,8 @@ getTables(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 "
@@ -4079,7 +4120,8 @@ getTables(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 "
@@ -4115,7 +4157,8 @@ getTables(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 "
@@ -4150,7 +4193,8 @@ getTables(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 "
@@ -4181,7 +4225,8 @@ getTables(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",
@@ -4207,7 +4252,8 @@ getTables(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",
@@ -4243,7 +4289,8 @@ getTables(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",
@@ -4292,6 +4339,7 @@ getTables(int *numTables)
i_reloptions = PQfnumber(res, "reloptions");
i_toastreloptions = PQfnumber(res, "toast_reloptions");
i_reloftype = PQfnumber(res, "reloftype");
+ i_reltoasttablespace = PQfnumber(res, "reltoasttablespace");
if (lockWaitTimeout && g_fout->remoteVersion >= 70300)
{
@@ -4346,6 +4394,7 @@ getTables(int *numTables)
tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
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 */
@@ -12879,7 +12928,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
}
-
+ if (strlen(tbinfo->reltoasttablespace) > 0)
+ {
+ appendPQExpBuffer(q, "ALTER TABLE %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n",
+ tbinfo->reltoasttablespace);
+ }
+
if (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 11c4d37..7e08343 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -243,6 +243,7 @@ typedef struct _tableInfo
char relkind;
char relpersistence; /* relation persistence */
char *reltablespace; /* relation tablespace */
+ char *reltoasttablespace; /* TOAST relation tablespace */
char *reloptions; /* options specified by WITH (...) */
char *toast_reloptions; /* ditto, for the TOAST table */
bool hasindex; /* does it have any indexes? */
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 4eee4be..73fb09a 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -29,6 +29,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,
@@ -1121,6 +1122,7 @@ describeOneTableDetails(const char *schemaname,
bool hastriggers;
bool hasoids;
Oid tablespace;
+ Oid toasttablespace;
char *reloptions;
char *reloftype;
char relpersistence;
@@ -1143,7 +1145,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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 "
@@ -1160,7 +1162,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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"
@@ -1176,7 +1178,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, "
"c.relhastriggers, 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';",
@@ -1191,7 +1193,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "%s, reltablespace\n"
+ "%s, reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
(verbose ?
"pg_catalog.array_to_string(reloptions, E', ')" : "''"),
@@ -1202,7 +1204,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', reltablespace\n"
+ "'', reltablespace, ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1211,7 +1213,7 @@ describeOneTableDetails(const char *schemaname,
printfPQExpBuffer(&buf,
"SELECT relchecks, relkind, relhasindex, relhasrules, "
"reltriggers <> 0, relhasoids, "
- "'', ''\n"
+ "'', '', ''\n"
"FROM pg_catalog.pg_class WHERE oid = '%s';",
oid);
}
@@ -1239,10 +1241,12 @@ describeOneTableDetails(const char *schemaname,
strdup(PQgetvalue(res, 0, 6)) : 0;
tableinfo.tablespace = (pset.sversion >= 80000) ?
atooid(PQgetvalue(res, 0, 7)) : 0;
- tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ?
- strdup(PQgetvalue(res, 0, 8)) : 0;
- tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
- PQgetvalue(res, 0, 9)[0] : 0;
+ tableinfo.toasttablespace = (pset.sversion >= 80400) ?
+ atooid(PQgetvalue(res, 0, 8)) : 0;
+ tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ?
+ strdup(PQgetvalue(res, 0, 9)) : 0;
+ tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 10), "") != 0) ?
+ PQgetvalue(res, 0, 10)[0] : 0;
PQclear(res);
res = NULL;
@@ -1598,6 +1602,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);
@@ -2248,6 +2253,7 @@ describeOneTableDetails(const char *schemaname,
add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace,
true);
+ add_toasttablespace_footer(&cont, tableinfo.toasttablespace);
}
printTable(&cont, pset.queryFout, pset.logfile);
@@ -2347,6 +2353,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, false);
+ 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 6efc0ce..8240f69 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -814,7 +814,7 @@ psql_completion(char *text, int start, int end)
"GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR",
"ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE",
"TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE",
- "USER", "USER MAPPING FOR", "VIEW", NULL};
+ "USER", "USER MAPPING FOR", "VIEW", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_ALTER);
}
@@ -1288,12 +1288,15 @@ psql_completion(char *text, int start, int end)
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
- /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
+ /*
+ * If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE, TOAST TABLESPACE,
+ * TABLE TABLESPACE and SCHEMA
+ */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
+ {"(", "WITHOUT", "TABLESPACE", "SCHEMA", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
@@ -1302,6 +1305,16 @@ psql_completion(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 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 a4f1718..d253f01 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -17,7 +17,7 @@
/*
* toasting.c prototypes
*/
-extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions);
+extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, 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 21b2a58..1c8e863 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -25,7 +25,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck, LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid);
-extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace);
+extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, 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/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 03f397d..e5cdb93 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -24,6 +24,10 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId);
extern void RemoveRelations(DropStmt *drop);
+extern void RelationIsMoveableToNewTablespace(Relation rel, Oid NewTableSpaceOid);
+
+extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid);
+
extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index dce0e72..7701e4f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1226,7 +1226,9 @@ typedef enum AlterTableType
AT_DropInherit, /* NO INHERIT parent */
AT_AddOf, /* OF <type_name> */
AT_DropOf, /* NOT OF */
- AT_GenericOptions /* OPTIONS (...) */
+ AT_GenericOptions, /* OPTIONS (...) */
+ AT_SetToastTableSpace, /* SET TOAST TABLESPACE */
+ AT_SetTableTableSpace /* SET TABLE TABLESPACE */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 3d7de06..64f89b8 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -364,6 +364,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 27b86da..15c29f9 100644
--- a/src/test/regress/GNUmakefile
+++ b/src/test/regress/GNUmakefile
@@ -125,7 +125,9 @@ $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX): $(top_builddir
.PHONY: tablespace-setup
tablespace-setup:
rm -rf ./testtablespace
+ rm -rf ./testtablespace2
mkdir ./testtablespace
+ mkdir ./testtablespace2
##
@@ -170,4 +172,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 dba96f4..4cac8fb 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -11,7 +11,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';
@@ -54,7 +54,30 @@ 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 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;
+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';
+
DROP SCHEMA testschema CASCADE;
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 1260c96..dea569b 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -10,7 +10,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
@@ -72,11 +72,61 @@ 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 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;
+NOTICE: CREATE TABLE will create implicit sequence "foo2_id_seq" for serial column "foo2.id"
+NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo2_pkey" for table "foo2"
+ALTER TABLE testschema.foo2 SET TOAST 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)
+
DROP SCHEMA testschema CASCADE;
-NOTICE: drop cascades to 4 other objects
+NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table testschema.foo
drop cascades to table testschema.asselect
drop cascades to table testschema.asexecute
drop cascades to table testschema.atable
+drop cascades to table testschema.foo2
-- Should succeed
DROP TABLESPACE testspace;
+DROP TABLESPACE testspace2;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 2f6b37b..a0860ca 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -410,6 +410,7 @@ static void
convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
{
char testtablespace[MAXPGPATH];
+ char testtablespace2[MAXPGPATH];
char indir[MAXPGPATH];
struct stat st;
int ret;
@@ -436,6 +437,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
exit(2);
snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
+ snprintf(testtablespace2, MAXPGPATH, "%s/testtablespace2", outputdir);
#ifdef WIN32
@@ -452,6 +454,9 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
if (directory_exists(testtablespace))
rmtree(testtablespace, true);
make_directory(testtablespace);
+ if (directory_exists(testtablespace2))
+ rmtree(testtablespace2, true);
+ make_directory(testtablespace2);
#endif
/* finally loop on each file and do the replacement */
@@ -496,6 +501,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
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);
On Sun, Jan 22, 2012 at 11:04 AM, Julien Tachoires <julmon@gmail.com> wrote:
2011/12/15 Alvaro Herrera <alvherre@commandprompt.com>:
Uhm, surely you could compare the original toast tablespace to the heap
tablespace, and if they differ, handle appropriately when creating the
new toast table? Just pass down the toast tablespace into
AlterTableCreateToastTable, instead of having it assume that
rel->rd_rel->relnamespace is sufficient. This should be done in all
cases where a toast tablespace is created, which shouldn't be more than
a handful of them.Thank you, that way seems right.
Now, I distinguish before each creation of a TOAST table with
AlterTableCreateToastTable() : if it will create a new one or recreate
an existing one.
Thus, in create_toast_table() when toastTableSpace is equal to
InvalidOid, we are able :
- to fallback to the main table tablespace in case of new TOAST table creation
- to keep it previous tablespace in case of recreation.Here's a new version rebased against HEAD.
To ask more directly the question that's come up a few times upthread,
why do *you* think this is useful? What motivated you to want this
behavior, and/or how do you think it could benefit other PostgreSQL
users?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2012/1/24 Robert Haas <robertmhaas@gmail.com>:
On Sun, Jan 22, 2012 at 11:04 AM, Julien Tachoires <julmon@gmail.com> wrote:
2011/12/15 Alvaro Herrera <alvherre@commandprompt.com>:
Uhm, surely you could compare the original toast tablespace to the heap
tablespace, and if they differ, handle appropriately when creating the
new toast table? Just pass down the toast tablespace into
AlterTableCreateToastTable, instead of having it assume that
rel->rd_rel->relnamespace is sufficient. This should be done in all
cases where a toast tablespace is created, which shouldn't be more than
a handful of them.Thank you, that way seems right.
Now, I distinguish before each creation of a TOAST table with
AlterTableCreateToastTable() : if it will create a new one or recreate
an existing one.
Thus, in create_toast_table() when toastTableSpace is equal to
InvalidOid, we are able :
- to fallback to the main table tablespace in case of new TOAST table creation
- to keep it previous tablespace in case of recreation.Here's a new version rebased against HEAD.
To ask more directly the question that's come up a few times upthread,
why do *you* think this is useful? What motivated you to want this
behavior, and/or how do you think it could benefit other PostgreSQL
users?
Sorry, I didn't get this question to me. I've just picked up this item
from the TODO list and then I was thinking that it could be useful. My
motivation was to learn more about PostgreSQL dev. and to work on a
concrete case. Now, I'm not sure anymore this is useful.
--
JT
On Wed, Jan 25, 2012 at 7:13 AM, Julien Tachoires <julmon@gmail.com> wrote:
2012/1/24 Robert Haas <robertmhaas@gmail.com>:
On Sun, Jan 22, 2012 at 11:04 AM, Julien Tachoires <julmon@gmail.com> wrote:
2011/12/15 Alvaro Herrera <alvherre@commandprompt.com>:
Uhm, surely you could compare the original toast tablespace to the heap
tablespace, and if they differ, handle appropriately when creating the
new toast table? Just pass down the toast tablespace into
AlterTableCreateToastTable, instead of having it assume that
rel->rd_rel->relnamespace is sufficient. This should be done in all
cases where a toast tablespace is created, which shouldn't be more than
a handful of them.Thank you, that way seems right.
Now, I distinguish before each creation of a TOAST table with
AlterTableCreateToastTable() : if it will create a new one or recreate
an existing one.
Thus, in create_toast_table() when toastTableSpace is equal to
InvalidOid, we are able :
- to fallback to the main table tablespace in case of new TOAST table creation
- to keep it previous tablespace in case of recreation.Here's a new version rebased against HEAD.
To ask more directly the question that's come up a few times upthread,
why do *you* think this is useful? What motivated you to want this
behavior, and/or how do you think it could benefit other PostgreSQL
users?Sorry, I didn't get this question to me. I've just picked up this item
from the TODO list and then I was thinking that it could be useful. My
motivation was to learn more about PostgreSQL dev. and to work on a
concrete case. Now, I'm not sure anymore this is useful.
OK. In that case, I don't think it makes sense to add this right now.
I share the feeling that it could possibly be useful under some set
of circumstances, but it doesn't seem prudent to add features because
there might hypothetically be a use case. I suggest that we mark this
patch Returned with Feedback and add links to your latest version of
this patch and one of the emails expressing concerns about the utility
of this patch to the Todo list. If we later have a clearer idea in
mind why this might be useful, we can add it then - and document the
use case.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi
We have some tables with documents and their metadata (filename,
filetype, etc).
Most of the time we just want to get the metadata (filename, filetype,
etc) and not the document itself.
In this case it would be nice to have the metadata (which wouldn't end
up on the TOAST) on a fast array (SSD-based),
and put the filedata on a slow array (HDD-based). It's probably possible
to have two tables one for metadata and one
for the extra file, but this is a workaround.
--
Mvh
Christian Nicolaisen
Deltasoft AS
Tlf +47 938 38 596
On 2/8/12 6:17 AM, Christian Nicolaisen wrote:
Hi
We have some tables with documents and their metadata (filename, filetype, etc).
Most of the time we just want to get the metadata (filename, filetype, etc) and not the document itself.
In this case it would be nice to have the metadata (which wouldn't end up on the TOAST) on a fast array (SSD-based),
and put the filedata on a slow array (HDD-based). It's probably possible to have two tables one for metadata and one
for the extra file, but this is a workaround.
Did you intend to post a patch? Because nothing was attached. Also, if you're submitting a patch you should add it to the next commitfest.
On Fri, Feb 10, 2012 at 2:23 PM, Jim Nasby <jim@nasby.net> wrote:
On 2/8/12 6:17 AM, Christian Nicolaisen wrote:
We have some tables with documents and their metadata (filename, filetype,
etc).
Most of the time we just want to get the metadata (filename, filetype,
etc) and not the document itself.
In this case it would be nice to have the metadata (which wouldn't end up
on the TOAST) on a fast array (SSD-based),
and put the filedata on a slow array (HDD-based). It's probably possible
to have two tables one for metadata and one
for the extra file, but this is a workaround.Did you intend to post a patch? Because nothing was attached. Also, if
you're submitting a patch you should add it to the next commitfest.
He was commenting on the possible utility of a previously-submitted
patch, which got pushed off because we weren't sure it was good for
anything.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Julien Tachoires <julmon@gmail.com> writes:
2011/12/15 Alvaro Herrera <alvherre@commandprompt.com>:
Uhm, surely you could compare the original toast tablespace to the heap
tablespace, and if they differ, handle appropriately when creating the
new toast table? =A0Just pass down the toast tablespace into
AlterTableCreateToastTable, instead of having it assume that
rel->rd_rel->relnamespace is sufficient. =A0This should be done in all
cases where a toast tablespace is created, which shouldn't be more than
a handful of them.Thank you, that way seems right.
Now, I distinguish before each creation of a TOAST table with
AlterTableCreateToastTable() : if it will create a new one or recreate
an existing one.
Thus, in create_toast_table() when toastTableSpace is equal to
InvalidOid, we are able :
- to fallback to the main table tablespace in case of new TOAST table creat=
ion
- to keep it previous tablespace in case of recreation.Here's a new version rebased against HEAD.
Almost 3 years later, here's a version rebased against current HEAD. :-)
It compiles and even passes the included regression test.
There were doubts originally if this is actually a useful feature, but
there is at least one plausible use case (main table on SSD, TOAST on
HDD):
/messages/by-id/4F3267EE.80405@deltasoft.no
I didn't add anything on top of the latest version, but I notice we've
added mat. views since then, so it looks like we now also need this:
ALTER MATERIALIZED VIEW SET [VIEW | TOAST] TABLESPACE
Should I add this patch to the next CommitFest?
--
Alex
Attachments:
On Fri, Nov 28, 2014 at 11:38 AM, Alex Shulgin <ash@commandprompt.com> wrote:
Should I add this patch to the next CommitFest?
If you don't want it to get forgotten about, yes.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
Here is my review of the feature.
- I have not worked enough with tablespaces to see if this patch would
be useful. There was some uncertainty about this upstream.
- Would it not be enough to simply allow running ALTER TABLE SET
TABLESPACE on the TOAST tables? Or is manually modifying those too ugly?
- I like that it adds tab completion for the new commands.
- The feature seems to work as described, other than the ALTER INDEX
issue noted below and that it broke pg_dump. The broken pg_dump means I
have not tested how this changes database dumps.
- 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)
- 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...
- "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.
- You should add a link to
http://www.postgresql.org/docs/current/interactive/storage-toast.html to
the manual page of ALTER TABLE.
- 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.
- Would it be interesting to add syntax for moving the toast index to a
separate tablespace?
- There is no warning if you set the toast table space of a table
lacking toast. I think there should be one.
- No support for materialized views as pointed out by Alex.
- 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.
- I think a couple of tests for the error cases would be nice.
= Minor style issue
- 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.
- Double spaces in src/backend/catalog/toasting.c after "if (new_toast".
- 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.
- The local variables ToastTableSpace and ToastRel do not follow the
naming conventions.
- Remove the parentheses around "(is_system_catalog)".
- 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"?
- Incorrect indentation for new code added last in ATExecSetTableSpace.
- The patch adds commented out code in src/include/commands/tablecmds.h.
--
Andreas Karlsson
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Tue, Dec 30, 2014 at 11:48 AM, Andreas Karlsson <andreas@proxel.se> wrote:
Here is my review of the feature.
Marked as returned with feedback.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
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
Attachments:
set_toast_tablespace_v0.12.patchtext/x-patch; name=set_toast_tablespace_v0.12.patchDownload
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);
On 03/03/2015 04:14 PM, Julien Tachoires wrote:
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.[...]
I can't reproduce this issue.
Neither can I anymore.
- 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.
I still get a failing pg_dump test though when running make check-world.
And it does not work when run manually either.
+ pg_dumpall -f
/home/andreas/dev/postgresql/contrib/pg_upgrade/tmp_check/dump1.sql
pg_dump: column number -1 is out of range 0..28
cannot duplicate null pointer (internal error)
pg_dumpall: pg_dump failed on database "postgres", exiting
+ pg_dumpall1_status=1
+ [ /home/andreas/dev/postgresql != /home/andreas/dev/postgresql ]
+
/home/andreas/dev/postgresql/contrib/pg_upgrade/tmp_check/install//home/andreas/dev/postgresql-inst/bin/pg_ctl
-m fast stop
waiting for server to shut down.... done
server stopped
+ [ -n ]
+ [ -n ]
+ [ -n 1 ]
+ echo pg_dumpall of pre-upgrade database cluster failed
pg_dumpall of pre-upgrade database cluster failed
+ exit 1
+ rm -rf /tmp/pg_upgrade_check-5A3wsI
- 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.
I think we will have to agree to disagree here. I think it should be
obvious when the toast table is in the default tablespace for tables
outside it.
- 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.
I think we should allow moving the indexes for consistency. With this
patch we can move everything except for TOAST indexes.
- 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.
Excellent, also add a test case for this.
- 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 ?
I mean that "TOAST table can be moved to a different tablespace with
<command>ALTER TABLE SET TOAST TABLESPACE</>" should be changed to
"TOAST table can be moved to a different tablespace with <command>ALTER
TABLE SET TOAST TABLESPACE</>." since a sentece should always end in ".".
= New comments
- The patch does not apply cleanly anymore, I had to solve to minor
conflicts.
- Rewrap the documentation for SET TABLESPACE.
- You have added a tab in alter_table.sgml.
- Merge "case AT_SetTableTableSpace:" and "case AT_SetToastTableSpace:"
where they both do the same thing, i.e. nothing.
- Should it not be foo_mv in the query from the test suite below?
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';
--
Andreas Karlsson
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 03/03/2015 04:14 PM, Julien Tachoires wrote:
Sorry for the delay, I missed this thread.
Here is a new version of this patch considering Andreas' comments.
Please also add it to the next open commitfest so we do not lose the patch.
--
Andreas Karlsson
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Mar 9, 2015 at 7:26 PM, Andreas Karlsson <andreas@proxel.se> wrote:
- 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.I think we will have to agree to disagree here. I think it should be obvious
when the toast table is in the default tablespace for tables outside it.
I'm not sure about the details here, but that seems like a pretty
sound principle.
- 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.I think we should allow moving the indexes for consistency. With this patch
we can move everything except for TOAST indexes.
It might make sense to always put the TOAST index with the TOAST
table, but it seems strange to put the TOAST index with the heap and
the TOAST table someplace else. Or at least, that's how it seems to
me.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas wrote:
On Mon, Mar 9, 2015 at 7:26 PM, Andreas Karlsson <andreas@proxel.se> wrote:
I think we should allow moving the indexes for consistency. With this patch
we can move everything except for TOAST indexes.It might make sense to always put the TOAST index with the TOAST
table, but it seems strange to put the TOAST index with the heap and
the TOAST table someplace else. Or at least, that's how it seems to
me.
Agreed. It doesn't seem necessary to allow moving the toast index to a
tablespace other than the one containing the toast table. In other
words, if you move the toast table, the index always follows it, and
there's no option to move it independently.
--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 03/10/2015 01:23 PM, Robert Haas wrote:
On Mon, Mar 9, 2015 at 7:26 PM, Andreas Karlsson <andreas@proxel.se> wrote:
- 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.I think we will have to agree to disagree here. I think it should be obvious
when the toast table is in the default tablespace for tables outside it.I'm not sure about the details here, but that seems like a pretty
sound principle.
What I am talking about are the 6 cases below of \d with the current
code. I think that case 4) and 5) look the same, which may confuse users
skimming the \d into thinking that we have no TOAST table in the case
where the TOAST table is in pg_default while the table tablespace is
somewhere else.
1. Table in pg_default, no TOAST
Column | Type | Modifiers
--------+---------+-----------
x | integer |
2. Table in pg_default, TOAST in pg_default
Column | Type | Modifiers
--------+------+-----------
x | text |
3. Table in pg_default, TOAST in ts1
Column | Type | Modifiers
--------+------+-----------
x | text |
TOAST Tablespace: "ts1"
4. Table in ts1, no TOAST
Column | Type | Modifiers
--------+---------+-----------
x | integer |
Tablespace: "ts1"
5. Table in ts1, TOAST in pg_default
Column | Type | Modifiers
--------+------+-----------
x | text |
Tablespace: "ts1"
6. Table in ts1, TOAST in ts1
Column | Type | Modifiers
--------+------+-----------
x | text |
Tablespace: "ts1"
TOAST Tablespace: "ts1"
I think we should allow moving the indexes for consistency. With this patch
we can move everything except for TOAST indexes.It might make sense to always put the TOAST index with the TOAST
table, but it seems strange to put the TOAST index with the heap and
the TOAST table someplace else. Or at least, that's how it seems to
me.
Ok, my idea was that one might want to put all indexes in a third table
space, including TOAST indexes. Not sure how realistic this use case is.
--
Andreas Karlsson
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 10/03/2015 13:27, Alvaro Herrera wrote:
Robert Haas wrote:
On Mon, Mar 9, 2015 at 7:26 PM, Andreas Karlsson <andreas@proxel.se> wrote:
I think we should allow moving the indexes for consistency. With this patch
we can move everything except for TOAST indexes.It might make sense to always put the TOAST index with the TOAST
table, but it seems strange to put the TOAST index with the heap and
the TOAST table someplace else. Or at least, that's how it seems to
me.Agreed. It doesn't seem necessary to allow moving the toast index to a
tablespace other than the one containing the toast table. In other
words, if you move the toast table, the index always follows it, and
there's no option to move it independently.
This behaviour is already implemented, the TOAST index always follows
the TOAST table. I'll add a couple of regression tests about it.
--
Julien
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi,
On 10/03/2015 00:31, Andreas Karlsson wrote:
On 03/03/2015 04:14 PM, Julien Tachoires wrote:
Sorry for the delay, I missed this thread.
Here is a new version of this patch considering Andreas' comments.Please also add it to the next open commitfest so we do not lose the patch.
Here is a new version rebased against head and considering Andreas' last
comments:
- New regression tests about the fact that a notice is raised when
ALTER TABLE SET TOAST TABLESPACE is done for a non-TOASTed table.
- New regression tests to check that a TOAST index follows the TOAST
table when it's moved.
- Some fixes in the documentation.
- psql's '\d' command shows now both table and TOAST table tablespaces
when they are different, even if one of them is pg_default.
- patch added to the next open commitfest:
https://commitfest.postgresql.org/5/188/
Regards,
--
Julien
Attachments:
set_toast_tablespace_v0.13.patch.gzapplication/gzip; name=set_toast_tablespace_v0.13.patch.gzDownload
���U set_toast_tablespace_v0.13.patch �\{s�����W���
��'��`&C�
Y ���wJ%��FH�$��&��{��n�%��!{�jSR������G�d;�)�VgN����N� ����I��'�������1]�_�6�X�l�_�#���Gvyyz�O����[W�u��������j������������X����be�{��g�v���6���o{���a�{����-������mw������_���������a��xa���P��=���$8b�o�u�����m{����x�����7b2@ ������j������o}��x�>m���G��V(~f�w�a�?6����g6��F�A_��"���oYxf���h�����wx����`se/O�'�_�N?����� �����o1���4�����6�j�I}zv~9��04*�Ak��j��_� �xvSz��B�S\T���7��
z7�����q���GUST?���������W��?^m#�`�K�t�e�����J������J���)�K����K��O�}�V��'���j'��N�
����5�>��_�{'dS?�3���f<d�=g$�B���E>� �r���{�g+zs�A����dS�����`��������'�(��`��"�C<���t�0g
C>U�@G?�A�k6YF���Y��&\\V�B�����$f`��9�g�l+C������0�W�u�t�#�f-��{-{b���:��I��[��7S��B<�}�f�����{���u�,\����x:1A?l�a�B�a<��� |��t0��g�r���(`�MC�HV�����SJk�"�I������%�K{�\%��=��J�^{���H��A�H��*�L�����lR�=�0��3���V�k����J���i�~k�����J�9Z7E��C2�������tZzJ�L�����������P��F���F���+/�����A_�'���j�by����4��Q���ZH ��x�"��f��)�M���c��I�B7�<�X#�I[c��Q&s���W�����v�]H�~�9�8�\���QY\�QD���P"B��z�wO��9���A�����P;I�f�i)��j��aMQ� L:@ Ef�������aN�gt�>5�+�M��{2�/����E�vqv�<�lmH�R�sR��}���:�|� ���+"\����������.@��d�P�'�h����h�b8(� ?�t�PE��X,@��R~]�R���!W9Ei�^�����u��7���@��P����N@���2G����qF����i!N,0#������+�Y���
�`�gV�i�`O������B�[�����+�_�8v�P�8�<��,0mnx�cdPGc13��7|��|��AJ�����F S�=���=�>�=�p��1v�L������
�1�� �V����n�_�7]r��R�5&����s�����p�(��H�!jH�/��j�M\.���8��PZ�q%�IK�QL���Z��WV�����Y�;>c��v�v�%�@��J���Pi^!(�G�� 4��'����KP����h�"2���A���\
���U�X/���e�\,�G,Q��q�>���55�>f"�(Xr����O��:���|A%����_��8 �D)^D����S�!���D`��+���5����Z�S����mY<�-��~�x6����Qi���_:�>�1m����� ���B9��|F!��/���������� �f'/r\�}`,@���~��"����#�5&qA�{�y$^��d5��m���������� 5B�W?mU.X�^?�����Ga�M������!��$��e=+�g,~��C�P�X�I�J�4�
��sm���D@��)@�bw8+������X,���1?�W�_�I��0���&���{?a71QY������N�'pA����
���:vQ�gt������6Z.�I��S����ze��Lc�L3
�e�����0w7�$�,�9����Ul��G��)���Wi�����Q@�)����e����Y���H����$��4^!������x���o�x�/@I�CfCfr���� �P�����3��~�,E��fe����w��zK��~����7!��8�Z�H�B����.`��t�Q<������������u\RM�]�}gz7����l
����b���?��`�T�~���k(-�St��6����v
Lz}iBnW� �C��Z�Xi K�)���y�v������
��U9���F�g�D�>�>Y:.��8�h�����>8��#|��:�&�](��`OB�&O��{X�C\��R��l�����r��n��n���������`�"p5����$�{C����3���
� -Sff�$���z
^��J��:G{��J�� ��
!��Ox 2p U��O0�9��#����#��pD���E9*9��)R�=�T
�
*��L-E 0��v�|��"���L���`~���������@����kL�kH[?J��� �S�C
���E����d�W�h)��9u1�]U��/�� ���*���.���.E��`�V����Q��B��$�q\z��kbX�� ���X?��[�BA��"'.��53{y`D�y��u��M6D[4 ��2�b���q�"�!l���+G~��K�m�I��h9�U�iPayh���/J1�X��e�����CWM����?������r���
$���N���[hFp�(��K�Y��,��y\g���� ����?��7<�
r&��/$�0��{�}��i������":��#���K���B�A�"'�/]�R�B�!����K5��jB{^�,4�����1��B��r��=t,�r�$�����nP�2B9?�4���Z��l�M��K�����$��t�)���.�y�H0Xd���� l2��
.b���
�5�a��[c�( k!d� ���������A��}��]W��F��@)6Uq��f������)��� �Wr��>���^�L?��\t����x������U(n 5�O����Y��
��6;hPBb�%�(�n]��������!�<��3������9���=C����9��������(
o��Ay� g3���]��
����0z�?����N.
��tf�a�3�^���E0i�* *��i�H��������Ek"���h��dZ�5&�f����z�D6(����`�CTW)2�����%��m�m�DLx�<�������l�!�#��m`L>�c�w{+)0��Ci_^D��1p"~]y����������mu�G��C���h����kv
:�y����w�=�HbZ����b�u?{���5S|��qp�D���c`�J������/x���6��`+��
���z�aNA>�����:2���n���T���������{����#:tD�8�2�HE��*�J�&��J��j4��#n��|�!6k|�������5rHb�5��:�^��+,EU��X`"�}�N������T<��`�����<��}fQa������� �/�9���V+A��������QMy��������_�E�Y.%����7.E_�sz8�!�Q��u��tL�E���P������������XR����k��Qa���sF����!15 �#��>`J��p/�0"��*� ������8;B`�~�?�\B����f]>��
�������"���
��A ;���{?"���'�g(������SY^T��������t�d���v���)#���|P�D���>z�]��Ml}��c� h;v�1�77. �
eDJ�DZ�����%��/�����G�9��V2���&�f��?L6�L��i{<r�e���;aHH"����8�����������Q�w
I�tHV��8&a����c�@�������s��Z�{� ��O^�\ PY�!�����������:�������V�8y�L(e�6��Q����� "[�4�!29������.��0�=��,alZags�X��Jq���?��<���82��0�����Y��)\��
���{Vo��KOm�",���}�����>���n�^�z��IA}D��o�ME�f�W���H�g{�������u��R�[�N�O_��!����t�]���������tq_
��se��2#��_������B~��2���&4 M������6��Z�R���%�9�N33����U?[���t����o�$O�E����$���%������V��N��V��[]������r� ���.�!L ?~{������:w$�_����]�0N��D��H�0�����7���A�T.�\ WR�����Y����LADK��� �d�2uP.A�����E��h|f���Bv�0��
E
+X�r����PX�*�����\�b��[�I�X���q�E^�w��_���n�����7�cv$� �����/��j�9�.���E��i��83�s^z0�PQY�]������)�c%���s��T���5���T�����TK!��M����5��J4y��*M�z%o��zw�t����������`j�\���S������;M�^u�T�/�{�,N��f����������+I�W���-N��`k+Y�S����Xq�@r����@���W���ZgI��O�T>����V;�}�Q9>���!j�����X����#H���d>��R\�c2�,��]����{B>Qsy@7����M���gfr�T�!�����V��I�@<�
@o��(�����x6�,fW���U�n%
�����"b���yQ��A�6�� 91�����S���� ~���=�HJi���I3 ������N��fY0-7�o�TA����rn�:��ZD����;�<=�X�p�sk�|�Q�@-���t4! ���_��_����Y��$�-��[R���/�hOi]/�#��>#�A<�Ji*��8�n<y�bU[�P��+�#���A$O��������b,����7�M|��Y��2=<#}�K���K%��R����XS��2��������������TA��|X�������Q�Sr"4��]!��U.��X������ia=�g�N�~��
�E�z���$��5����f����`����
�r����>"�G��W������-�V�b���=�fi�E�h"��>�D �����8@Q�!������� ��������QU�G�����@Hgk+E�/I����U���Ro@4��jT��I4��Fk�R[��9����N x�W���7�=���Mu>�7z�2H�dH�Us�Wr$<yY�;By\����4�c-] xz��c�r$����<��J�����hp!eW���s�����v
�?c�����p��)�GF2����$���5J��G���1q��H��l^U�3&L��l���9�g����7����-���O����^�}��wc�o>2���
�C�i��%��$zMH2�D������o|��q]�1���|�uj�|�����Bz�\qt�@|�D�� �1g�H� ��/�|H�3�=�72�_`�����o�5�4?0�����\�)q4V�3RD�e�7d�����G�#�.���/El�������"���d�����5�>_������NxJ�Vd�
�;� w�C:�&>�a 7rnG�<�O��>�-�L������[��U���-�����f����2�TJ��7�9fW����B�0������U������]��U c���R��j`�����]=,`W5
:`W�vJY6��S����=���*���?h�s�s���kQ���c�H�<����h/����D�0&��E��W��V���sv^�\4>k^TL%�#��_��2t>�#��'������p�I�X�:�V����T����S�K���O|'g���<�t����[7�8#B��*=sp����������=W�on+f�#�
U�Yld2����^V�k����4�RG^����9PoO�m�\��
��^������.���n�d�cu�& \���sDh$^@@�.KI4�^���^����#1�W�>Nv��i�Y�������
^m[����iY�6�����kK�,o�����D*��.I'�6��w � %;�����D���`��Y�J���U�7�-kf����&�(���$��A�|>�����t1�lX�y����(?~�����d�G#��o�ZUV4��S����P��xa��H��G)�V�1�ne;B�e�D6>K�D���F�Z�3��
^���ZW����VBi[�:;C�M*;K��P���uo����@+:������E�:����U�Y�i������qZy�wz�tL�~���k�Lq�@�������;����#�1$�?����}lY���;:�9{s��a��:{s�;�����AU;��/�E'���(��o����1�����F��.�9������hx�;��_�����!�_�����^��w��w����mj�������������9���B��o�q&�w��~p�?�� ;89��F���?�9���������^�����G�t9� }�1� ?��J)2�.�}���d�`�Q_5��k_}�>�������IZa�e��������}����~4�#����p�_
W�WI�y�,#Wf�_N�{H9������F�N� �v������b�V��#'n+�Z����!��V�JA�
L�N��j���\����������1���O���o�����*]��'bt�&b��'`��
a��*��^x�
f�o�B>�]���T*�����r�'���m����&j[� ��S�;�s:���`Y����b_�.o�E���\ � t5��K�g�.'���G��I~�%
n�K������������.Y��:��p�9�-�^�;W�K�3�tO�q��&�F
���S��,���Za��>E��\�?I�������f^y2[]�����8���8@G�v�dy��[Qp���`#���Y��t5W��V��
d�zWk&��^%g��^�K��~�����B�y�b]�}���G���/�o�E���cZ��T.�#
���_���U�/Nm�u��U�����3-uGA����Y�z��jT��7���-^�a[}p\�������-�RB����$��1t�����#��7�N>1K"