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/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 (ClusterStmt *stmt, bool isTopLevel)
 
void cluster_rel (Oid tableOid, Oid indexOid, int options)
 
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, 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 444 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().

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

◆ cluster()

void cluster ( ClusterStmt stmt,
bool  isTopLevel 
)

Definition at line 102 of file cluster.c.

References AccessExclusiveLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CLUOPT_RECHECK, cluster_rel(), CommitTransactionCommand(), elog, ereport, errcode(), errmsg(), ERROR, get_relname_relid(), get_tables_to_cluster(), GETSTRUCT, GetTransactionSnapshot(), HeapTupleIsValid, ClusterStmt::indexname, RelToCluster::indexOid, INDEXRELID, InvalidOid, lfirst, lfirst_oid, MemoryContextDelete(), NoLock, ObjectIdGetDatum, OidIsValid, ClusterStmt::options, PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), PushActiveSnapshot(), RangeVarCallbackOwnsTable(), RangeVarGetRelidExtended(), RelationData::rd_rel, ClusterStmt::relation, RELATION_IS_OTHER_TEMP, RelationGetIndexList(), ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), StartTransactionCommand(), table_close(), table_open(), and RelToCluster::tableOid.

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

103 {
104  if (stmt->relation != NULL)
105  {
106  /* This is the single-relation case. */
107  Oid tableOid,
108  indexOid = InvalidOid;
109  Relation rel;
110 
111  /* Find, lock, and check permissions on the table */
112  tableOid = RangeVarGetRelidExtended(stmt->relation,
114  0,
116  rel = table_open(tableOid, NoLock);
117 
118  /*
119  * Reject clustering a remote temp table ... their local buffer
120  * manager is not going to cope.
121  */
122  if (RELATION_IS_OTHER_TEMP(rel))
123  ereport(ERROR,
124  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
125  errmsg("cannot cluster temporary tables of other sessions")));
126 
127  /*
128  * Reject clustering a partitioned table.
129  */
130  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
131  ereport(ERROR,
132  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
133  errmsg("cannot cluster a partitioned table")));
134 
135  if (stmt->indexname == NULL)
136  {
137  ListCell *index;
138 
139  /* We need to find the index that has indisclustered set. */
140  foreach(index, RelationGetIndexList(rel))
141  {
142  HeapTuple idxtuple;
143  Form_pg_index indexForm;
144 
145  indexOid = lfirst_oid(index);
146  idxtuple = SearchSysCache1(INDEXRELID,
147  ObjectIdGetDatum(indexOid));
148  if (!HeapTupleIsValid(idxtuple))
149  elog(ERROR, "cache lookup failed for index %u", indexOid);
150  indexForm = (Form_pg_index) GETSTRUCT(idxtuple);
151  if (indexForm->indisclustered)
152  {
153  ReleaseSysCache(idxtuple);
154  break;
155  }
156  ReleaseSysCache(idxtuple);
157  indexOid = InvalidOid;
158  }
159 
160  if (!OidIsValid(indexOid))
161  ereport(ERROR,
162  (errcode(ERRCODE_UNDEFINED_OBJECT),
163  errmsg("there is no previously clustered index for table \"%s\"",
164  stmt->relation->relname)));
165  }
166  else
167  {
168  /*
169  * The index is expected to be in the same namespace as the
170  * relation.
171  */
172  indexOid = get_relname_relid(stmt->indexname,
173  rel->rd_rel->relnamespace);
174  if (!OidIsValid(indexOid))
175  ereport(ERROR,
176  (errcode(ERRCODE_UNDEFINED_OBJECT),
177  errmsg("index \"%s\" for table \"%s\" does not exist",
178  stmt->indexname, stmt->relation->relname)));
179  }
180 
181  /* close relation, keep lock till commit */
182  table_close(rel, NoLock);
183 
184  /* Do the job. */
185  cluster_rel(tableOid, indexOid, stmt->options);
186  }
187  else
188  {
189  /*
190  * This is the "multi relation" case. We need to cluster all tables
191  * that have some index with indisclustered set.
192  */
193  MemoryContext cluster_context;
194  List *rvs;
195  ListCell *rv;
196 
197  /*
198  * We cannot run this form of CLUSTER inside a user transaction block;
199  * we'd be holding locks way too long.
200  */
201  PreventInTransactionBlock(isTopLevel, "CLUSTER");
202 
203  /*
204  * Create special memory context for cross-transaction storage.
205  *
206  * Since it is a child of PortalContext, it will go away even in case
207  * of error.
208  */
209  cluster_context = AllocSetContextCreate(PortalContext,
210  "Cluster",
212 
213  /*
214  * Build the list of relations to cluster. Note that this lives in
215  * cluster_context.
216  */
217  rvs = get_tables_to_cluster(cluster_context);
218 
219  /* Commit to get out of starting transaction */
222 
223  /* Ok, now that we've got them all, cluster them one by one */
224  foreach(rv, rvs)
225  {
226  RelToCluster *rvtc = (RelToCluster *) lfirst(rv);
227 
228  /* Start a new transaction for each relation. */
230  /* functions in indexes may want a snapshot set */
232  /* Do the job. */
233  cluster_rel(rvtc->tableOid, rvtc->indexOid,
234  stmt->options | CLUOPT_RECHECK);
237  }
238 
239  /* Start a new transaction for the cleanup work. */
241 
242  /* Clean up working storage */
243  MemoryContextDelete(cluster_context);
244  }
245 }
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:14785
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:170
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
void CommitTransactionCommand(void)
Definition: xact.c:2898
int errcode(int sqlerrcode)
Definition: elog.c:608
void PopActiveSnapshot(void)
Definition: snapmgr.c:814
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:306
#define OidIsValid(objectId)
Definition: c.h:645
void cluster_rel(Oid tableOid, Oid indexOid, int options)
Definition: cluster.c:265
Oid tableOid
Definition: cluster.c:65
MemoryContext PortalContext
Definition: mcxt.c:53
Definition: type.h:89
char * relname
Definition: primnodes.h:68
char * indexname
Definition: parsenodes.h:3175
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1687
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define NoLock
Definition: lockdefs.h:34
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:735
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3331
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:228
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1529
#define ereport(elevel, rest)
Definition: elog.h:141
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:552
void StartTransactionCommand(void)
Definition: xact.c:2797
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4347
Oid indexOid
Definition: cluster.c:66
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:822
#define elog(elevel,...)
Definition: elog.h:228
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
RangeVar * relation
Definition: parsenodes.h:3174
Definition: pg_list.h:50
#define lfirst_oid(lc)
Definition: pg_list.h:192

