Skip to content

Commit 692bcad

Browse files
committed
Allow CLUSTER and VACUUM FULL to change tablespace
1 parent 7acd35b commit 692bcad

File tree

13 files changed

+231
-16
lines changed

13 files changed

+231
-16
lines changed

doc/src/sgml/ref/cluster.sgml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PostgreSQL documentation
2121

2222
<refsynopsisdiv>
2323
<synopsis>
24-
CLUSTER [VERBOSE] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">index_name</replaceable> ]
24+
CLUSTER [VERBOSE] <replaceable class="parameter">table_name</replaceable> [ USING <replaceable class="parameter">index_name</replaceable> ] [ TABLESPACE <replaceable class="parameter">new_tablespace</replaceable> ]
2525
CLUSTER [VERBOSE]
2626
</synopsis>
2727
</refsynopsisdiv>
@@ -99,6 +99,15 @@ CLUSTER [VERBOSE]
9999
</listitem>
100100
</varlistentry>
101101

102+
<varlistentry>
103+
<term><replaceable class="parameter">new_tablespace</replaceable></term>
104+
<listitem>
105+
<para>
106+
The name of a specific tablespace to store clustered relations.
107+
</para>
108+
</listitem>
109+
</varlistentry>
110+
102111
<varlistentry>
103112
<term><literal>VERBOSE</literal></term>
104113
<listitem>

doc/src/sgml/ref/vacuum.sgml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ PostgreSQL documentation
2323
<synopsis>
2424
VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <replaceable class="parameter">table_and_columns</replaceable> [, ...] ]
2525
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="parameter">table_and_columns</replaceable> [, ...] ]
26+
VACUUM ( FULL [, ...] ) [ TABLESPACE <replaceable class="parameter">new_tablespace</replaceable> ] [ <replaceable class="parameter">table_and_columns</replaceable> [, ...] ]
2627

2728
<phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
2829

@@ -299,6 +300,16 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
299300
</para>
300301
</listitem>
301302
</varlistentry>
303+
304+
<varlistentry>
305+
<term><replaceable class="parameter">new_tablespace</replaceable></term>
306+
<listitem>
307+
<para>
308+
The name of a specific tablespace to write a new copy of the table.
309+
</para>
310+
</listitem>
311+
</varlistentry>
312+
302313
</variablelist>
303314
</refsect1>
304315

src/backend/commands/cluster.c

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@
3333
#include "catalog/namespace.h"
3434
#include "catalog/objectaccess.h"
3535
#include "catalog/pg_am.h"
36+
#include "catalog/pg_tablespace.h"
3637
#include "catalog/toasting.h"
3738
#include "commands/cluster.h"
3839
#include "commands/progress.h"
3940
#include "commands/tablecmds.h"
41+
#include "commands/tablespace.h"
4042
#include "commands/vacuum.h"
4143
#include "miscadmin.h"
4244
#include "optimizer/optimizer.h"
@@ -67,7 +69,7 @@ typedef struct
6769
} RelToCluster;
6870

6971

