PostgreSQL Source Code  git master
cluster.c File Reference
#include "postgres.h"
#include "access/amapi.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "access/toast_internals.h"
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/defrem.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "optimizer/optimizer.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/relmapper.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
Include dependency graph for cluster.c:

Go to the source code of this file.

Data Structures

struct  RelToCluster
 

Functions

static void rebuild_relation (Relation OldHeap, Oid indexOid, bool verbose)
 
static void copy_table_data (Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
 
static Listget_tables_to_cluster (MemoryContext cluster_context)
 
void cluster (ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 
void cluster_rel (Oid tableOid, Oid indexOid, ClusterParams *params)
 
void check_index_is_clusterable (Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
 
void mark_index_clustered (Relation rel, Oid indexOid, bool is_internal)
 
Oid make_new_heap (Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
 
static void swap_relation_files (Oid r1, Oid r2, bool target_is_pg_class, bool swap_toast_by_content, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, Oid *mapped_tables)
 
void finish_heap_swap (Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
 

Function Documentation

◆ check_index_is_clusterable()

void check_index_is_clusterable ( Relation  OldHeap,
Oid  indexOid,
bool  recheck,
LOCKMODE  lockmode 
)

Definition at line 443 of file cluster.c.

References IndexAmRoutine::amclusterable, ereport, errcode(), errmsg(), ERROR, heap_attisnull(), index_close(), index_open(), NoLock, RelationData::rd_indam, RelationData::rd_index, RelationData::rd_indextuple, RelationGetRelationName, and RelationGetRelid.

Referenced by ATExecClusterOn(), and cluster_rel().

444 {
445  Relation OldIndex;
446 
447  OldIndex = index_open(indexOid, lockmode);
448 
449  /*
450  * Check that index is in fact an index on the given relation
451  */
452  if (OldIndex->rd_index == NULL ||
453  OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
454  ereport(ERROR,
455  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
456  errmsg("\"%s\" is not an index for table \"%s\"",
457  RelationGetRelationName(OldIndex),
458  RelationGetRelationName(OldHeap))));
459 
460  /* Index AM must allow clustering */
461  if (!OldIndex->rd_indam->amclusterable)
462  ereport(ERROR,
463  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
464  errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
465  RelationGetRelationName(OldIndex))));
466 
467  /*
468  * Disallow clustering on incomplete indexes (those that might not index
469  * every row of the relation). We could relax this by making a separate
470  * seqscan pass over the table to copy the missing rows, but that seems
471  * expensive and tedious.
472  */
473  if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
474  ereport(ERROR,
475  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
476  errmsg("cannot cluster on partial index \"%s\"",
477  RelationGetRelationName(OldIndex))));
478 
479  /*
480  * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
481  * it might well not contain entries for every heap row, or might not even
482  * be internally consistent. (But note that we don't check indcheckxmin;
483  * the worst consequence of following broken HOT chains would be that we
484  * might put recently-dead tuples out-of-order in the new table, and there
485  * is little harm in that.)
486  */
487  if (!OldIndex->rd_index->indisvalid)
488  ereport(ERROR,
489  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
490  errmsg("cannot cluster on invalid index \"%s\"",
491  RelationGetRelationName(OldIndex))));
492 
493  /* Drop relcache refcnt on OldIndex, but keep lock */
494  index_close(OldIndex, NoLock);
495 }
struct IndexAmRoutine * rd_indam
Definition: rel.h:201
int errcode(int sqlerrcode)
Definition: elog.c:698
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:359
struct HeapTupleData * rd_indextuple
Definition: rel.h:189
Form_pg_index rd_index
Definition: rel.h:187
#define ERROR
Definition: elog.h:46
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:511
bool amclusterable
Definition: amapi.h:238
#define ereport(elevel,...)
Definition: elog.h:157
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define RelationGetRelid(relation)
Definition: rel.h:477
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132

◆ cluster()

void cluster ( ParseState pstate,
ClusterStmt stmt,
bool  isTopLevel 
)

Definition at line 103 of file cluster.c.

References AccessExclusiveLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CLUOPT_RECHECK, CLUOPT_VERBOSE, cluster_rel(), CommitTransactionCommand(), defGetBoolean(), DefElem::defname, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), get_relname_relid(), get_tables_to_cluster(), GetTransactionSnapshot(), ClusterStmt::indexname, RelToCluster::indexOid, InvalidOid, lfirst, lfirst_oid, DefElem::location, MemoryContextDelete(), NoLock, OidIsValid, ClusterParams::options, ClusterStmt::params, parser_errposition(), PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), PushActiveSnapshot(), RangeVarCallbackOwnsTable(), RangeVarGetRelidExtended(), RelationData::rd_rel, ClusterStmt::relation, RELATION_IS_OTHER_TEMP, RelationGetIndexList(), RangeVar::relname, StartTransactionCommand(), table_close(), table_open(), RelToCluster::tableOid, and verbose.

Referenced by standard_ProcessUtility(), start_postmaster(), and stop_postmaster().

104 {
105  ListCell *lc;
106  ClusterParams params = {0};
107  bool verbose = false;
108 
109  /* Parse option list */
110  foreach(lc, stmt->params)
111  {
112  DefElem *opt = (DefElem *) lfirst(lc);
113 
114  if (strcmp(opt->defname, "verbose") == 0)
115  verbose = defGetBoolean(opt);
116  else
117  ereport(ERROR,
118  (errcode(ERRCODE_SYNTAX_ERROR),
119  errmsg("unrecognized CLUSTER option \"%s\"",
120  opt->defname),
121  parser_errposition(pstate, opt->location)));
122  }
123 
124  params.options = (verbose ? CLUOPT_VERBOSE : 0);
125 
126  if (stmt->relation != NULL)
127  {
128  /* This is the single-relation case. */
129  Oid tableOid,
130  indexOid = InvalidOid;
131  Relation rel;
132 
133  /* Find, lock, and check permissions on the table */
134  tableOid = RangeVarGetRelidExtended(stmt->relation,
136  0,
138  rel = table_open(tableOid, NoLock);
139 
140  /*
141  * Reject clustering a remote temp table ... their local buffer
142  * manager is not going to cope.
143  */
144  if (RELATION_IS_OTHER_TEMP(rel))
145  ereport(ERROR,
146  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
147  errmsg("cannot cluster temporary tables of other sessions")));
148 
149  /*
150  * Reject clustering a partitioned table.
151  */
152  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
153  ereport(ERROR,
154  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
155  errmsg("cannot cluster a partitioned table")));
156 
157  if (stmt->indexname == NULL)
158  {
159  ListCell *index;
160 
161  /* We need to find the index that has indisclustered set. */
162  foreach(index, RelationGetIndexList(rel))
163  {
164  indexOid = lfirst_oid(index);
165  if (get_index_isclustered(indexOid))
166  break;
167  indexOid = InvalidOid;
168  }
169 
170  if (!OidIsValid(indexOid))
171  ereport(ERROR,
172  (errcode(ERRCODE_UNDEFINED_OBJECT),
173  errmsg("there is no previously clustered index for table \"%s\"",
174  stmt->relation->relname)));
175  }
176  else
177  {
178  /*
179  * The index is expected to be in the same namespace as the
180  * relation.
181  */
182  indexOid = get_relname_relid(stmt->indexname,
183  rel->rd_rel->relnamespace);
184  if (!OidIsValid(indexOid))
185  ereport(ERROR,
186  (errcode(ERRCODE_UNDEFINED_OBJECT),
187  errmsg("index \"%s\" for table \"%s\" does not exist",
188  stmt->indexname, stmt->relation->relname)));
189  }
190 
191  /* close relation, keep lock till commit */
192  table_close(rel, NoLock);
193 
194  /* Do the job. */
195  cluster_rel(tableOid, indexOid, &params);
196  }
197  else
198  {
199  /*
200  * This is the "multi relation" case. We need to cluster all tables
201  * that have some index with indisclustered set.
202  */
203  MemoryContext cluster_context;
204  List *rvs;
205  ListCell *rv;
206 
207  /*
208  * We cannot run this form of CLUSTER inside a user transaction block;
209  * we'd be holding locks way too long.
210  */
211  PreventInTransactionBlock(isTopLevel, "CLUSTER");
212 
213  /*
214  * Create special memory context for cross-transaction storage.
215  *
216  * Since it is a child of PortalContext, it will go away even in case
217  * of error.
218  */
219  cluster_context = AllocSetContextCreate(PortalContext,
220  "Cluster",
222 
223  /*
224  * Build the list of relations to cluster. Note that this lives in
225  * cluster_context.
226  */
227  rvs = get_tables_to_cluster(cluster_context);
228 
229  /* Commit to get out of starting transaction */
232 
233  /* Ok, now that we've got them all, cluster them one by one */
234  foreach(rv, rvs)
235  {
236  RelToCluster *rvtc = (RelToCluster *) lfirst(rv);
237  ClusterParams cluster_params = params;
238 
239  /* Start a new transaction for each relation. */
241  /* functions in indexes may want a snapshot set */
243  /* Do the job. */
244  cluster_params.options |= CLUOPT_RECHECK;
245  cluster_rel(rvtc->tableOid, rvtc->indexOid,
246  &cluster_params);
249  }
250 
251  /* Start a new transaction for the cleanup work. */
253 
254  /* Clean up working storage */
255  MemoryContextDelete(cluster_context);
256  }
257 }
List * params
Definition: parsenodes.h:3332
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:16464
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
#define AllocSetContextCreate
Definition: memutils.h:173
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
void CommitTransactionCommand(void)
Definition: xact.c:2939
int errcode(int sqlerrcode)
Definition: elog.c:698
#define CLUOPT_VERBOSE
Definition: cluster.h:24
void PopActiveSnapshot(void)
Definition: snapmgr.c:759
Form_pg_class rd_rel
Definition: rel.h:109
unsigned int Oid
Definition: postgres_ext.h:31
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
#define OidIsValid(objectId)
Definition: c.h:710
Oid tableOid
Definition: cluster.c:66
MemoryContext PortalContext
Definition: mcxt.c:57
Definition: type.h:89
char * relname
Definition: primnodes.h:68
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * indexname
Definition: parsenodes.h:3331
#define ERROR
Definition: elog.h:46
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1856
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
#define NoLock
Definition: lockdefs.h:34
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
int location
Definition: parsenodes.h:749
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3379
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
Definition: cluster.c:277
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:237
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1547
static int verbose
bits32 options
Definition: cluster.h:29
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
#define lfirst(lc)
Definition: pg_list.h:169
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:631
void StartTransactionCommand(void)
Definition: xact.c:2838
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3555
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4573
Oid indexOid
Definition: cluster.c:67
#define CLUOPT_RECHECK
Definition: cluster.h:23
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:909
char * defname
Definition: parsenodes.h:746
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
RangeVar * relation
Definition: parsenodes.h:3330
Definition: pg_list.h:50
#define lfirst_oid(lc)
Definition: pg_list.h:171