◆ cluster_rel()

void cluster_rel ( Oid  tableOid,
Oid  indexOid,
int  options 
)

Definition at line 265 of file cluster.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), CLUOPT_RECHECK, CLUOPT_VERBOSE, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, GetUserId(), HeapTupleIsValid, INDEXRELID, ObjectIdGetDatum, OidIsValid, 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, ReleaseSysCache(), RELOID, SearchSysCache1(), SearchSysCacheExists1, TransferPredicateLocksToHeapRelation(), try_relation_open(), and verbose.

Referenced by cluster(), and vacuum_rel().

266 {
267  Relation OldHeap;
268  bool verbose = ((options & CLUOPT_VERBOSE) != 0);
269  bool recheck = ((options & CLUOPT_RECHECK) != 0);
270 
271  /* Check for user-requested abort. */
273 
275  if (OidIsValid(indexOid))
278  else
281 
282  /*
283  * We grab exclusive access to the target rel and index for the duration
284  * of the transaction. (This is redundant for the single-transaction
285  * case, since cluster() already did it.) The index lock is taken inside
286  * check_index_is_clusterable.
287  */
288  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
289 
290  /* If the table has gone away, we can skip processing it */
291  if (!OldHeap)
292  {
294  return;
295  }
296 
297  /*
298  * Since we may open a new transaction for each relation, we have to check
299  * that the relation still is what we think it is.
300  *
301  * If this is a single-transaction CLUSTER, we can skip these tests. We
302  * *must* skip the one on indisclustered since it would reject an attempt
303  * to cluster a not-previously-clustered index.
304  */
305  if (recheck)
306  {
307  HeapTuple tuple;
308  Form_pg_index indexForm;
309 
310  /* Check that the user still owns the relation */
311  if (!pg_class_ownercheck(tableOid, GetUserId()))
312  {
315  return;
316  }
317 
318  /*
319  * Silently skip a temp table for a remote session. Only doing this
320  * check in the "recheck" case is appropriate (which currently means
321  * somebody is executing a database-wide CLUSTER), because there is
322  * another check in cluster() which will stop any attempt to cluster
323  * remote temp tables by name. There is another check in cluster_rel
324  * which is redundant, but we leave it for extra safety.
325  */
326  if (RELATION_IS_OTHER_TEMP(OldHeap))
327  {
330  return;
331  }
332 
333  if (OidIsValid(indexOid))
334  {
335  /*
336  * Check that the index still exists
337  */
339  {
342  return;
343  }
344 
345  /*
346  * Check that the index is still the one with indisclustered set.
347  */
348  tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
349  if (!HeapTupleIsValid(tuple)) /* probably can't happen */
350  {
353  return;
354  }
355  indexForm = (Form_pg_index) GETSTRUCT(tuple);
356  if (!indexForm->indisclustered)
357  {
358  ReleaseSysCache(tuple);
361  return;
362  }
363  ReleaseSysCache(tuple);
364  }
365  }
366 
367  /*
368  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
369  * would work in most respects, but the index would only get marked as
370  * indisclustered in the current database, leading to unexpected behavior
371  * if CLUSTER were later invoked in another database.
372  */
373  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
374  ereport(ERROR,
375  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
376  errmsg("cannot cluster a shared catalog")));
377 
378  /*
379  * Don't process temp tables of other backends ... their local buffer
380  * manager is not going to cope.
381  */
382  if (RELATION_IS_OTHER_TEMP(OldHeap))
383  {
384  if (OidIsValid(indexOid))
385  ereport(ERROR,
386  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
387  errmsg("cannot cluster temporary tables of other sessions")));
388  else
389  ereport(ERROR,
390  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
391  errmsg("cannot vacuum temporary tables of other sessions")));
392  }
393 
394  /*
395  * Also check for active uses of the relation in the current transaction,
396  * including open scans and pending AFTER trigger events.
397  */
398  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
399 
400  /* Check heap and index are valid to cluster on */
401  if (OidIsValid(indexOid))
402  check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
403 
404  /*
405  * Quietly ignore the request if this is a materialized view which has not
406  * been populated from its query. No harm is done because there is no data
407  * to deal with, and we don't want to throw an error if this is part of a
408  * multi-relation request -- for example, CLUSTER was run on the entire
409  * database.
410  */
411  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
412  !RelationIsPopulated(OldHeap))
413  {
416  return;
417  }
418 
419  /*
420  * All predicate locks on the tuples or pages are about to be made
421  * invalid, because we move tuples around. Promote them to relation
422  * locks. Predicate locks on indexes will be promoted when they are
423  * reindexed.
424  */
426 
427  /* rebuild_relation does all the dirty work */
428  rebuild_relation(OldHeap, indexOid, verbose);
429 
430  /* NB: rebuild_relation does table_close() on OldHeap */
431 
433 }
#define RelationIsPopulated(relation)
Definition: rel.h:571
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
Oid GetUserId(void)
Definition: miscinit.c:380
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
Definition: pgstat.c:3197
void pgstat_progress_update_param(int index, int64 val)
Definition: pgstat.c:3218
int errcode(int sqlerrcode)
Definition: elog.c:608
Form_pg_class rd_rel
Definition: rel.h:83
#define OidIsValid(objectId)
Definition: c.h:645
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:183
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:587
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:58
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:57
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3423
#define ereport(elevel, rest)
Definition: elog.h:141
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:38
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3081
static int verbose
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
void pgstat_progress_end_command(void)
Definition: pgstat.c:3269
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:552
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4753
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:822
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
Definition: cluster.c:444
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99

