1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Add TABLESPACE option to REINDEX

This patch adds the possibility to move indexes to a new tablespace
while rebuilding them.  Both the concurrent and the non-concurrent cases
are supported, and the following set of restrictions apply:
- When using TABLESPACE with a REINDEX command that targets a
partitioned table or index, all the indexes of the leaf partitions are
moved to the new tablespace.  The tablespace references of the non-leaf,
partitioned tables in pg_class.reltablespace are not changed. This
requires an extra ALTER TABLE SET TABLESPACE.
- Any index on a toast table rebuilt as part of a parent table is kept
in its original tablespace.
- The operation is forbidden on system catalogs, including trying to
directly move a toast relation with REINDEX.  This results in an error
if doing REINDEX on a single object.  REINDEX SCHEMA, DATABASE and
SYSTEM skip system relations when TABLESPACE is used.

Author: Alexey Kondratov, Michael Paquier, Justin Pryzby
Reviewed-by: Álvaro Herrera, Michael Paquier
Discussion: https://postgr.es/m/8a8f5f73-00d3-55f8-7583-1375ca8f6a91@postgrespro.ru
This commit is contained in:
Michael Paquier
2021-02-04 14:34:20 +09:00
parent 9624321ec5
commit c5b286047c
7 changed files with 505 additions and 4 deletions

View File

@ -2474,6 +2474,7 @@ ExecReindex(ParseState *pstate, ReindexStmt *stmt, bool isTopLevel)
ListCell *lc;
bool concurrently = false;
bool verbose = false;
char *tablespacename = NULL;
/* Parse option list */
foreach(lc, stmt->params)
@ -2484,6 +2485,8 @@ ExecReindex(ParseState *pstate, ReindexStmt *stmt, bool isTopLevel)
verbose = defGetBoolean(opt);
else if (strcmp(opt->defname, "concurrently") == 0)
concurrently = defGetBoolean(opt);
else if (strcmp(opt->defname, "tablespace") == 0)
tablespacename = defGetString(opt);
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@ -2500,6 +2503,30 @@ ExecReindex(ParseState *pstate, ReindexStmt *stmt, bool isTopLevel)
(verbose ? REINDEXOPT_VERBOSE : 0) |
(concurrently ? REINDEXOPT_CONCURRENTLY : 0);
/*
* Assign the tablespace OID to move indexes to, with InvalidOid to do
* nothing.
*/
if (tablespacename != NULL)
{
params.tablespaceOid = get_tablespace_oid(tablespacename, false);
/* Check permissions except when moving to database's default */
if (OidIsValid(params.tablespaceOid) &&
params.tablespaceOid != MyDatabaseTableSpace)
{
AclResult aclresult;
aclresult = pg_tablespace_aclcheck(params.tablespaceOid,
GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_TABLESPACE,
get_tablespace_name(params.tablespaceOid));
}
}
else
params.tablespaceOid = InvalidOid;
switch (stmt->kind)
{
case REINDEX_OBJECT_INDEX:
@ -2730,6 +2757,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
List *relids = NIL;
int num_keys;
bool concurrent_warning = false;
bool tablespace_warning = false;
AssertArg(objectName);
Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
@ -2856,6 +2884,40 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
continue;
}
/*
* If a new tablespace is set, check if this relation has to be
* skipped.
*/
if (OidIsValid(params->tablespaceOid))
{
bool skip_rel = false;
/*
* Mapped relations cannot be moved to different tablespaces (in
* particular this eliminates all shared catalogs.).
*/
if (RELKIND_HAS_STORAGE(classtuple->relkind) &&
!OidIsValid(classtuple->relfilenode))
skip_rel = true;
/*
* A system relation is always skipped, even with
* allow_system_table_mods enabled.
*/
if (IsSystemClass(relid, classtuple))
skip_rel = true;
if (skip_rel)
{
if (!tablespace_warning)
ereport(WARNING,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot move system relations, skipping all")));
tablespace_warning = true;
continue;
}
}
/* Save the list of relation OIDs in private context */
old = MemoryContextSwitchTo(private_context);
@ -3032,6 +3094,24 @@ ReindexMultipleInternal(List *relids, ReindexParams *params)
continue;
}
/*
* Check permissions except when moving to database's default if a new
* tablespace is chosen. Note that this check also happens in
* ExecReindex(), but we do an extra check here as this runs across
* multiple transactions.
*/
if (OidIsValid(params->tablespaceOid) &&
params->tablespaceOid != MyDatabaseTableSpace)
{
AclResult aclresult;
aclresult = pg_tablespace_aclcheck(params->tablespaceOid,
GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_TABLESPACE,
get_tablespace_name(params->tablespaceOid));
}
relkind = get_rel_relkind(relid);
relpersistence = get_rel_persistence(relid);
@ -3210,6 +3290,13 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
heapRelation = table_open(relationOid,
ShareUpdateExclusiveLock);
if (OidIsValid(params->tablespaceOid) &&
IsSystemRelation(heapRelation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move system relation \"%s\"",
RelationGetRelationName(heapRelation))));
/* Add all the valid indexes of relation to list */
foreach(lc, RelationGetIndexList(heapRelation))
{
@ -3346,6 +3433,14 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
else
heapRelation = table_open(heapId,
ShareUpdateExclusiveLock);
if (OidIsValid(params->tablespaceOid) &&
IsSystemRelation(heapRelation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move system relation \"%s\"",
get_rel_name(relationOid))));
table_close(heapRelation, NoLock);
/* Save the list of relation OIDs in private context */
@ -3390,6 +3485,13 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
return false;
}
/* It's not a shared catalog, so refuse to move it to shared tablespace */
if (params->tablespaceOid == GLOBALTABLESPACE_OID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move non-shared relation to tablespace \"%s\"",
get_tablespace_name(params->tablespaceOid))));
Assert(heapRelationIds != NIL);
/*-----
@ -3427,6 +3529,7 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
Relation heapRel;
Relation newIndexRel;
LockRelId *lockrelid;
Oid tablespaceid;
indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock);
heapRel = table_open(indexRel->rd_index->indrelid,
@ -3458,9 +3561,17 @@ ReindexRelationConcurrently(Oid relationOid, ReindexParams *params)
get_rel_namespace(indexRel->rd_index->indrelid),
false);
/* Choose the new tablespace, indexes of toast tables are not moved */
if (OidIsValid(params->tablespaceOid) &&
heapRel->rd_rel->relkind != RELKIND_TOASTVALUE)
tablespaceid = params->tablespaceOid;
else
tablespaceid = indexRel->rd_rel->reltablespace;
/* Create new index definition based on given index */
newIndexId = index_concurrently_create_copy(heapRel,
idx->indexId,
tablespaceid,
concurrentName);
/*