◆ cluster_rel()

void cluster_rel ( Oid  tableOid,
Oid  indexOid,
ClusterParams params 
)

Definition at line 277 of file cluster.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), CLUOPT_RECHECK, CLUOPT_VERBOSE, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), GetUserId(), ObjectIdGetDatum, OidIsValid, ClusterParams::options, pg_class_ownercheck(), pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), PROGRESS_CLUSTER_COMMAND, PROGRESS_CLUSTER_COMMAND_CLUSTER, PROGRESS_CLUSTER_COMMAND_VACUUM_FULL, PROGRESS_COMMAND_CLUSTER, RelationData::rd_rel, rebuild_relation(), relation_close(), RELATION_IS_OTHER_TEMP, RelationIsPopulated, RELOID, SearchSysCacheExists1, TransferPredicateLocksToHeapRelation(), try_relation_open(), and verbose.

Referenced by cluster(), and vacuum_rel().

278 {
279  Relation OldHeap;
280  bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
281  bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
282 
283  /* Check for user-requested abort. */
285 
287  if (OidIsValid(indexOid))
290  else
293 
294  /*
295  * We grab exclusive access to the target rel and index for the duration
296  * of the transaction. (This is redundant for the single-transaction
297  * case, since cluster() already did it.) The index lock is taken inside
298  * check_index_is_clusterable.
299  */
300  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
301 
302  /* If the table has gone away, we can skip processing it */
303  if (!OldHeap)
304  {
306  return;
307  }
308 
309  /*
310  * Since we may open a new transaction for each relation, we have to check
311  * that the relation still is what we think it is.
312  *
313  * If this is a single-transaction CLUSTER, we can skip these tests. We
314  * *must* skip the one on indisclustered since it would reject an attempt
315  * to cluster a not-previously-clustered index.
316  */
317  if (recheck)
318  {
319  /* Check that the user still owns the relation */
320  if (!pg_class_ownercheck(tableOid, GetUserId()))
321  {
324  return;
325  }
326 
327  /*
328  * Silently skip a temp table for a remote session. Only doing this
329  * check in the "recheck" case is appropriate (which currently means
330  * somebody is executing a database-wide CLUSTER), because there is
331  * another check in cluster() which will stop any attempt to cluster
332  * remote temp tables by name. There is another check in cluster_rel
333  * which is redundant, but we leave it for extra safety.
334  */
335  if (RELATION_IS_OTHER_TEMP(OldHeap))
336  {
339  return;
340  }
341 
342  if (OidIsValid(indexOid))
343  {
344  /*
345  * Check that the index still exists
346  */
348  {
351  return;
352  }
353 
354  /*
355  * Check that the index is still the one with indisclustered set.
356  */
357  if (!get_index_isclustered(indexOid))
358  {
361  return;
362  }
363  }
364  }
365 
366  /*
367  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
368  * would work in most respects, but the index would only get marked as
369  * indisclustered in the current database, leading to unexpected behavior
370  * if CLUSTER were later invoked in another database.
371  */
372  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
373  ereport(ERROR,
374  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
375  errmsg("cannot cluster a shared catalog")));
376 
377  /*
378  * Don't process temp tables of other backends ... their local buffer
379  * manager is not going to cope.
380  */
381  if (RELATION_IS_OTHER_TEMP(OldHeap))
382  {
383  if (OidIsValid(indexOid))
384  ereport(ERROR,
385  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
386  errmsg("cannot cluster temporary tables of other sessions")));
387  else
388  ereport(ERROR,
389  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390  errmsg("cannot vacuum temporary tables of other sessions")));
391  }
392 
393  /*
394  * Also check for active uses of the relation in the current transaction,
395  * including open scans and pending AFTER trigger events.
396  */
397  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
398 
399  /* Check heap and index are valid to cluster on */
400  if (OidIsValid(indexOid))
401  check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
402 
403  /*
404  * Quietly ignore the request if this is a materialized view which has not
405  * been populated from its query. No harm is done because there is no data
406  * to deal with, and we don't want to throw an error if this is part of a
407  * multi-relation request -- for example, CLUSTER was run on the entire
408  * database.
409  */
410  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
411  !RelationIsPopulated(OldHeap))
412  {
415  return;
416  }
417 
418  /*
419  * All predicate locks on the tuples or pages are about to be made
420  * invalid, because we move tuples around. Promote them to relation
421  * locks. Predicate locks on indexes will be promoted when they are
422  * reindexed.
423  */
425 
426  /* rebuild_relation does all the dirty work */
427  rebuild_relation(OldHeap, indexOid, verbose);
428 
429  /* NB: rebuild_relation does table_close() on OldHeap */
430 
432 }
#define RelationIsPopulated(relation)
Definition: rel.h:650
Oid GetUserId(void)
Definition: miscinit.c:478
int errcode(int sqlerrcode)
Definition: elog.c:698
#define CLUOPT_VERBOSE
Definition: cluster.h:24
Form_pg_class rd_rel
Definition: rel.h:109
#define OidIsValid(objectId)
Definition: c.h:710
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
void pgstat_progress_end_command(void)
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:184
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:576
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:75
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:74
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3923
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:55
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3146
static int verbose
bits32 options
Definition: cluster.h:29
#define ereport(elevel,...)
Definition: elog.h:157
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
void pgstat_progress_update_param(int index, int64 val)
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:631
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4818
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3555
#define CLUOPT_RECHECK
Definition: cluster.h:23
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:909
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
Definition: cluster.c:443
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:120