◆ 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 766 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(), MultiXactCutoff, MultiXactIdIsValid, MultiXactIdPrecedes(), NoLock, ObjectIdGetDatum, OidIsValid, OldestXmin, 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().

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

◆ 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 1340 of file cluster.c.

References AccessShareLock, 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().

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

◆ get_tables_to_cluster()

static List * get_tables_to_cluster ( MemoryContext  cluster_context)
static

Definition at line 1529 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().

1530 {
1531  Relation indRelation;
1532  TableScanDesc scan;
1533  ScanKeyData entry;
1534  HeapTuple indexTuple;
1536  MemoryContext old_context;
1537  RelToCluster *rvtc;
1538  List *rvs = NIL;
1539 
1540  /*
1541  * Get all indexes that have indisclustered set and are owned by
1542  * appropriate user. System relations or nailed-in relations cannot ever
1543  * have indisclustered set, because CLUSTER will refuse to set it when
1544  * called with one of them as argument.
1545  */
1546  indRelation = table_open(IndexRelationId, AccessShareLock);
1547  ScanKeyInit(&entry,
1548  Anum_pg_index_indisclustered,
1549  BTEqualStrategyNumber, F_BOOLEQ,
1550  BoolGetDatum(true));
1551  scan = table_beginscan_catalog(indRelation, 1, &entry);
1552  while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1553  {
1554  index = (Form_pg_index) GETSTRUCT(indexTuple);
1555 
1556  if (!pg_class_ownercheck(index->indrelid, GetUserId()))
1557  continue;
1558 
1559  /*
1560  * We have to build the list in a different memory context so it will
1561  * survive the cross-transaction processing
1562  */
1563  old_context = MemoryContextSwitchTo(cluster_context);
1564 
1565  rvtc = (RelToCluster *) palloc(sizeof(RelToCluster));
1566  rvtc->tableOid = index->indrelid;
1567  rvtc->indexOid = index->indexrelid;
1568  rvs = lappend(rvs, rvtc);
1569 
1570  MemoryContextSwitchTo(old_context);
1571  }
1572  table_endscan(scan);
1573 
1574  relation_close(indRelation, AccessShareLock);
1575 
1576  return rvs;
1577 }
#define NIL
Definition: pg_list.h:65
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:98
Oid GetUserId(void)
Definition: miscinit.c:380
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
#define AccessShareLock
Definition: lockdefs.h:36
Oid tableOid
Definition: cluster.c:65
Definition: type.h:89
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1290
List * lappend(List *list, void *datum)
Definition: list.c:322
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
#define BoolGetDatum(X)
Definition: postgres.h:402
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4753
Oid indexOid
Definition: cluster.c:66
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:831
void * palloc(Size size)
Definition: mcxt.c:949
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,
char  relpersistence,
LOCKMODE  lockmode 
)

