From 6ab88fc740a96e2729fceb021bfa982d1c2eace0 Mon Sep 17 00:00:00 2001 From: Justin Pryzby Date: Tue, 31 Mar 2020 20:35:41 -0500 Subject: [PATCH v18 6/6] Implement vacuum full/cluster (INDEX_TABLESPACE ) --- doc/src/sgml/ref/cluster.sgml | 10 ++++ doc/src/sgml/ref/vacuum.sgml | 10 ++++ src/backend/commands/cluster.c | 59 ++++++++++++++--------- src/backend/commands/matview.c | 3 +- src/backend/commands/tablecmds.c | 2 +- src/backend/commands/vacuum.c | 47 ++++++++---------- src/backend/postmaster/autovacuum.c | 1 + src/include/commands/cluster.h | 5 +- src/include/commands/vacuum.h | 6 ++- src/test/regress/input/tablespace.source | 13 +++++ src/test/regress/output/tablespace.source | 20 ++++++++ 11 files changed, 119 insertions(+), 57 deletions(-) diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml index 0e81e6189b..73216a14c0 100644 --- a/doc/src/sgml/ref/cluster.sgml +++ b/doc/src/sgml/ref/cluster.sgml @@ -28,6 +28,7 @@ CLUSTER [VERBOSE] VERBOSE TABLESPACE new_tablespace + INDEX_TABLESPACE new_tablespace @@ -105,6 +106,15 @@ CLUSTER [VERBOSE] + + INDEX_TABLESPACE + + + Specifies that the relation's indexes will be rebuilt on a new tablespace. + + + + table_name diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index b44b10c783..92d92b04d9 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -37,6 +37,7 @@ VACUUM ( FULL [, ...] ) [ table_and_columnsboolean ] PARALLEL integer TABLESPACE new_tablespace + INDEX_TABLESPACE new_tablespace and table_and_columns is: @@ -266,6 +267,15 @@ VACUUM ( FULL [, ...] ) [ table_and_columns + + TABLESPACE + + + Specifies that the relation's indexes will be rebuilt on a new tablespace. + + + + boolean diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 0d631efb30..1a7f31ef6e 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -70,7 +70,7 @@ typedef struct } RelToCluster; -static void rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTableSpaceOid, bool verbose); +static void rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTableSpaceOid, Oid NewIdxTableSpaceOid, bool verbose); static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti); @@ -105,9 +105,11 @@ void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) { ListCell *lc; - /* Name and Oid of tablespace to use for clustered relation. */ - char *tablespaceName = NULL; - Oid tablespaceOid = InvalidOid; + /* Name and Oid of tablespaces to use for clustered relations. */ + char *tablespaceName = NULL, + *idxtablespaceName = NULL; + Oid tablespaceOid; + Oid idxtablespaceOid; /* Parse list of generic parameter not handled by the parser */ foreach(lc, stmt->params) @@ -119,6 +121,8 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) // XXX: handle boolean opt: VERBOSE off else if (strcmp(opt->defname, "tablespace") == 0) tablespaceName = defGetString(opt); + else if (strcmp(opt->defname, "index_tablespace") == 0) + idxtablespaceName = defGetString(opt); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -127,18 +131,11 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) parser_errposition(pstate, opt->location))); } - /* Select tablespace Oid to use. */ - if (tablespaceName) - { - tablespaceOid = get_tablespace_oid(tablespaceName, false); - - /* Can't move a non-shared relation into pg_global */ - if (tablespaceOid == GLOBALTABLESPACE_OID) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot move non-shared relation to tablespace \"%s\"", - tablespaceName))); - } + /* Get tablespaces to use. */ + tablespaceOid = tablespaceName ? + get_tablespace_oid(tablespaceName, false) : InvalidOid; + idxtablespaceOid = idxtablespaceName ? + get_tablespace_oid(idxtablespaceName, false) : InvalidOid; if (stmt->relation != NULL) { @@ -209,7 +206,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) table_close(rel, NoLock); /* Do the job. */ - cluster_rel(tableOid, indexOid, tablespaceOid, stmt->options); + cluster_rel(tableOid, indexOid, tablespaceOid, idxtablespaceOid, stmt->options); } else { @@ -258,7 +255,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) PushActiveSnapshot(GetTransactionSnapshot()); /* Do the job. */ cluster_rel(rvtc->tableOid, rvtc->indexOid, tablespaceOid, - stmt->options | CLUOPT_RECHECK); + idxtablespaceOid, stmt->options | CLUOPT_RECHECK); PopActiveSnapshot(); CommitTransactionCommand(); } @@ -292,7 +289,7 @@ cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel) * InvalidOid, use the tablespace in-use instead. */ void -cluster_rel(Oid tableOid, Oid indexOid, Oid tablespaceOid, int options) +cluster_rel(Oid tableOid, Oid indexOid, Oid tablespaceOid, Oid idxtablespaceOid, int options) { Relation OldHeap; bool verbose = ((options & CLUOPT_VERBOSE) != 0); @@ -459,7 +456,7 @@ cluster_rel(Oid tableOid, Oid indexOid, Oid tablespaceOid, int options) TransferPredicateLocksToHeapRelation(OldHeap); /* rebuild_relation does all the dirty work */ - rebuild_relation(OldHeap, indexOid, tablespaceOid, verbose); + rebuild_relation(OldHeap, indexOid, tablespaceOid, idxtablespaceOid, verbose); /* NB: rebuild_relation does table_close() on OldHeap */ @@ -608,10 +605,11 @@ mark_index_clustered(Relation rel, Oid indexOid, bool is_internal) * NB: this routine closes OldHeap at the right time; caller should not. */ static void -rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTablespaceOid, bool verbose) +rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTablespaceOid, Oid NewIdxTablespaceOid, bool verbose) { Oid tableOid = RelationGetRelid(OldHeap); Oid tableSpace = OldHeap->rd_rel->reltablespace; + Oid idxtableSpace; Oid OIDNewHeap; char relpersistence; bool is_system_catalog; @@ -621,7 +619,20 @@ rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTablespaceOid, bool verb /* Use new tablespace if passed. */ if (OidIsValid(NewTablespaceOid)) + { tableSpace = NewTablespaceOid; + /* XXX: It's not a shared catalog, so refuse to move it to shared tablespace */ + if (tableSpace == GLOBALTABLESPACE_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move non-shared relation to tablespace \"%s\"", + get_tablespace_name(tableSpace)))); + } + + if (OidIsValid(NewIdxTablespaceOid)) + idxtableSpace = NewIdxTablespaceOid; + else + idxtableSpace = get_rel_tablespace(indexOid); /* Mark the correct index as clustered */ if (OidIsValid(indexOid)) @@ -650,7 +661,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, Oid NewTablespaceOid, bool verb finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog, swap_toast_by_content, false, true, frozenXid, cutoffMulti, - relpersistence); + relpersistence, idxtableSpace); } @@ -1398,7 +1409,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, - char newrelpersistence) + char newrelpersistence, Oid idxtableSpace) { ObjectAddress object; Oid mapped_tables[4]; @@ -1460,7 +1471,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, PROGRESS_CLUSTER_PHASE_REBUILD_INDEX); - reindex_relation(OIDOldHeap, InvalidOid, reindex_flags, 0); + reindex_relation(OIDOldHeap, idxtableSpace, reindex_flags, 0); /* Report that we are now doing clean up */ pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE, diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index f80a9e96a9..68ea07cae5 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -844,7 +844,8 @@ static void refresh_by_heap_swap(Oid matviewOid, Oid OIDNewHeap, char relpersistence) { finish_heap_swap(matviewOid, OIDNewHeap, false, false, true, true, - RecentXmin, ReadNextMultiXactId(), relpersistence); + RecentXmin, ReadNextMultiXactId(), relpersistence, + InvalidOid); } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f8fcbc1143..c2468f4cfd 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -4917,7 +4917,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, !OidIsValid(tab->newTableSpace), RecentXmin, ReadNextMultiXactId(), - persistence); + persistence, InvalidOid); } else { diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index e8a2b10497..b79aa3d30f 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -110,9 +110,9 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) bool parallel_option = false; ListCell *lc; - /* Name and Oid of tablespace to use for relations after VACUUM FULL. */ - char *tablespacename = NULL; - Oid tablespaceOid = InvalidOid; + /* Tablespace to use for relations after VACUUM FULL. */ + char *tablespacename = NULL, + *idxtablespacename = NULL; /* Set default value */ params.index_cleanup = VACOPT_TERNARY_DEFAULT; @@ -152,6 +152,8 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) params.truncate = get_vacopt_ternary_value(opt); else if (strcmp(opt->defname, "tablespace") == 0) tablespacename = defGetString(opt); + else if (strcmp(opt->defname, "index_tablespace") == 0) + idxtablespacename = defGetString(opt); else if (strcmp(opt->defname, "parallel") == 0) { parallel_option = true; @@ -213,26 +215,18 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot specify both FULL and PARALLEL options"))); - /* Get tablespace Oid to use. */ - if (tablespacename) - { - if ((params.options & VACOPT_FULL) == 0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("incompatible TABLESPACE option"), - errdetail("You can only use TABLESPACE with VACUUM FULL."))); - - tablespaceOid = get_tablespace_oid(tablespacename, false); - - /* Can't move a non-shared relation into pg_global */ - if (tablespaceOid == GLOBALTABLESPACE_OID) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot move non-shared relation to tablespace \"%s\"", - tablespacename))); + if ((params.options & VACOPT_FULL) == 0 && + (tablespacename || idxtablespacename)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("incompatible TABLESPACE option"), + errdetail("You can only use TABLESPACE with VACUUM FULL."))); - } - params.tablespace_oid = tablespaceOid; + /* Get tablespace Oids to use. */ + params.tablespace_oid = tablespacename ? + get_tablespace_oid(tablespacename, false) : InvalidOid; + params.idxtablespace_oid = idxtablespacename ? + get_tablespace_oid(idxtablespacename, false) : InvalidOid; /* * Make sure VACOPT_ANALYZE is specified if any column lists are present. @@ -1703,8 +1697,7 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) Relation onerel; LockRelId onerelid; Oid toast_relid, - save_userid, - tablespaceOid = InvalidOid; + save_userid; int save_sec_context; int save_nestlevel; @@ -1846,14 +1839,13 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) OidIsValid(params->tablespace_oid) && IsSystemRelation(onerel) && !allowSystemTableMods) { + params->tablespace_oid = InvalidOid; ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("skipping tablespace change of \"%s\"", RelationGetRelationName(onerel)), errdetail("Cannot move system relation, only VACUUM is performed."))); } - else - tablespaceOid = params->tablespace_oid; /* * Get a session-level lock too. This will protect our access to the @@ -1924,7 +1916,8 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params) cluster_options |= CLUOPT_VERBOSE; /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ - cluster_rel(relid, InvalidOid, tablespaceOid, cluster_options); + cluster_rel(relid, InvalidOid, params->tablespace_oid, + params->idxtablespace_oid, cluster_options); } else table_relation_vacuum(onerel, params, vac_strategy); diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 8acc321faa..7de5797f9c 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2898,6 +2898,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map, tab->at_params.is_wraparound = wraparound; tab->at_params.log_min_duration = log_min_duration; tab->at_params.tablespace_oid = InvalidOid; + tab->at_params.idxtablespace_oid = InvalidOid; tab->at_vacuum_cost_limit = vac_cost_limit; tab->at_vacuum_cost_delay = vac_cost_delay; tab->at_relname = NULL; diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index bc9f881d8c..515e810505 100644 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -20,7 +20,7 @@ extern void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel); -extern void cluster_rel(Oid tableOid, Oid indexOid, Oid tablespaceOid, int options); +extern void cluster_rel(Oid tableOid, Oid indexOid, Oid tablespaceOid, Oid indextablespaceOid, int options); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); @@ -34,6 +34,7 @@ extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_internal, TransactionId frozenXid, MultiXactId minMulti, - char newrelpersistence); + char newrelpersistence, + Oid idxtablespaceOid); #endif /* CLUSTER_H */ diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 6758e9812f..d6f8e5de23 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -229,8 +229,10 @@ typedef struct VacuumParams * disabled. */ int nworkers; - Oid tablespace_oid; /* tablespace Oid to use for relations - * after VACUUM FULL */ + + /* tablespace Oids to use for relations rebuilt by VACUUM FULL */ + Oid tablespace_oid; + Oid idxtablespace_oid; } VacuumParams; /* GUC parameters */ diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source index 6942775bfb..7b9d9ab2d9 100644 --- a/src/test/regress/input/tablespace.source +++ b/src/test/regress/input/tablespace.source @@ -74,6 +74,19 @@ REINDEX (TABLESPACE pg_default) TABLE CONCURRENTLY regress_tblspace_test_tbl; -- SELECT relname FROM pg_class WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace'); +-- check CLUSTER with INDEX_TABLESPACE change to non-default location +CLUSTER (INDEX_TABLESPACE regress_tblspace) regress_tblspace_test_tbl USING regress_tblspace_test_tbl_idx; -- ok +-- check relations moved to new tablespace +SELECT relname FROM pg_class +WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace') +ORDER BY relname; +-- check VACUUM with INDEX_TABLESPACE change +VACUUM (FULL, ANALYSE, FREEZE, INDEX_TABLESPACE pg_default) regress_tblspace_test_tbl; -- ok +-- check relations moved back to pg_default +SELECT relname FROM pg_class +WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace'); + + -- create a schema we can use CREATE SCHEMA testschema; diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source index 2825984394..4c339a12ec 100644 --- a/src/test/regress/output/tablespace.source +++ b/src/test/regress/output/tablespace.source @@ -103,6 +103,26 @@ WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspa --------- (0 rows) +-- check CLUSTER with INDEX_TABLESPACE change to non-default location +CLUSTER (INDEX_TABLESPACE regress_tblspace) regress_tblspace_test_tbl USING regress_tblspace_test_tbl_idx; -- ok +-- check relations moved to new tablespace +SELECT relname FROM pg_class +WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace') +ORDER BY relname; + relname +------------------------------- + regress_tblspace_test_tbl_idx +(1 row) + +-- check VACUUM with INDEX_TABLESPACE change +VACUUM (FULL, ANALYSE, FREEZE, INDEX_TABLESPACE pg_default) regress_tblspace_test_tbl; -- ok +-- check relations moved back to pg_default +SELECT relname FROM pg_class +WHERE reltablespace=(SELECT oid FROM pg_tablespace WHERE spcname='regress_tblspace'); + relname +--------- +(0 rows) + -- create a schema we can use CREATE SCHEMA testschema; -- try a table -- 2.17.0