◆ copy_table_data()

static void copy_table_data ( Oid  OIDNewHeap,
Oid  OIDOldHeap,
Oid  OIDOldIndex,
bool  verbose,
bool pSwapToastByContent,
TransactionId pFreezeXid,
MultiXactId pCutoffMulti 
)
static

Definition at line 757 of file cluster.c.

References AccessExclusiveLock, Assert, CacheInvalidateRelcacheByTuple(), CatalogTupleUpdate(), CommandCounterIncrement(), DEBUG2, elevel, elog, ereport, errdetail(), errmsg(), ERROR, get_namespace_name(), GETSTRUCT, heap_freetuple(), HeapTupleIsValid, index_close(), index_open(), INFO, InvalidOid, LockRelationOid(), MultiXactIdIsValid, MultiXactIdPrecedes(), NoLock, ObjectIdGetDatum, OidIsValid, pg_rusage_init(), pg_rusage_show(), PG_USED_FOR_ASSERTS_ONLY, plan_cluster_use_sort(), RelationData::rd_rel, RelationData::rd_toastoid, RelationGetDescr, RelationGetNamespace, RelationGetNumberOfBlocks, RelationGetRelationName, RELOID, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), table_open(), table_relation_copy_for_cluster(), TransactionIdIsValid, TransactionIdPrecedes(), and vacuum_set_xid_limits().

Referenced by rebuild_relation().

760 {
761  Relation NewHeap,
762  OldHeap,
763  OldIndex;
764  Relation relRelation;
765  HeapTuple reltup;
766  Form_pg_class relform;
769  TransactionId OldestXmin;
770  TransactionId FreezeXid;
771  MultiXactId MultiXactCutoff;
772  bool use_sort;
773  double num_tuples = 0,
774  tups_vacuumed = 0,
775  tups_recently_dead = 0;
776  BlockNumber num_pages;
777  int elevel = verbose ? INFO : DEBUG2;
778  PGRUsage ru0;
779 
780  pg_rusage_init(&ru0);
781 
782  /*
783  * Open the relations we need.
784  */
785  NewHeap = table_open(OIDNewHeap, AccessExclusiveLock);
786  OldHeap = table_open(OIDOldHeap, AccessExclusiveLock);
787  if (OidIsValid(OIDOldIndex))
788  OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
789  else
790  OldIndex = NULL;
791 
792  /*
793  * Their tuple descriptors should be exactly alike, but here we only need
794  * assume that they have the same number of columns.
795  */
796  oldTupDesc = RelationGetDescr(OldHeap);
797  newTupDesc = RelationGetDescr(NewHeap);
798  Assert(newTupDesc->natts == oldTupDesc->natts);
799 
800  /*
801  * If the OldHeap has a toast table, get lock on the toast table to keep
802  * it from being vacuumed. This is needed because autovacuum processes
803  * toast tables independently of their main tables, with no lock on the
804  * latter. If an autovacuum were to start on the toast table after we
805  * compute our OldestXmin below, it would use a later OldestXmin, and then
806  * possibly remove as DEAD toast tuples belonging to main tuples we think
807  * are only RECENTLY_DEAD. Then we'd fail while trying to copy those
808  * tuples.
809  *
810  * We don't need to open the toast relation here, just lock it. The lock
811  * will be held till end of transaction.
812  */
813  if (OldHeap->rd_rel->reltoastrelid)
814  LockRelationOid(OldHeap->rd_rel->reltoastrelid, AccessExclusiveLock);
815 
816  /*
817  * If both tables have TOAST tables, perform toast swap by content. It is
818  * possible that the old table has a toast table but the new one doesn't,
819  * if toastable columns have been dropped. In that case we have to do
820  * swap by links. This is okay because swap by content is only essential
821  * for system catalogs, and we don't support schema changes for them.
822  */
823  if (OldHeap->rd_rel->reltoastrelid && NewHeap->rd_rel->reltoastrelid)
824  {
825  *pSwapToastByContent = true;
826 
827  /*
828  * When doing swap by content, any toast pointers written into NewHeap
829  * must use the old toast table's OID, because that's where the toast
830  * data will eventually be found. Set this up by setting rd_toastoid.
831  * This also tells toast_save_datum() to preserve the toast value
832  * OIDs, which we want so as not to invalidate toast pointers in
833  * system catalog caches, and to avoid making multiple copies of a
834  * single toast value.
835  *
836  * Note that we must hold NewHeap open until we are done writing data,
837  * since the relcache will not guarantee to remember this setting once
838  * the relation is closed. Also, this technique depends on the fact
839  * that no one will try to read from the NewHeap until after we've
840  * finished writing it and swapping the rels --- otherwise they could
841  * follow the toast pointers to the wrong place. (It would actually
842  * work for values copied over from the old toast table, but not for
843  * any values that we toast which were previously not toasted.)
844  */
845  NewHeap->rd_toastoid = OldHeap->rd_rel->reltoastrelid;
846  }
847  else
848  *pSwapToastByContent = false;
849 
850  /*
851  * Compute xids used to freeze and weed out dead tuples and multixacts.
852  * Since we're going to rewrite the whole table anyway, there's no reason
853  * not to be aggressive about this.
854  */
855  vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0,
856  &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
857  NULL);
858 
859  /*
860  * FreezeXid will become the table's new relfrozenxid, and that mustn't go
861  * backwards, so take the max.
862  */
863  if (TransactionIdIsValid(OldHeap->rd_rel->relfrozenxid) &&
864  TransactionIdPrecedes(FreezeXid, OldHeap->rd_rel->relfrozenxid))
865  FreezeXid = OldHeap->rd_rel->relfrozenxid;
866 
867  /*
868  * MultiXactCutoff, similarly, shouldn't go backwards either.
869  */
870  if (MultiXactIdIsValid(OldHeap->rd_rel->relminmxid) &&
871  MultiXactIdPrecedes(MultiXactCutoff, OldHeap->rd_rel->relminmxid))
872  MultiXactCutoff = OldHeap->rd_rel->relminmxid;
873 
874  /*
875  * Decide whether to use an indexscan or seqscan-and-optional-sort to scan
876  * the OldHeap. We know how to use a sort to duplicate the ordering of a
877  * btree index, and will use seqscan-and-sort for that case if the planner
878  * tells us it's cheaper. Otherwise, always indexscan if an index is
879  * provided, else plain seqscan.
880  */
881  if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID)
882  use_sort = plan_cluster_use_sort(OIDOldHeap, OIDOldIndex);
883  else
884  use_sort = false;
885 
886  /* Log what we're doing */
887  if (OldIndex != NULL && !use_sort)
888  ereport(elevel,
889  (errmsg("clustering \"%s.%s\" using index scan on \"%s\"",
891  RelationGetRelationName(OldHeap),
892  RelationGetRelationName(OldIndex))));
893  else if (use_sort)
894  ereport(elevel,
895  (errmsg("clustering \"%s.%s\" using sequential scan and sort",
897  RelationGetRelationName(OldHeap))));
898  else
899  ereport(elevel,
900  (errmsg("vacuuming \"%s.%s\"",
902  RelationGetRelationName(OldHeap))));
903 
904  /*
905  * Hand of the actual copying to AM specific function, the generic code
906  * cannot know how to deal with visibility across AMs. Note that this
907  * routine is allowed to set FreezeXid / MultiXactCutoff to different
908  * values (e.g. because the AM doesn't use freezing).
909  */
910  table_relation_copy_for_cluster(OldHeap, NewHeap, OldIndex, use_sort,
911  OldestXmin, &FreezeXid, &MultiXactCutoff,
912  &num_tuples, &tups_vacuumed,
913  &tups_recently_dead);
914 
915  /* return selected values to caller, get set as relfrozenxid/minmxid */
916  *pFreezeXid = FreezeXid;
917  *pCutoffMulti = MultiXactCutoff;
918 
919  /* Reset rd_toastoid just to be tidy --- it shouldn't be looked at again */
920  NewHeap->rd_toastoid = InvalidOid;
921 
922  num_pages = RelationGetNumberOfBlocks(NewHeap);
923 
924  /* Log what we did */
925  ereport(elevel,
926  (errmsg("\"%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
927  RelationGetRelationName(OldHeap),
928  tups_vacuumed, num_tuples,
929  RelationGetNumberOfBlocks(OldHeap)),
930  errdetail("%.0f dead row versions cannot be removed yet.\n"
931  "%s.",
932  tups_recently_dead,
933  pg_rusage_show(&ru0))));
934 
935  if (OldIndex != NULL)
936  index_close(OldIndex, NoLock);
937  table_close(OldHeap, NoLock);
938  table_close(NewHeap, NoLock);
939 
940  /* Update pg_class to reflect the correct values of pages and tuples. */
941  relRelation = table_open(RelationRelationId, RowExclusiveLock);
942 
943  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDNewHeap));
944  if (!HeapTupleIsValid(reltup))
945  elog(ERROR, "cache lookup failed for relation %u", OIDNewHeap);
946  relform = (Form_pg_class) GETSTRUCT(reltup);
947 
948  relform->relpages = num_pages;
949  relform->reltuples = num_tuples;
950 
951  /* Don't update the stats for pg_class. See swap_relation_files. */
952  if (OIDOldHeap != RelationRelationId)
953  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
954  else
956 
957  /* Clean up. */
958  heap_freetuple(reltup);
959  table_close(relRelation, RowExclusiveLock);
960 
961  /* Make the update visible */
963 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
bool plan_cluster_use_sort(Oid tableOid, Oid indexOid)
Definition: planner.c:5733
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
uint32 TransactionId
Definition: c.h:587
#define RelationGetDescr(relation)
Definition: rel.h:503
#define INFO
Definition: elog.h:33
void vacuum_set_xid_limits(Relation rel, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, TransactionId *oldestXmin, TransactionId *freezeLimit, TransactionId *xidFullScanLimit, MultiXactId *multiXactCutoff, MultiXactId *mxactFullScanLimit)
Definition: vacuum.c:957
uint32 BlockNumber
Definition: block.h:31
Form_pg_class rd_rel
Definition: rel.h:109
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
#define OidIsValid(objectId)
Definition: c.h:710
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define DEBUG2
Definition: elog.h:24
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3316
#define NoLock
Definition: lockdefs.h:34
Oid rd_toastoid
Definition: rel.h:246
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:1042
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
#define RelationGetRelationName(relation)
Definition: rel.h:511
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
static int verbose
static int elevel
Definition: vacuumlazy.c:403
void CommandCounterIncrement(void)
Definition: xact.c:1021
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:213
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
TransactionId MultiXactId
Definition: c.h:597
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:804
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3159
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:175
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1315
#define TransactionIdIsValid(xid)
Definition: transam.h:41
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:109
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
static void table_relation_copy_for_cluster(Relation OldTable, Relation NewTable, Relation OldIndex, bool use_sort, TransactionId OldestXmin, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead)
Definition: tableam.h:1646
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:155
#define RelationGetNamespace(relation)
Definition: rel.h:518