Definition at line 640 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().

642 {
643  TupleDesc OldHeapDesc;
644  char NewHeapName[NAMEDATALEN];
645  Oid OIDNewHeap;
646  Oid toastid;
647  Relation OldHeap;
648  HeapTuple tuple;
649  Datum reloptions;
650  bool isNull;
651  Oid namespaceid;
652 
653  OldHeap = table_open(OIDOldHeap, lockmode);
654  OldHeapDesc = RelationGetDescr(OldHeap);
655 
656  /*
657  * Note that the NewHeap will not receive any of the defaults or
658  * constraints associated with the OldHeap; we don't need 'em, and there's
659  * no reason to spend cycles inserting them into the catalogs only to
660  * delete them.
661  */
662 
663  /*
664  * But we do want to use reloptions of the old heap for new heap.
665  */
666  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
667  if (!HeapTupleIsValid(tuple))
668  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
669  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
670  &isNull);
671  if (isNull)
672  reloptions = (Datum) 0;
673 
674  if (relpersistence == RELPERSISTENCE_TEMP)
675  namespaceid = LookupCreationNamespace("pg_temp");
676  else
677  namespaceid = RelationGetNamespace(OldHeap);
678 
679  /*
680  * Create the new heap, using a temporary name in the same namespace as
681  * the existing table. NOTE: there is some risk of collision with user
682  * relnames. Working around this seems more trouble than it's worth; in
683  * particular, we can't create the new heap in a different namespace from
684  * the old, or we will have problems with the TEMP status of temp tables.
685  *
686  * Note: the new heap is not a shared relation, even if we are rebuilding
687  * a shared rel. However, we do make the new heap mapped if the source is
688  * mapped. This simplifies swap_relation_files, and is absolutely
689  * necessary for rebuilding pg_class, for reasons explained there.
690  */
691  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
692 
693  OIDNewHeap = heap_create_with_catalog(NewHeapName,
694  namespaceid,
695  NewTableSpace,
696  InvalidOid,
697  InvalidOid,
698  InvalidOid,
699  OldHeap->rd_rel->relowner,
700  OldHeap->rd_rel->relam,
701  OldHeapDesc,
702  NIL,
703  RELKIND_RELATION,
705  false,
706  RelationIsMapped(OldHeap),
708  reloptions,
709  false,
710  true,
711  true,
712  OIDOldHeap,
713  NULL);
714  Assert(OIDNewHeap != InvalidOid);
715 
716  ReleaseSysCache(tuple);
717 
718  /*
719  * Advance command counter so that the newly-created relation's catalog
720  * tuples will be visible to table_open.
721  */
723 
724  /*
725  * If necessary, create a TOAST table for the new relation.
726  *
727  * If the relation doesn't have a TOAST table already, we can't need one
728  * for the new relation. The other way around is possible though: if some
729  * wide columns have been dropped, NewHeapCreateToastTable can decide that
730  * no TOAST table is needed for the new table.
731  *
732  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
733  * that the TOAST table will be visible for insertion.
734  */
735  toastid = OldHeap->rd_rel->reltoastrelid;
736  if (OidIsValid(toastid))
737  {
738  /* keep the existing toast table's reloptions, if any */
739  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
740  if (!HeapTupleIsValid(tuple))
741  elog(ERROR, "cache lookup failed for relation %u", toastid);
742  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
743  &isNull);
744  if (isNull)
745  reloptions = (Datum) 0;
746 
747  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode);
748 
749  ReleaseSysCache(tuple);
750  }
751 
752  table_close(OldHeap, NoLock);
753 
754  return OIDNewHeap;
755 }
#define NIL
Definition: pg_list.h:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define RelationGetDescr(relation)
Definition: rel.h:448
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2928
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
char relpersistence
Definition: pg_class.h:78
#define NoLock
Definition: lockdefs.h:34
#define RelationIsMapped(relation)
Definition: rel.h:471
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1116
uintptr_t Datum
Definition: postgres.h:367
void CommandCounterIncrement(void)
Definition: xact.c:1005
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1164
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1377
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:739
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:1065
#define elog(elevel,...)
Definition: elog.h:228
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define snprintf
Definition: port.h:192
#define RelationGetNamespace(relation)
Definition: rel.h:463
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:66

◆ mark_index_clustered()

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

Definition at line 504 of file cluster.c.

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

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

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

◆ rebuild_relation()

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

Definition at line 587 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, relpersistence, and table_close().

Referenced by cluster_rel().

588 {
589  Oid tableOid = RelationGetRelid(OldHeap);
590  Oid tableSpace = OldHeap->rd_rel->reltablespace;
591  Oid OIDNewHeap;
592  char relpersistence;
593  bool is_system_catalog;
594  bool swap_toast_by_content;
595  TransactionId frozenXid;
596  MultiXactId cutoffMulti;
597 
598  /* Mark the correct index as clustered */
599  if (OidIsValid(indexOid))
600  mark_index_clustered(OldHeap, indexOid, true);
601 
602  /* Remember info about rel before closing OldHeap */
603  relpersistence = OldHeap->rd_rel->relpersistence;
604  is_system_catalog = IsSystemRelation(OldHeap);
605 
606  /* Close relcache entry, but keep lock until transaction commit */
607  table_close(OldHeap, NoLock);
608 
609  /* Create the transient table that will receive the re-ordered data */
610  OIDNewHeap = make_new_heap(tableOid, tableSpace,
611  relpersistence,
613 
614  /* Copy the heap data into the new table in the desired order */
615  copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
616  &swap_toast_by_content, &frozenXid, &cutoffMulti);
617 
618  /*
619  * Swap the physical files of the target and transient tables, then
620  * rebuild the target's indexes and throw away the transient table.
621  */
622  finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog,
623  swap_toast_by_content, false, true,
624  frozenXid, cutoffMulti,
625  relpersistence);
626 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
bool IsSystemRelation(Relation relation)
Definition: catalog.c:69
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:1340
uint32 TransactionId
Definition: c.h:514
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:645
char relpersistence
Definition: pg_class.h:78
#define NoLock
Definition: lockdefs.h:34
static int verbose
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:504
TransactionId MultiXactId
Definition: c.h:524
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
Definition: cluster.c:766
#define AccessExclusiveLock
Definition: lockdefs.h:45
#define RelationGetRelid(relation)
Definition: rel.h:422
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence, LOCKMODE lockmode)
Definition: cluster.c:640

◆ 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 1001 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, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidIsValid, recordDependencyOn(), 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().

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