70-
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose);
72+
static void rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTableSpaceOid, bool verbose);
7173
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
7274
bool verbose, bool *pSwapToastByContent,
7375
TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
@@ -101,6 +103,22 @@ static List *get_tables_to_cluster(MemoryContext cluster_context);
101103
void
102104
cluster(ClusterStmt *stmt, bool isTopLevel)
103105
{
106+
/* Oid of tablespace to use for clustered relation. */
107+
Oid tablespaceOid = InvalidOid;
108+
109+
/* Select tablespace Oid to use. */
110+
if (stmt->tablespacename)
111+
{
112+
tablespaceOid = get_tablespace_oid(stmt->tablespacename, false);
113+
114+
/* Can't move a non-shared relation into pg_global */
115+
if (tablespaceOid == GLOBALTABLESPACE_OID)
116+
ereport(ERROR,
117+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
118+
errmsg("cannot move non-shared relation to tablespace \"%s\"",
119+
stmt->tablespacename)));
120+
}
121+
104122
if (stmt->relation != NULL)
105123
{
106124
/* This is the single-relation case. */
@@ -182,7 +200,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
182200
table_close(rel, NoLock);
183201

184202
/* Do the job. */
185-
cluster_rel(tableOid, indexOid, stmt->options);
203+
cluster_rel(tableOid, indexOid, tablespaceOid, stmt->options);
186204
}
187205
else
188206
{
@@ -230,7 +248,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
230248
/* functions in indexes may want a snapshot set */
231249
PushActiveSnapshot(GetTransactionSnapshot());
232250
/* Do the job. */
233-
cluster_rel(rvtc->tableOid, rvtc->indexOid,
251+
cluster_rel(rvtc->tableOid, rvtc->indexOid, tablespaceOid,
234252
stmt->options | CLUOPT_RECHECK);
235253
PopActiveSnapshot();
236254
CommitTransactionCommand();
@@ -262,7 +280,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
262280
* and error messages should refer to the operation as VACUUM not CLUSTER.
263281
*/
264282
void
265-
cluster_rel(Oid tableOid, Oid indexOid, int options)
283+
cluster_rel(Oid tableOid, Oid indexOid, Oid tablespaceOid, int options)
266284
{
267285
Relation OldHeap;
268286
bool verbose = ((options & CLUOPT_VERBOSE) != 0);
@@ -375,6 +393,23 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
375393
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
376394
errmsg("cannot cluster a shared catalog")));
377395

396+
if (OidIsValid(tablespaceOid) &&
397+
!allowSystemTableMods && IsSystemRelation(OldHeap))
398+
ereport(ERROR,
399+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
400+
errmsg("permission denied: \"%s\" is a system catalog",
401+
RelationGetRelationName(OldHeap))));
402+
403+
/*
404+
* We cannot support moving mapped relations into different tablespaces.
405+
* (In particular this eliminates all shared catalogs.)
406+
*/
407+
if (OidIsValid(tablespaceOid) && RelationIsMapped(OldHeap))
408+
ereport(ERROR,
409+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
410+
errmsg("cannot change tablespace of mapped relation \"%s\"",
411+
RelationGetRelationName(OldHeap))));
412+
378413
/*
379414
* Don't process temp tables of other backends ... their local buffer
380415
* manager is not going to cope.
@@ -425,7 +460,7 @@ cluster_rel(Oid tableOid, Oid indexOid, int options)
425460
TransferPredicateLocksToHeapRelation(OldHeap);
426461

427462
/* rebuild_relation does all the dirty work */
428-
rebuild_relation(OldHeap, indexOid, verbose);
463+
rebuild_relation(OldHeap, indexOid, tablespaceOid, verbose);
429464

430465
/* NB: rebuild_relation does table_close() on OldHeap */
431466

@@ -584,7 +619,7 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
584619
* NB: this routine closes OldHeap at the right time; caller should not.
585620
*/
586621
static void
587-
rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
622+
rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTablespaceOid, bool verbose)
588623
{
589624
Oid tableOid = RelationGetRelid(OldHeap);
590625
Oid tableSpace = OldHeap->rd_rel->reltablespace;
@@ -595,6 +630,10 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
595630
TransactionId frozenXid;
596631
MultiXactId cutoffMulti;
597632

633+
/* Use new tablespace if passed. */
634+
if (OidIsValid(NewTablespaceOid))
635+
tableSpace = NewTablespaceOid;
636+
598637
/* Mark the correct index as clustered */
599638
if (OidIsValid(indexOid))
600639
mark_index_clustered(OldHeap, indexOid, true);
@@ -1039,6 +1078,13 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
10391078
*/
10401079
Assert(!target_is_pg_class);
10411080

1081+
if (!allowSystemTableMods && IsSystemClass(r1, relform1) &&
1082+
relform1->reltablespace != relform2->reltablespace)
1083+
ereport(ERROR,
1084+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1085+
errmsg("permission denied: \"%s\" is a system catalog",
1086+
get_rel_name(r1))));
1087+
10421088
swaptemp = relform1->relfilenode;
10431089
relform1->relfilenode = relform2->relfilenode;
10441090
relform2->relfilenode = swaptemp;

src/backend/commands/vacuum.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131
#include "access/tableam.h"
3232
#include "access/transam.h"
3333
#include "access/xact.h"
34+
#include "catalog/catalog.h"
3435
#include "catalog/namespace.h"
3536
#include "catalog/pg_database.h"
3637
#include "catalog/pg_inherits.h"
3738
#include "catalog/pg_namespace.h"
39+
#include "catalog/pg_tablespace.h"
3840
#include "commands/cluster.h"
3941
#include "commands/defrem.h"
4042
#include "commands/vacuum.h"
43+
#include "commands/tablespace.h"
4144
#include "miscadmin.h"
4245
#include "nodes/makefuncs.h"
4346
#include "pgstat.h"
@@ -106,6 +109,8 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
106109
bool disable_page_skipping = false;
107110
bool parallel_option = false;
108111
ListCell *lc;
112+
Oid tablespaceOid = InvalidOid; /* Oid of tablespace to use for relations
113+
* after VACUUM FULL. */
109114