◆ finish_heap_swap()

void finish_heap_swap ( Oid  OIDOldHeap,
Oid  OIDNewHeap,
bool  is_system_catalog,
bool  swap_toast_by_content,
bool  check_constraints,
bool  is_internal,
TransactionId  frozenXid,
MultiXactId  cutoffMulti,
char  newrelpersistence 
)

Definition at line 1357 of file cluster.c.

References CacheInvalidateCatalog(), CatalogTupleUpdate(), DROP_RESTRICT, elog, ERROR, GETSTRUCT, HeapTupleIsValid, i, NAMEDATALEN, NoLock, ObjectIdGetDatum, OidIsValid, PERFORM_DELETION_INTERNAL, performDeletion(), pgstat_progress_update_param(), PROGRESS_CLUSTER_PHASE, PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP, PROGRESS_CLUSTER_PHASE_REBUILD_INDEX, PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES, RelationData::rd_rel, REINDEX_REL_CHECK_CONSTRAINTS, REINDEX_REL_FORCE_INDEXES_PERMANENT, REINDEX_REL_FORCE_INDEXES_UNLOGGED, REINDEX_REL_SUPPRESS_INDEX_USE, reindex_relation(), relation_close(), RelationClearMissing(), RelationMapRemoveMapping(), RELOID, RenameRelationInternal(), RowExclusiveLock, SearchSysCacheCopy1, snprintf, swap_relation_files(), HeapTupleData::t_self, table_close(), table_open(), and toast_get_valid_index().

Referenced by ATRewriteTables(), rebuild_relation(), and refresh_by_heap_swap().

1365 {
1366  ObjectAddress object;
1367  Oid mapped_tables[4];
1368  int reindex_flags;
1369  ReindexParams reindex_params = {0};
1370  int i;
1371 
1372  /* Report that we are now swapping relation files */
1375 
1376  /* Zero out possible results from swapped_relation_files */
1377  memset(mapped_tables, 0, sizeof(mapped_tables));
1378 
1379  /*
1380  * Swap the contents of the heap relations (including any toast tables).
1381  * Also set old heap's relfrozenxid to frozenXid.
1382  */
1383  swap_relation_files(OIDOldHeap, OIDNewHeap,
1384  (OIDOldHeap == RelationRelationId),
1385  swap_toast_by_content, is_internal,
1386  frozenXid, cutoffMulti, mapped_tables);
1387 
1388  /*
1389  * If it's a system catalog, queue a sinval message to flush all catcaches
1390  * on the catalog when we reach CommandCounterIncrement.
1391  */
1392  if (is_system_catalog)
1393  CacheInvalidateCatalog(OIDOldHeap);
1394 
1395  /*
1396  * Rebuild each index on the relation (but not the toast table, which is
1397  * all-new at this point). It is important to do this before the DROP
1398  * step because if we are processing a system catalog that will be used
1399  * during DROP, we want to have its indexes available. There is no
1400  * advantage to the other order anyway because this is all transactional,
1401  * so no chance to reclaim disk space before commit. We do not need a
1402  * final CommandCounterIncrement() because reindex_relation does it.
1403  *
1404  * Note: because index_build is called via reindex_relation, it will never
1405  * set indcheckxmin true for the indexes. This is OK even though in some
1406  * sense we are building new indexes rather than rebuilding existing ones,
1407  * because the new heap won't contain any HOT chains at all, let alone
1408  * broken ones, so it can't be necessary to set indcheckxmin.
1409  */
1410  reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE;
1411  if (check_constraints)
1412  reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS;
1413 
1414  /*
1415  * Ensure that the indexes have the same persistence as the parent
1416  * relation.
1417  */
1418  if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1419  reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1420  else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1421  reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
1422 
1423  /* Report that we are now reindexing relations */
1426 
1427  reindex_relation(OIDOldHeap, reindex_flags, &reindex_params);
1428 
1429  /* Report that we are now doing clean up */
1432 
1433  /*
1434  * If the relation being rebuilt is pg_class, swap_relation_files()
1435  * couldn't update pg_class's own pg_class entry (check comments in
1436  * swap_relation_files()), thus relfrozenxid was not updated. That's
1437  * annoying because a potential reason for doing a VACUUM FULL is a
1438  * imminent or actual anti-wraparound shutdown. So, now that we can
1439  * access the new relation using its indices, update relfrozenxid.
1440  * pg_class doesn't have a toast relation, so we don't need to update the
1441  * corresponding toast relation. Not that there's little point moving all
1442  * relfrozenxid updates here since swap_relation_files() needs to write to
1443  * pg_class for non-mapped relations anyway.
1444  */
1445  if (OIDOldHeap == RelationRelationId)
1446  {
1447  Relation relRelation;
1448  HeapTuple reltup;
1449  Form_pg_class relform;
1450 
1451  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1452 
1453  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
1454  if (!HeapTupleIsValid(reltup))
1455  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1456  relform = (Form_pg_class) GETSTRUCT(reltup);
1457 
1458  relform->relfrozenxid = frozenXid;
1459  relform->relminmxid = cutoffMulti;
1460 
1461  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1462 
1463  table_close(relRelation, RowExclusiveLock);
1464  }
1465 
1466  /* Destroy new heap with old filenode */
1467  object.classId = RelationRelationId;
1468  object.objectId = OIDNewHeap;
1469  object.objectSubId = 0;
1470 
1471  /*
1472  * The new relation is local to our transaction and we know nothing
1473  * depends on it, so DROP_RESTRICT should be OK.
1474  */
1476 
1477  /* performDeletion does CommandCounterIncrement at end */
1478 
1479  /*
1480  * Now we must remove any relation mapping entries that we set up for the
1481  * transient table, as well as its toast table and toast index if any. If
1482  * we fail to do this before commit, the relmapper will complain about new
1483  * permanent map entries being added post-bootstrap.
1484  */
1485  for (i = 0; OidIsValid(mapped_tables[i]); i++)
1486  RelationMapRemoveMapping(mapped_tables[i]);
1487 
1488  /*
1489  * At this point, everything is kosher except that, if we did toast swap
1490  * by links, the toast table's name corresponds to the transient table.
1491  * The name is irrelevant to the backend because it's referenced by OID,
1492  * but users looking at the catalogs could be confused. Rename it to
1493  * prevent this problem.
1494  *
1495  * Note no lock required on the relation, because we already hold an
1496  * exclusive lock on it.
1497  */
1498  if (!swap_toast_by_content)
1499  {
1500  Relation newrel;
1501 
1502  newrel = table_open(OIDOldHeap, NoLock);
1503  if (OidIsValid(newrel->rd_rel->reltoastrelid))
1504  {
1505  Oid toastidx;
1506  char NewToastName[NAMEDATALEN];
1507 
1508  /* Get the associated valid index to be renamed */
1509  toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
1510  NoLock);
1511 
1512  /* rename the toast table ... */
1513  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
1514  OIDOldHeap);
1515  RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1516  NewToastName, true, false);
1517 
1518  /* ... and its valid index too. */
1519  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
1520  OIDOldHeap);
1521 
1522  RenameRelationInternal(toastidx,
1523  NewToastName, true, true);
1524  }
1525  relation_close(newrel, NoLock);
1526  }
1527 
1528  /* if it's not a catalog table, clear any missing attribute settings */
1529  if (!is_system_catalog)
1530  {
1531  Relation newrel;
1532 
1533  newrel = table_open(OIDOldHeap, NoLock);
1534  RelationClearMissing(newrel);
1535  relation_close(newrel, NoLock);
1536  }
1537 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES
Definition: progress.h:69
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition: index.h:156
Form_pg_class rd_rel
Definition: rel.h:109
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:313
void RelationClearMissing(Relation rel)
Definition: heap.c:2079
static void swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, bool swap_toast_by_content, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, Oid *mapped_tables)
Definition: cluster.c:992
void CacheInvalidateCatalog(Oid catalogId)
Definition: inval.c:1255
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition: index.h:158
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
void RelationMapRemoveMapping(Oid relationId)
Definition: relmapper.c:373
void pgstat_progress_update_param(int index, int64 val)
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:157
#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP
Definition: progress.h:71
bool reindex_relation(Oid relid, int flags, ReindexParams *params)
Definition: index.c:3795
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:175
#define elog(elevel,...)
Definition: elog.h:232
int i
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition: index.h:159
#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX
Definition: progress.h:70
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:3821
#define PROGRESS_CLUSTER_PHASE
Definition: progress.h:56
#define snprintf
Definition: port.h:216
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:133

◆ get_tables_to_cluster()

static List * get_tables_to_cluster ( MemoryContext  cluster_context)
static

Definition at line 1547 of file cluster.c.

References AccessShareLock, BoolGetDatum, BTEqualStrategyNumber, ForwardScanDirection, GETSTRUCT, GetUserId(), heap_getnext(), RelToCluster::indexOid, lappend(), MemoryContextSwitchTo(), NIL, palloc(), pg_class_ownercheck(), relation_close(), ScanKeyInit(), table_beginscan_catalog(), table_endscan(), table_open(), and RelToCluster::tableOid.

Referenced by cluster().

1548 {
1549  Relation indRelation;
1550  TableScanDesc scan;
1551  ScanKeyData entry;
1552  HeapTuple indexTuple;
1554  MemoryContext old_context;
1555  RelToCluster *rvtc;
1556  List *rvs = NIL;
1557 
1558  /*
1559  * Get all indexes that have indisclustered set and are owned by
1560  * appropriate user.
1561  */
1562  indRelation = table_open(IndexRelationId, AccessShareLock);
1563  ScanKeyInit(&entry,
1564  Anum_pg_index_indisclustered,
1565  BTEqualStrategyNumber, F_BOOLEQ,
1566  BoolGetDatum(true));
1567  scan = table_beginscan_catalog(indRelation, 1, &entry);
1568  while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1569  {
1570  index = (Form_pg_index) GETSTRUCT(indexTuple);
1571 
1572  if (!pg_class_ownercheck(index->indrelid, GetUserId()))
1573  continue;
1574 
1575  /*
1576  * We have to build the list in a different memory context so it will
1577  * survive the cross-transaction processing
1578  */
1579  old_context = MemoryContextSwitchTo(cluster_context);
1580 
1581  rvtc = (RelToCluster *) palloc(sizeof(RelToCluster));
1582  rvtc->tableOid = index->indrelid;
1583  rvtc->indexOid = index->indexrelid;
1584  rvs = lappend(rvs, rvtc);
1585 
1586  MemoryContextSwitchTo(old_context);
1587  }
1588  table_endscan(scan);
1589 
1590  relation_close(indRelation, AccessShareLock);
1591 
1592  return rvs;
1593 }
#define NIL
Definition: pg_list.h:65
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
Oid GetUserId(void)
Definition: miscinit.c:478
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
Oid tableOid
Definition: cluster.c:66
Definition: type.h:89
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1340
List * lappend(List *list, void *datum)
Definition: list.c:336
FormData_pg_index * Form_pg_index
Definition: pg_index.h:69
#define BoolGetDatum(X)
Definition: postgres.h:446
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4818
Oid indexOid
Definition: cluster.c:67
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:991
void * palloc(Size size)
Definition: mcxt.c:1062
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
Definition: pg_list.h:50
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ make_new_heap()

Oid make_new_heap ( Oid  OIDOldHeap,
Oid  NewTableSpace,
Oid  NewAccessMethod,
char  relpersistence,
LOCKMODE  lockmode 
)

Definition at line 631 of file cluster.c.

References Assert, CommandCounterIncrement(), elog, ERROR, heap_create_with_catalog(), HeapTupleIsValid, InvalidOid, LookupCreationNamespace(), NAMEDATALEN, NewHeapCreateToastTable(), NIL, NoLock, ObjectIdGetDatum, OidIsValid, ONCOMMIT_NOOP, RelationData::rd_rel, RelationGetDescr, RelationGetNamespace, RelationIsMapped, ReleaseSysCache(), RELOID, SearchSysCache1(), snprintf, SysCacheGetAttr(), table_close(), and table_open().

Referenced by ATRewriteTables(), ExecRefreshMatView(), and rebuild_relation().

633 {
634  TupleDesc OldHeapDesc;
635  char NewHeapName[NAMEDATALEN];
636  Oid OIDNewHeap;
637  Oid toastid;
638  Relation OldHeap;
639  HeapTuple tuple;
640  Datum reloptions;
641  bool isNull;
642  Oid namespaceid;
643 
644  OldHeap = table_open(OIDOldHeap, lockmode);
645  OldHeapDesc = RelationGetDescr(OldHeap);
646 
647  /*
648  * Note that the NewHeap will not receive any of the defaults or
649  * constraints associated with the OldHeap; we don't need 'em, and there's
650  * no reason to spend cycles inserting them into the catalogs only to
651  * delete them.
652  */
653 
654  /*
655  * But we do want to use reloptions of the old heap for new heap.
656  */
657  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
658  if (!HeapTupleIsValid(tuple))
659  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
660  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
661  &isNull);
662  if (isNull)
663  reloptions = (Datum) 0;
664 
665  if (relpersistence == RELPERSISTENCE_TEMP)
666  namespaceid = LookupCreationNamespace("pg_temp");
667  else
668  namespaceid = RelationGetNamespace(OldHeap);
669 
670  /*
671  * Create the new heap, using a temporary name in the same namespace as
672  * the existing table. NOTE: there is some risk of collision with user
673  * relnames. Working around this seems more trouble than it's worth; in
674  * particular, we can't create the new heap in a different namespace from
675  * the old, or we will have problems with the TEMP status of temp tables.
676  *
677  * Note: the new heap is not a shared relation, even if we are rebuilding
678  * a shared rel. However, we do make the new heap mapped if the source is
679  * mapped. This simplifies swap_relation_files, and is absolutely
680  * necessary for rebuilding pg_class, for reasons explained there.
681  */
682  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
683 
684  OIDNewHeap = heap_create_with_catalog(NewHeapName,
685  namespaceid,
686  NewTableSpace,
687  InvalidOid,
688  InvalidOid,
689  InvalidOid,
690  OldHeap->rd_rel->relowner,
691  NewAccessMethod,
692  OldHeapDesc,
693  NIL,
694  RELKIND_RELATION,
695  relpersistence,
696  false,
697  RelationIsMapped(OldHeap),
699  reloptions,
700  false,
701  true,
702  true,
703  OIDOldHeap,
704  NULL);
705  Assert(OIDNewHeap != InvalidOid);
706 
707  ReleaseSysCache(tuple);
708 
709  /*
710  * Advance command counter so that the newly-created relation's catalog
711  * tuples will be visible to table_open.
712  */
714 
715  /*
716  * If necessary, create a TOAST table for the new relation.
717  *
718  * If the relation doesn't have a TOAST table already, we can't need one
719  * for the new relation. The other way around is possible though: if some
720  * wide columns have been dropped, NewHeapCreateToastTable can decide that
721  * no TOAST table is needed for the new table.
722  *
723  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
724  * that the TOAST table will be visible for insertion.
725  */
726  toastid = OldHeap->rd_rel->reltoastrelid;
727  if (OidIsValid(toastid))
728  {
729  /* keep the existing toast table's reloptions, if any */
730  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
731  if (!HeapTupleIsValid(tuple))
732  elog(ERROR, "cache lookup failed for relation %u", toastid);
733  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
734  &isNull);
735  if (isNull)
736  reloptions = (Datum) 0;
737 
738  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode);
739 
740  ReleaseSysCache(tuple);
741  }
742 
743  table_close(OldHeap, NoLock);
744 
745  return OIDNewHeap;
746 }
#define NIL
Definition: pg_list.h:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define RelationGetDescr(relation)
Definition: rel.h:503
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2981
Form_pg_class rd_rel
Definition: rel.h:109
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
#define NoLock
Definition: lockdefs.h:34
#define RelationIsMapped(relation)
Definition: rel.h:526
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1127
uintptr_t Datum
Definition: postgres.h:411
void CommandCounterIncrement(void)
Definition: xact.c:1021
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1175
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1388
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:804
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition: heap.c:1143
#define elog(elevel,...)
Definition: elog.h:232
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define snprintf
Definition: port.h:216
#define RelationGetNamespace(relation)
Definition: rel.h:518
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:64

◆ mark_index_clustered()

void mark_index_clustered ( Relation  rel,
Oid  indexOid,
bool  is_internal 
)

Definition at line 503 of file cluster.c.

References CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), GETSTRUCT, heap_freetuple(), HeapTupleIsValid, INDEXRELID, InvalidOid, InvokeObjectPostAlterHookArg, lfirst_oid, ObjectIdGetDatum, OidIsValid, RelationData::rd_rel, RelationGetIndexList(), RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecClusterOn(), ATExecDropCluster(), and rebuild_relation().

504 {
505  HeapTuple indexTuple;
506  Form_pg_index indexForm;
507  Relation pg_index;
508  ListCell *index;
509 
510  /* Disallow applying to a partitioned table */
511  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
512  ereport(ERROR,
513  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
514  errmsg("cannot mark index clustered in partitioned table")));
515 
516  /*
517  * If the index is already marked clustered, no need to do anything.
518  */
519  if (OidIsValid(indexOid))
520  {
521  if (get_index_isclustered(indexOid))
522  return;
523  }
524 
525  /*
526  * Check each index of the relation and set/clear the bit as needed.
527  */
528  pg_index = table_open(IndexRelationId, RowExclusiveLock);
529 
530  foreach(index, RelationGetIndexList(rel))
531  {
532  Oid thisIndexOid = lfirst_oid(index);
533 
534  indexTuple = SearchSysCacheCopy1(INDEXRELID,
535  ObjectIdGetDatum(thisIndexOid));
536  if (!HeapTupleIsValid(indexTuple))
537  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
538  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
539 
540  /*
541  * Unset the bit if set. We know it's wrong because we checked this
542  * earlier.
543  */
544  if (indexForm->indisclustered)
545  {
546  indexForm->indisclustered = false;
547  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
548  }
549  else if (thisIndexOid == indexOid)
550  {
551  /* this was checked earlier, but let's be real sure */
552  if (!indexForm->indisvalid)
553  elog(ERROR, "cannot cluster on invalid index %u", indexOid);
554  indexForm->indisclustered = true;
555  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
556  }
557 
558  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
559  InvalidOid, is_internal);
560 
561  heap_freetuple(indexTuple);
562  }
563 
564  table_close(pg_index, RowExclusiveLock);
565 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
int errcode(int sqlerrcode)
Definition: elog.c:698
Form_pg_class rd_rel
Definition: rel.h:109
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:178
Definition: type.h:89
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
FormData_pg_index * Form_pg_index
Definition: pg_index.h:69
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3555
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4573
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:175
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define elog(elevel,...)
Definition: elog.h:232
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define lfirst_oid(lc)
Definition: pg_list.h:171

◆ rebuild_relation()

static void rebuild_relation ( Relation  OldHeap,
Oid  indexOid,
bool  verbose 
)
static

Definition at line 576 of file cluster.c.

References AccessExclusiveLock, copy_table_data(), finish_heap_swap(), IsSystemRelation(), make_new_heap(), mark_index_clustered(), NoLock, OidIsValid, RelationData::rd_rel, RelationGetRelid, and table_close().

Referenced by cluster_rel().

577 {
578  Oid tableOid = RelationGetRelid(OldHeap);
579  Oid accessMethod = OldHeap->rd_rel->relam;
580  Oid tableSpace = OldHeap->rd_rel->reltablespace;
581  Oid OIDNewHeap;
582  char relpersistence;
583  bool is_system_catalog;
584  bool swap_toast_by_content;
585  TransactionId frozenXid;
586  MultiXactId cutoffMulti;
587 
588  /* Mark the correct index as clustered */
589  if (OidIsValid(indexOid))
590  mark_index_clustered(OldHeap, indexOid, true);
591 
592  /* Remember info about rel before closing OldHeap */
593  relpersistence = OldHeap->rd_rel->relpersistence;
594  is_system_catalog = IsSystemRelation(OldHeap);
595 
596  /* Close relcache entry, but keep lock until transaction commit */
597  table_close(OldHeap, NoLock);
598 
599  /* Create the transient table that will receive the re-ordered data */
600  OIDNewHeap = make_new_heap(tableOid, tableSpace,
601  accessMethod,
602  relpersistence,
604 
605  /* Copy the heap data into the new table in the desired order */
606  copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
607  &swap_toast_by_content, &frozenXid, &cutoffMulti);
608 
609  /*
610  * Swap the physical files of the target and transient tables, then
611  * rebuild the target's indexes and throw away the transient table.
612  */
613  finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog,
614  swap_toast_by_content, false, true,
615  frozenXid, cutoffMulti,
616  relpersistence);
617 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
bool IsSystemRelation(Relation relation)
Definition: catalog.c:74
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition: cluster.c:1357
uint32 TransactionId
Definition: c.h:587
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition: cluster.c:631
Form_pg_class rd_rel
Definition: rel.h:109
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define NoLock
Definition: lockdefs.h:34
static int verbose
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:503
TransactionId MultiXactId
Definition: c.h:597
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
Definition: cluster.c:757
#define AccessExclusiveLock
Definition: lockdefs.h:45
#define RelationGetRelid(relation)
Definition: rel.h:477

◆ swap_relation_files()

static void swap_relation_files ( Oid  r1,
Oid  r2,
bool  target_is_pg_class,
bool  swap_toast_by_content,
bool  is_internal,
TransactionId  frozenXid,
MultiXactId  cutoffMulti,
Oid mapped_tables 
)
static

Definition at line 992 of file cluster.c.

References AccessExclusiveLock, Assert, CacheInvalidateRelcacheByTuple(), CatalogCloseIndexes(), CatalogOpenIndexes(), CatalogTupleUpdateWithInfo(), ObjectAddress::classId, deleteDependencyRecordsFor(), DEPENDENCY_INTERNAL, elog, ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, InvalidMultiXactId, InvalidOid, InvalidTransactionId, InvokeObjectPostAlterHookArg, IsSystemClass(), NameStr, NoLock, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, RelationData::rd_createSubid, RelationData::rd_firstRelfilenodeSubid, RelationData::rd_newRelfilenodeSubid, recordDependencyOn(), relation_close(), relation_open(), RelationAssumeNewRelfilenode(), RelationCloseSmgrByOid(), RelationMapOidToFilenode(), RelationMapUpdateMap(), RELOID, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), table_open(), toast_get_valid_index(), TransactionIdIsNormal, and TransactionIdIsValid.

Referenced by finish_heap_swap().

998 {
999  Relation relRelation;
1000  HeapTuple reltup1,
1001  reltup2;
1002  Form_pg_class relform1,
1003  relform2;
1004  Oid relfilenode1,
1005  relfilenode2;
1006  Oid swaptemp;
1007  char swptmpchr;
1008 
1009  /* We need writable copies of both pg_class tuples. */
1010  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1011 
1013  if (!HeapTupleIsValid(reltup1))
1014  elog(ERROR, "cache lookup failed for relation %u", r1);
1015  relform1 = (Form_pg_class) GETSTRUCT(reltup1);
1016 
1018  if (!HeapTupleIsValid(reltup2))
1019  elog(ERROR, "cache lookup failed for relation %u", r2);
1020  relform2 = (Form_pg_class) GETSTRUCT(reltup2);
1021 
1022  relfilenode1 = relform1->relfilenode;
1023  relfilenode2 = relform2->relfilenode;
1024 
1025  if (OidIsValid(relfilenode1) && OidIsValid(relfilenode2))
1026  {
1027  /*
1028  * Normal non-mapped relations: swap relfilenodes, reltablespaces,
1029  * relpersistence
1030  */
1031  Assert(!target_is_pg_class);
1032 
1033  swaptemp = relform1->relfilenode;
1034  relform1->relfilenode = relform2->relfilenode;
1035  relform2->relfilenode = swaptemp;
1036 
1037  swaptemp = relform1->reltablespace;
1038  relform1->reltablespace = relform2->reltablespace;
1039  relform2->reltablespace = swaptemp;
1040 
1041  swaptemp = relform1->relam;
1042  relform1->relam = relform2->relam;
1043  relform2->relam = swaptemp;
1044 
1045  swptmpchr = relform1->relpersistence;
1046  relform1->relpersistence = relform2->relpersistence;
1047  relform2->relpersistence = swptmpchr;
1048 
1049  /* Also swap toast links, if we're swapping by links */
1050  if (!swap_toast_by_content)
1051  {
1052  swaptemp = relform1->reltoastrelid;
1053  relform1->reltoastrelid = relform2->reltoastrelid;
1054  relform2->reltoastrelid = swaptemp;
1055  }
1056  }
1057  else
1058  {
1059  /*
1060  * Mapped-relation case. Here we have to swap the relation mappings
1061  * instead of modifying the pg_class columns. Both must be mapped.
1062  */
1063  if (OidIsValid(relfilenode1) || OidIsValid(relfilenode2))
1064  elog(ERROR, "cannot swap mapped relation \"%s\" with non-mapped relation",
1065  NameStr(relform1->relname));
1066 
1067  /*
1068  * We can't change the tablespace nor persistence of a mapped rel, and
1069  * we can't handle toast link swapping for one either, because we must
1070  * not apply any critical changes to its pg_class row. These cases
1071  * should be prevented by upstream permissions tests, so these checks
1072  * are non-user-facing emergency backstop.
1073  */
1074  if (relform1->reltablespace != relform2->reltablespace)
1075  elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
1076  NameStr(relform1->relname));
1077  if (relform1->relpersistence != relform2->relpersistence)
1078  elog(ERROR, "cannot change persistence of mapped relation \"%s\"",
1079  NameStr(relform1->relname));
1080  if (relform1->relam != relform2->relam)
1081  elog(ERROR, "cannot change access method of mapped relation \"%s\"",
1082  NameStr(relform1->relname));
1083  if (!swap_toast_by_content &&
1084  (relform1->reltoastrelid || relform2->reltoastrelid))
1085  elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
1086  NameStr(relform1->relname));
1087 
1088  /*
1089  * Fetch the mappings --- shouldn't fail, but be paranoid
1090  */
1091  relfilenode1 = RelationMapOidToFilenode(r1, relform1->relisshared);
1092  if (!OidIsValid(relfilenode1))
1093  elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1094  NameStr(relform1->relname), r1);
1095  relfilenode2 = RelationMapOidToFilenode(r2, relform2->relisshared);
1096  if (!OidIsValid(relfilenode2))
1097  elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1098  NameStr(relform2->relname), r2);
1099 
1100  /*
1101  * Send replacement mappings to relmapper. Note these won't actually
1102  * take effect until CommandCounterIncrement.
1103  */
1104  RelationMapUpdateMap(r1, relfilenode2, relform1->relisshared, false);
1105  RelationMapUpdateMap(r2, relfilenode1, relform2->relisshared, false);
1106 
1107  /* Pass OIDs of mapped r2 tables back to caller */
1108  *mapped_tables++ = r2;
1109  }
1110 
1111  /*
1112  * Recognize that rel1's relfilenode (swapped from rel2) is new in this
1113  * subtransaction. The rel2 storage (swapped from rel1) may or may not be
1114  * new.
1115  */
1116  {
1117  Relation rel1,
1118  rel2;
1119 
1120  rel1 = relation_open(r1, NoLock);
1121  rel2 = relation_open(r2, NoLock);
1122  rel2->rd_createSubid = rel1->rd_createSubid;
1126  relation_close(rel1, NoLock);
1127  relation_close(rel2, NoLock);
1128  }
1129 
1130  /*
1131  * In the case of a shared catalog, these next few steps will only affect
1132  * our own database's pg_class row; but that's okay, because they are all
1133  * noncritical updates. That's also an important fact for the case of a
1134  * mapped catalog, because it's possible that we'll commit the map change
1135  * and then fail to commit the pg_class update.
1136  */
1137 
1138  /* set rel1's frozen Xid and minimum MultiXid */
1139  if (relform1->relkind != RELKIND_INDEX)
1140  {
1141  Assert(!TransactionIdIsValid(frozenXid) ||
1142  TransactionIdIsNormal(frozenXid));
1143  relform1->relfrozenxid = frozenXid;
1144  relform1->relminmxid = cutoffMulti;
1145  }
1146 
1147  /* swap size statistics too, since new rel has freshly-updated stats */
1148  {
1149  int32 swap_pages;
1150  float4 swap_tuples;
1151  int32 swap_allvisible;
1152 
1153  swap_pages = relform1->relpages;
1154  relform1->relpages = relform2->relpages;
1155  relform2->relpages = swap_pages;
1156 
1157  swap_tuples = relform1->reltuples;
1158  relform1->reltuples = relform2->reltuples;
1159  relform2->reltuples = swap_tuples;
1160 
1161  swap_allvisible = relform1->relallvisible;
1162  relform1->relallvisible = relform2->relallvisible;
1163  relform2->relallvisible = swap_allvisible;
1164  }
1165 
1166  /*
1167  * Update the tuples in pg_class --- unless the target relation of the
1168  * swap is pg_class itself. In that case, there is zero point in making
1169  * changes because we'd be updating the old data that we're about to throw
1170  * away. Because the real work being done here for a mapped relation is
1171  * just to change the relation map settings, it's all right to not update
1172  * the pg_class rows in this case. The most important changes will instead
1173  * performed later, in finish_heap_swap() itself.
1174  */
1175  if (!target_is_pg_class)
1176  {
1177  CatalogIndexState indstate;
1178 
1179  indstate = CatalogOpenIndexes(relRelation);
1180  CatalogTupleUpdateWithInfo(relRelation, &reltup1->t_self, reltup1,
1181  indstate);
1182  CatalogTupleUpdateWithInfo(relRelation, &reltup2->t_self, reltup2,
1183  indstate);
1184  CatalogCloseIndexes(indstate);
1185  }
1186  else
1187  {
1188  /* no update ... but we do still need relcache inval */
1191  }
1192 
1193  /*
1194  * Post alter hook for modified relations. The change to r2 is always
1195  * internal, but r1 depends on the invocation context.
1196  */
1197  InvokeObjectPostAlterHookArg(RelationRelationId, r1, 0,
1198  InvalidOid, is_internal);
1199  InvokeObjectPostAlterHookArg(RelationRelationId, r2, 0,
1200  InvalidOid, true);
1201 
1202  /*
1203  * If we have toast tables associated with the relations being swapped,
1204  * deal with them too.
1205  */
1206  if (relform1->reltoastrelid || relform2->reltoastrelid)
1207  {
1208  if (swap_toast_by_content)
1209  {
1210  if (relform1->reltoastrelid && relform2->reltoastrelid)
1211  {
1212  /* Recursively swap the contents of the toast tables */
1213  swap_relation_files(relform1->reltoastrelid,
1214  relform2->reltoastrelid,
1215  target_is_pg_class,
1216  swap_toast_by_content,
1217  is_internal,
1218  frozenXid,
1219  cutoffMulti,
1220  mapped_tables);
1221  }
1222  else
1223  {
1224  /* caller messed up */
1225  elog(ERROR, "cannot swap toast files by content when there's only one");
1226  }
1227  }
1228  else
1229  {
1230  /*
1231  * We swapped the ownership links, so we need to change dependency
1232  * data to match.
1233  *
1234  * NOTE: it is possible that only one table has a toast table.
1235  *
1236  * NOTE: at present, a TOAST table's only dependency is the one on
1237  * its owning table. If more are ever created, we'd need to use
1238  * something more selective than deleteDependencyRecordsFor() to
1239  * get rid of just the link we want.
1240  */
1241  ObjectAddress baseobject,
1242  toastobject;
1243  long count;
1244 
1245  /*
1246  * We disallow this case for system catalogs, to avoid the
1247  * possibility that the catalog we're rebuilding is one of the
1248  * ones the dependency changes would change. It's too late to be
1249  * making any data changes to the target catalog.
1250  */
1251  if (IsSystemClass(r1, relform1))
1252  elog(ERROR, "cannot swap toast files by links for system catalogs");
1253 
1254  /* Delete old dependencies */
1255  if (relform1->reltoastrelid)
1256  {
1257  count = deleteDependencyRecordsFor(RelationRelationId,
1258  relform1->reltoastrelid,
1259  false);
1260  if (count != 1)
1261  elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1262  count);
1263  }
1264  if (relform2->reltoastrelid)
1265  {
1266  count = deleteDependencyRecordsFor(RelationRelationId,
1267  relform2->reltoastrelid,
1268  false);
1269  if (count != 1)
1270  elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1271  count);
1272  }
1273 
1274  /* Register new dependencies */
1275  baseobject.classId = RelationRelationId;
1276  baseobject.objectSubId = 0;
1277  toastobject.classId = RelationRelationId;
1278  toastobject.objectSubId = 0;
1279 
1280  if (relform1->reltoastrelid)
1281  {
1282  baseobject.objectId = r1;
1283  toastobject.objectId = relform1->reltoastrelid;
1284  recordDependencyOn(&toastobject, &baseobject,
1286  }
1287 
1288  if (relform2->reltoastrelid)
1289  {
1290  baseobject.objectId = r2;
1291  toastobject.objectId = relform2->reltoastrelid;
1292  recordDependencyOn(&toastobject, &baseobject,
1294  }
1295  }
1296  }
1297 
1298  /*
1299  * If we're swapping two toast tables by content, do the same for their
1300  * valid index. The swap can actually be safely done only if the relations
1301  * have indexes.
1302  */
1303  if (swap_toast_by_content &&
1304  relform1->relkind == RELKIND_TOASTVALUE &&
1305  relform2->relkind == RELKIND_TOASTVALUE)
1306  {
1307  Oid toastIndex1,
1308  toastIndex2;
1309 
1310  /* Get valid index for each relation */
1311  toastIndex1 = toast_get_valid_index(r1,
1313  toastIndex2 = toast_get_valid_index(r2,
1315 
1316  swap_relation_files(toastIndex1,
1317  toastIndex2,
1318  target_is_pg_class,
1319  swap_toast_by_content,
1320  is_internal,
1323  mapped_tables);
1324  }
1325 
1326  /* Clean up. */
1327  heap_freetuple(reltup1);
1328  heap_freetuple(reltup2);
1329 
1330  table_close(relRelation, RowExclusiveLock);
1331 
1332  /*
1333  * Close both relcache entries' smgr links. We need this kluge because
1334  * both links will be invalidated during upcoming CommandCounterIncrement.
1335  * Whichever of the rels is the second to be cleared will have a dangling
1336  * reference to the other's smgr entry. Rather than trying to avoid this
1337  * by ordering operations just so, it's easiest to close the links first.
1338  * (Fortunately, since one of the entries is local in our transaction,
1339  * it's sufficient to clear out our own relcache this way; the problem
1340  * cannot arise for other backends when they see our update on the
1341  * non-transient relation.)
1342  *
1343  * Caution: the placement of this step interacts with the decision to
1344  * handle toast rels by recursion. When we are trying to rebuild pg_class
1345  * itself, the smgr close on pg_class must happen after all accesses in
1346  * this function.
1347  */
1350 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:236
SubTransactionId rd_newRelfilenodeSubid
Definition: rel.h:103
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:178
signed int int32
Definition: c.h:429
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:86
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
ItemPointerData t_self
Definition: htup.h:65
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
#define InvalidTransactionId
Definition: transam.h:31
void CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, CatalogIndexState indstate)
Definition: indexing.c:324
float float4
Definition: c.h:564
SubTransactionId rd_createSubid
Definition: rel.h:102
void RelationAssumeNewRelfilenode(Relation relation)
Definition: relcache.c:3749
SubTransactionId rd_firstRelfilenodeSubid
Definition: rel.h:105
Oid RelationMapOidToFilenode(Oid relationId, bool shared)
Definition: relmapper.c:159
#define InvalidMultiXactId
Definition: multixact.h:24
static void swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, bool swap_toast_by_content, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, Oid *mapped_tables)
Definition: cluster.c:992
#define InvalidOid
Definition: postgres_ext.h:36
void RelationCloseSmgrByOid(Oid relationId)
Definition: relcache.c:2943
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define Assert(condition)
Definition: c.h:804
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition: indexing.c:43
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:175
#define AccessExclusiveLock
Definition: lockdefs.h:45
#define elog(elevel,...)
Definition: elog.h:232
#define NameStr(name)
Definition: c.h:681
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition: indexing.c:61
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1315
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
void RelationMapUpdateMap(Oid relationId, Oid fileNode, bool shared, bool immediate)
Definition: relmapper.c:261
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)