110115
/* Set default value */
111116
params.index_cleanup = VACOPT_TERNARY_DEFAULT;
@@ -241,6 +246,28 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
241246
params.multixact_freeze_table_age = -1;
242247
}
243248

249+
/* Get tablespace Oid to use. */
250+
if (vacstmt->tablespacename)
251+
{
252+
if (params.options & VACOPT_FULL)
253+
{
254+
tablespaceOid = get_tablespace_oid(vacstmt->tablespacename, false);
255+
256+
/* Can't move a non-shared relation into pg_global */
257+
if (tablespaceOid == GLOBALTABLESPACE_OID)
258+
ereport(ERROR,
259+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
260+
errmsg("cannot move non-shared relation to tablespace \"%s\"",
261+
vacstmt->tablespacename)));
262+
}
263+
else
264+
ereport(ERROR,
265+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
266+
errmsg("incompatible TABLESPACE option"),
267+
errdetail("You can only use TABLESPACE with VACUUM FULL.")));
268+
}
269+
params.tablespace_oid = tablespaceOid;
270+
244271
/* user-invoked vacuum is never "for wraparound" */
245272
params.is_wraparound = false;
246273

@@ -1672,8 +1699,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
16721699
LOCKMODE lmode;
16731700
Relation onerel;
16741701
LockRelId onerelid;
1675-
Oid toast_relid;
1676-
Oid save_userid;
1702+
Oid toast_relid,
1703+
save_userid,
1704+
tablespaceOid = InvalidOid;
16771705
int save_sec_context;
16781706
int save_nestlevel;
16791707

@@ -1807,6 +1835,23 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
18071835
return true;
18081836
}
18091837

1838+
/*
1839+
* We cannot support moving system relations into different tablespaces,
1840+
* unless allow_system_table_mods=1.
1841+
*/
1842+
if (params->options & VACOPT_FULL &&
1843+
OidIsValid(params->tablespace_oid) &&
1844+
IsSystemRelation(onerel) && !allowSystemTableMods)
1845+
{
1846+
ereport(WARNING,
1847+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1848+
errmsg("skipping tablespace change of \"%s\"",
1849+
RelationGetRelationName(onerel)),
1850+
errdetail("Cannot move system relation, only VACUUM is performed.")));
1851+
}
1852+
else
1853+
tablespaceOid = params->tablespace_oid;
1854+
18101855
/*
18111856
* Get a session-level lock too. This will protect our access to the
18121857
* relation across multiple transactions, so that we can vacuum the
@@ -1876,7 +1921,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
18761921
cluster_options |= CLUOPT_VERBOSE;
18771922

18781923
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
1879-
cluster_rel(relid, InvalidOid, cluster_options);
1924+
cluster_rel(relid, InvalidOid, tablespaceOid, cluster_options);
18801925
}
18811926
else
18821927
table_relation_vacuum(onerel, params, vac_strategy);

src/backend/nodes/copyfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,7 @@ _copyClusterStmt(const ClusterStmt *from)
33163316
COPY_NODE_FIELD(relation);
33173317
COPY_STRING_FIELD(indexname);
33183318
COPY_SCALAR_FIELD(options);
3319+
COPY_STRING_FIELD(tablespacename);
33193320

33203321
return newnode;
33213322
}
@@ -3901,6 +3902,7 @@ _copyVacuumStmt(const VacuumStmt *from)
39013902
COPY_NODE_FIELD(options);
39023903
COPY_NODE_FIELD(rels);
39033904
COPY_SCALAR_FIELD(is_vacuumcmd);
3905+
COPY_STRING_FIELD(tablespacename);
39043906

39053907
return newnode;
39063908
}

src/backend/nodes/equalfuncs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@ _equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
12151215
COMPARE_NODE_FIELD(relation);
12161216
COMPARE_STRING_FIELD(indexname);
12171217
COMPARE_SCALAR_FIELD(options);
1218+
COMPARE_SCALAR_FIELD(tablespacename);
12181219

12191220
return true;
12201221
}
@@ -1702,6 +1703,7 @@ _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
17021703
COMPARE_NODE_FIELD(options);
17031704
COMPARE_NODE_FIELD(rels);
17041705
COMPARE_SCALAR_FIELD(is_vacuumcmd);
1706+
COMPARE_SCALAR_FIELD(tablespacename);
17051707

17061708
return true;
17071709
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy