PostgreSQL Source Code  git master
cluster.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * cluster.c
4  * CLUSTER a table on an index. This is now also used for VACUUM FULL.
5  *
6  * There is hardly anything left of Paul Brown's original implementation...
7  *
8  *
9  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994-5, Regents of the University of California
11  *
12  *
13  * IDENTIFICATION
14  * src/backend/commands/cluster.c
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19 
20 #include "access/amapi.h"
21 #include "access/heapam.h"
22 #include "access/multixact.h"
23 #include "access/relscan.h"
24 #include "access/tableam.h"
25 #include "access/toast_internals.h"
26 #include "access/transam.h"
27 #include "access/xact.h"
28 #include "access/xlog.h"
29 #include "catalog/catalog.h"
30 #include "catalog/dependency.h"
31 #include "catalog/heap.h"
32 #include "catalog/index.h"
33 #include "catalog/namespace.h"
34 #include "catalog/objectaccess.h"
35 #include "catalog/partition.h"
36 #include "catalog/pg_am.h"
37 #include "catalog/pg_database.h"
38 #include "catalog/pg_inherits.h"
39 #include "catalog/toasting.h"
40 #include "commands/cluster.h"
41 #include "commands/defrem.h"
42 #include "commands/progress.h"
43 #include "commands/tablecmds.h"
44 #include "commands/vacuum.h"
45 #include "miscadmin.h"
46 #include "optimizer/optimizer.h"
47 #include "pgstat.h"
48 #include "storage/bufmgr.h"
49 #include "storage/lmgr.h"
50 #include "storage/predicate.h"
51 #include "utils/acl.h"
52 #include "utils/fmgroids.h"
53 #include "utils/guc.h"
54 #include "utils/inval.h"
55 #include "utils/lsyscache.h"
56 #include "utils/memutils.h"
57 #include "utils/pg_rusage.h"
58 #include "utils/relmapper.h"
59 #include "utils/snapmgr.h"
60 #include "utils/syscache.h"
61 #include "utils/tuplesort.h"
62 
63 /*
64  * This struct is used to pass around the information on tables to be
65  * clustered. We need this so we can make a list of them when invoked without
66  * a specific table/index pair.
67  */
68 typedef struct
69 {
72 } RelToCluster;
73 
74 
75 static void cluster_multiple_rels(List *rtcs, ClusterParams *params);
76 static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose);
77 static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
78  bool verbose, bool *pSwapToastByContent,
79  TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
80 static List *get_tables_to_cluster(MemoryContext cluster_context);
82  Oid indexOid);
83 
84 
85 /*---------------------------------------------------------------------------
86  * This cluster code allows for clustering multiple tables at once. Because
87  * of this, we cannot just run everything on a single transaction, or we
88  * would be forced to acquire exclusive locks on all the tables being
89  * clustered, simultaneously --- very likely leading to deadlock.
90  *
91  * To solve this we follow a similar strategy to VACUUM code,
92  * clustering each relation in a separate transaction. For this to work,
93  * we need to:
94  * - provide a separate memory context so that we can pass information in
95  * a way that survives across transactions
96  * - start a new transaction every time a new relation is clustered
97  * - check for validity of the information on to-be-clustered relations,
98  * as someone might have deleted a relation behind our back, or
99  * clustered one on a different index
100  * - end the transaction
101  *
102  * The single-relation case does not have any such overhead.
103  *
104  * We also allow a relation to be specified without index. In that case,
105  * the indisclustered bit will be looked up, and an ERROR will be thrown
106  * if there is no index with the bit set.
107  *---------------------------------------------------------------------------
108  */
109 void
110 cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
111 {
112  ListCell *lc;
113  ClusterParams params = {0};
114  bool verbose = false;
115  Relation rel = NULL;
116  Oid indexOid = InvalidOid;
117  MemoryContext cluster_context;
118  List *rtcs;
119 
120  /* Parse option list */
121  foreach(lc, stmt->params)
122  {
123  DefElem *opt = (DefElem *) lfirst(lc);
124 
125  if (strcmp(opt->defname, "verbose") == 0)
126  verbose = defGetBoolean(opt);
127  else
128  ereport(ERROR,
129  (errcode(ERRCODE_SYNTAX_ERROR),
130  errmsg("unrecognized CLUSTER option \"%s\"",
131  opt->defname),
132  parser_errposition(pstate, opt->location)));
133  }
134 
135  params.options = (verbose ? CLUOPT_VERBOSE : 0);
136 
137  if (stmt->relation != NULL)
138  {
139  /* This is the single-relation case. */
140  Oid tableOid;
141 
142  /*
143  * Find, lock, and check permissions on the table. We obtain
144  * AccessExclusiveLock right away to avoid lock-upgrade hazard in the
145  * single-transaction case.
146  */
147  tableOid = RangeVarGetRelidExtended(stmt->relation,
149  0,
151  rel = table_open(tableOid, NoLock);
152 
153  /*
154  * Reject clustering a remote temp table ... their local buffer
155  * manager is not going to cope.
156  */
157  if (RELATION_IS_OTHER_TEMP(rel))
158  ereport(ERROR,
159  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
160  errmsg("cannot cluster temporary tables of other sessions")));
161 
162  if (stmt->indexname == NULL)
163  {
164  ListCell *index;
165 
166  /* We need to find the index that has indisclustered set. */
167  foreach(index, RelationGetIndexList(rel))
168  {
169  indexOid = lfirst_oid(index);
170  if (get_index_isclustered(indexOid))
171  break;
172  indexOid = InvalidOid;
173  }
174 
175  if (!OidIsValid(indexOid))
176  ereport(ERROR,
177  (errcode(ERRCODE_UNDEFINED_OBJECT),
178  errmsg("there is no previously clustered index for table \"%s\"",
179  stmt->relation->relname)));
180  }
181  else
182  {
183  /*
184  * The index is expected to be in the same namespace as the
185  * relation.
186  */
187  indexOid = get_relname_relid(stmt->indexname,
188  rel->rd_rel->relnamespace);
189  if (!OidIsValid(indexOid))
190  ereport(ERROR,
191  (errcode(ERRCODE_UNDEFINED_OBJECT),
192  errmsg("index \"%s\" for table \"%s\" does not exist",
193  stmt->indexname, stmt->relation->relname)));
194  }
195 
196  if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
197  {
198  /* close relation, keep lock till commit */
199  table_close(rel, NoLock);
200 
201  /* Do the job. */
202  cluster_rel(tableOid, indexOid, &params);
203 
204  return;
205  }
206  }
207 
208  /*
209  * By here, we know we are in a multi-table situation. In order to avoid
210  * holding locks for too long, we want to process each table in its own
211  * transaction. This forces us to disallow running inside a user
212  * transaction block.
213  */
214  PreventInTransactionBlock(isTopLevel, "CLUSTER");
215 
216  /* Also, we need a memory context to hold our list of relations */
217  cluster_context = AllocSetContextCreate(PortalContext,
218  "Cluster",
220 
221  /*
222  * Either we're processing a partitioned table, or we were not given any
223  * table name at all. In either case, obtain a list of relations to
224  * process.
225  *
226  * In the former case, an index name must have been given, so we don't
227  * need to recheck its "indisclustered" bit, but we have to check that it
228  * is an index that we can cluster on. In the latter case, we set the
229  * option bit to have indisclustered verified.
230  *
231  * Rechecking the relation itself is necessary here in all cases.
232  */
233  params.options |= CLUOPT_RECHECK;
234  if (rel != NULL)
235  {
236  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
238  rtcs = get_tables_to_cluster_partitioned(cluster_context, indexOid);
239 
240  /* close relation, releasing lock on parent table */
242  }
243  else
244  {
245  rtcs = get_tables_to_cluster(cluster_context);
247  }
248 
249  /* Do the job. */
250  cluster_multiple_rels(rtcs, &params);
251 
252  /* Start a new transaction for the cleanup work. */
254 
255  /* Clean up working storage */
256  MemoryContextDelete(cluster_context);
257 }
258 
259 /*
260  * Given a list of relations to cluster, process each of them in a separate
261  * transaction.
262  *
263  * We expect to be in a transaction at start, but there isn't one when we
264  * return.
265  */
266 static void
268 {
269  ListCell *lc;
270 
271  /* Commit to get out of starting transaction */
274 
275  /* Cluster the tables, each in a separate transaction */
276  foreach(lc, rtcs)
277  {
278  RelToCluster *rtc = (RelToCluster *) lfirst(lc);
279 
280  /* Start a new transaction for each relation. */
282 
283  /* functions in indexes may want a snapshot set */
285 
286  /* Do the job. */
287  cluster_rel(rtc->tableOid, rtc->indexOid, params);
288 
291  }
292 }
293 
294 /*
295  * cluster_rel
296  *
297  * This clusters the table by creating a new, clustered table and
298  * swapping the relfilenumbers of the new table and the old table, so
299  * the OID of the original table is preserved. Thus we do not lose
300  * GRANT, inheritance nor references to this table (this was a bug
301  * in releases through 7.3).
302  *
303  * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
304  * the new table, it's better to create the indexes afterwards than to fill
305  * them incrementally while we load the table.
306  *
307  * If indexOid is InvalidOid, the table will be rewritten in physical order
308  * instead of index order. This is the new implementation of VACUUM FULL,
309  * and error messages should refer to the operation as VACUUM not CLUSTER.
310  */
311 void
312 cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
313 {
314  Relation OldHeap;
315  Oid save_userid;
316  int save_sec_context;
317  int save_nestlevel;
318  bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
319  bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
320 
321  /* Check for user-requested abort. */
323 
325  if (OidIsValid(indexOid))
328  else
331 
332  /*
333  * We grab exclusive access to the target rel and index for the duration
334  * of the transaction. (This is redundant for the single-transaction
335  * case, since cluster() already did it.) The index lock is taken inside
336  * check_index_is_clusterable.
337  */
338  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
339 
340  /* If the table has gone away, we can skip processing it */
341  if (!OldHeap)
342  {
344  return;
345  }
346 
347  /*
348  * Switch to the table owner's userid, so that any index functions are run
349  * as that user. Also lock down security-restricted operations and
350  * arrange to make GUC variable changes local to this command.
351  */
352  GetUserIdAndSecContext(&save_userid, &save_sec_context);
353  SetUserIdAndSecContext(OldHeap->rd_rel->relowner,
354  save_sec_context | SECURITY_RESTRICTED_OPERATION);
355  save_nestlevel = NewGUCNestLevel();
356 
357  /*
358  * Since we may open a new transaction for each relation, we have to check
359  * that the relation still is what we think it is.
360  *
361  * If this is a single-transaction CLUSTER, we can skip these tests. We
362  * *must* skip the one on indisclustered since it would reject an attempt
363  * to cluster a not-previously-clustered index.
364  */
365  if (recheck)
366  {
367  /* Check that the user still owns the relation */
368  if (!object_ownercheck(RelationRelationId, tableOid, save_userid))
369  {
371  goto out;
372  }
373 
374  /*
375  * Silently skip a temp table for a remote session. Only doing this
376  * check in the "recheck" case is appropriate (which currently means
377  * somebody is executing a database-wide CLUSTER or on a partitioned
378  * table), because there is another check in cluster() which will stop
379  * any attempt to cluster remote temp tables by name. There is
380  * another check in cluster_rel which is redundant, but we leave it
381  * for extra safety.
382  */
383  if (RELATION_IS_OTHER_TEMP(OldHeap))
384  {
386  goto out;
387  }
388 
389  if (OidIsValid(indexOid))
390  {
391  /*
392  * Check that the index still exists
393  */
394  if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid)))
395  {
397  goto out;
398  }
399 
400  /*
401  * Check that the index is still the one with indisclustered set,
402  * if needed.
403  */
404  if ((params->options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
405  !get_index_isclustered(indexOid))
406  {
408  goto out;
409  }
410  }
411  }
412 
413  /*
414  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
415  * would work in most respects, but the index would only get marked as
416  * indisclustered in the current database, leading to unexpected behavior
417  * if CLUSTER were later invoked in another database.
418  */
419  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
420  ereport(ERROR,
421  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
422  errmsg("cannot cluster a shared catalog")));
423 
424  /*
425  * Don't process temp tables of other backends ... their local buffer
426  * manager is not going to cope.
427  */
428  if (RELATION_IS_OTHER_TEMP(OldHeap))
429  {
430  if (OidIsValid(indexOid))
431  ereport(ERROR,
432  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
433  errmsg("cannot cluster temporary tables of other sessions")));
434  else
435  ereport(ERROR,
436  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
437  errmsg("cannot vacuum temporary tables of other sessions")));
438  }
439 
440  /*
441  * Also check for active uses of the relation in the current transaction,
442  * including open scans and pending AFTER trigger events.
443  */
444  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
445 
446  /* Check heap and index are valid to cluster on */
447  if (OidIsValid(indexOid))
449 
450  /*
451  * Quietly ignore the request if this is a materialized view which has not
452  * been populated from its query. No harm is done because there is no data
453  * to deal with, and we don't want to throw an error if this is part of a
454  * multi-relation request -- for example, CLUSTER was run on the entire
455  * database.
456  */
457  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
458  !RelationIsPopulated(OldHeap))
459  {
461  goto out;
462  }
463 
464  Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
465  OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
466  OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
467 
468  /*
469  * All predicate locks on the tuples or pages are about to be made
470  * invalid, because we move tuples around. Promote them to relation
471  * locks. Predicate locks on indexes will be promoted when they are
472  * reindexed.
473  */
475 
476  /* rebuild_relation does all the dirty work */
477  rebuild_relation(OldHeap, indexOid, verbose);
478 
479  /* NB: rebuild_relation does table_close() on OldHeap */
480 
481 out:
482  /* Roll back any GUC changes executed by index functions */
483  AtEOXact_GUC(false, save_nestlevel);
484 
485  /* Restore userid and security context */
486  SetUserIdAndSecContext(save_userid, save_sec_context);
487 
489 }
490 
491 /*
492  * Verify that the specified heap and index are valid to cluster on
493  *
494  * Side effect: obtains lock on the index. The caller may
495  * in some cases already have AccessExclusiveLock on the table, but
496  * not in all cases so we can't rely on the table-level lock for
497  * protection here.
498  */
499 void
500 check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
501 {
502  Relation OldIndex;
503 
504  OldIndex = index_open(indexOid, lockmode);
505 
506  /*
507  * Check that index is in fact an index on the given relation
508  */
509  if (OldIndex->rd_index == NULL ||
510  OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
511  ereport(ERROR,
512  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
513  errmsg("\"%s\" is not an index for table \"%s\"",
514  RelationGetRelationName(OldIndex),
515  RelationGetRelationName(OldHeap))));
516 
517  /* Index AM must allow clustering */
518  if (!OldIndex->rd_indam->amclusterable)
519  ereport(ERROR,
520  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
521  errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
522  RelationGetRelationName(OldIndex))));
523 
524  /*
525  * Disallow clustering on incomplete indexes (those that might not index
526  * every row of the relation). We could relax this by making a separate
527  * seqscan pass over the table to copy the missing rows, but that seems
528  * expensive and tedious.
529  */
530  if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
531  ereport(ERROR,
532  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
533  errmsg("cannot cluster on partial index \"%s\"",
534  RelationGetRelationName(OldIndex))));
535 
536  /*
537  * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
538  * it might well not contain entries for every heap row, or might not even
539  * be internally consistent. (But note that we don't check indcheckxmin;
540  * the worst consequence of following broken HOT chains would be that we
541  * might put recently-dead tuples out-of-order in the new table, and there
542  * is little harm in that.)
543  */
544  if (!OldIndex->rd_index->indisvalid)
545  ereport(ERROR,
546  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
547  errmsg("cannot cluster on invalid index \"%s\"",
548  RelationGetRelationName(OldIndex))));
549 
550  /* Drop relcache refcnt on OldIndex, but keep lock */
551  index_close(OldIndex, NoLock);
552 }
553 
554 /*
555  * mark_index_clustered: mark the specified index as the one clustered on
556  *
557  * With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
558  */
559 void
560 mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
561 {
562  HeapTuple indexTuple;
563  Form_pg_index indexForm;
564  Relation pg_index;
565  ListCell *index;
566 
567  /* Disallow applying to a partitioned table */
568  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
569  ereport(ERROR,
570  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
571  errmsg("cannot mark index clustered in partitioned table")));
572 
573  /*
574  * If the index is already marked clustered, no need to do anything.
575  */
576  if (OidIsValid(indexOid))
577  {
578  if (get_index_isclustered(indexOid))
579  return;
580  }
581 
582  /*
583  * Check each index of the relation and set/clear the bit as needed.
584  */
585  pg_index = table_open(IndexRelationId, RowExclusiveLock);
586 
587  foreach(index, RelationGetIndexList(rel))
588  {
589  Oid thisIndexOid = lfirst_oid(index);
590 
591  indexTuple = SearchSysCacheCopy1(INDEXRELID,
592  ObjectIdGetDatum(thisIndexOid));
593  if (!HeapTupleIsValid(indexTuple))
594  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
595  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
596 
597  /*
598  * Unset the bit if set. We know it's wrong because we checked this
599  * earlier.
600  */
601  if (indexForm->indisclustered)
602  {
603  indexForm->indisclustered = false;
604  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
605  }
606  else if (thisIndexOid == indexOid)
607  {
608  /* this was checked earlier, but let's be real sure */
609  if (!indexForm->indisvalid)
610  elog(ERROR, "cannot cluster on invalid index %u", indexOid);
611  indexForm->indisclustered = true;
612  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
613  }
614 
615  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
616  InvalidOid, is_internal);
617 
618  heap_freetuple(indexTuple);
619  }
620 
621  table_close(pg_index, RowExclusiveLock);
622 }
623 
624 /*
625  * rebuild_relation: rebuild an existing relation in index or physical order
626  *
627  * OldHeap: table to rebuild --- must be opened and exclusive-locked!
628  * indexOid: index to cluster by, or InvalidOid to rewrite in physical order.
629  *
630  * NB: this routine closes OldHeap at the right time; caller should not.
631  */
632 static void
633 rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
634 {
635  Oid tableOid = RelationGetRelid(OldHeap);
636  Oid accessMethod = OldHeap->rd_rel->relam;
637  Oid tableSpace = OldHeap->rd_rel->reltablespace;
638  Oid OIDNewHeap;
639  char relpersistence;
640  bool is_system_catalog;
641  bool swap_toast_by_content;
642  TransactionId frozenXid;
643  MultiXactId cutoffMulti;
644 
645  if (OidIsValid(indexOid))
646  /* Mark the correct index as clustered */
647  mark_index_clustered(OldHeap, indexOid, true);
648 
649  /* Remember info about rel before closing OldHeap */
650  relpersistence = OldHeap->rd_rel->relpersistence;
651  is_system_catalog = IsSystemRelation(OldHeap);
652 
653  /* Close relcache entry, but keep lock until transaction commit */
654  table_close(OldHeap, NoLock);
655 
656  /* Create the transient table that will receive the re-ordered data */
657  OIDNewHeap = make_new_heap(tableOid, tableSpace,
658  accessMethod,
659  relpersistence,
661 
662  /* Copy the heap data into the new table in the desired order */
663  copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
664  &swap_toast_by_content, &frozenXid, &cutoffMulti);
665 
666  /*
667  * Swap the physical files of the target and transient tables, then
668  * rebuild the target's indexes and throw away the transient table.
669  */
670  finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog,
671  swap_toast_by_content, false, true,
672  frozenXid, cutoffMulti,
673  relpersistence);
674 }
675 
676 
677 /*
678  * Create the transient table that will be filled with new data during
679  * CLUSTER, ALTER TABLE, and similar operations. The transient table
680  * duplicates the logical structure of the OldHeap; but will have the
681  * specified physical storage properties NewTableSpace, NewAccessMethod, and
682  * relpersistence.
683  *
684  * After this, the caller should load the new heap with transferred/modified
685  * data, then call finish_heap_swap to complete the operation.
686  */
687 Oid
688 make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
689  char relpersistence, LOCKMODE lockmode)
690 {
691  TupleDesc OldHeapDesc;
692  char NewHeapName[NAMEDATALEN];
693  Oid OIDNewHeap;
694  Oid toastid;
695  Relation OldHeap;
696  HeapTuple tuple;
697  Datum reloptions;
698  bool isNull;
699  Oid namespaceid;
700 
701  OldHeap = table_open(OIDOldHeap, lockmode);
702  OldHeapDesc = RelationGetDescr(OldHeap);
703 
704  /*
705  * Note that the NewHeap will not receive any of the defaults or
706  * constraints associated with the OldHeap; we don't need 'em, and there's
707  * no reason to spend cycles inserting them into the catalogs only to
708  * delete them.
709  */
710 
711  /*
712  * But we do want to use reloptions of the old heap for new heap.
713  */
714  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
715  if (!HeapTupleIsValid(tuple))
716  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
717  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
718  &isNull);
719  if (isNull)
720  reloptions = (Datum) 0;
721 
722  if (relpersistence == RELPERSISTENCE_TEMP)
723  namespaceid = LookupCreationNamespace("pg_temp");
724  else
725  namespaceid = RelationGetNamespace(OldHeap);
726 
727  /*
728  * Create the new heap, using a temporary name in the same namespace as
729  * the existing table. NOTE: there is some risk of collision with user
730  * relnames. Working around this seems more trouble than it's worth; in
731  * particular, we can't create the new heap in a different namespace from
732  * the old, or we will have problems with the TEMP status of temp tables.
733  *
734  * Note: the new heap is not a shared relation, even if we are rebuilding
735  * a shared rel. However, we do make the new heap mapped if the source is
736  * mapped. This simplifies swap_relation_files, and is absolutely
737  * necessary for rebuilding pg_class, for reasons explained there.
738  */
739  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
740 
741  OIDNewHeap = heap_create_with_catalog(NewHeapName,
742  namespaceid,
743  NewTableSpace,
744  InvalidOid,
745  InvalidOid,
746  InvalidOid,
747  OldHeap->rd_rel->relowner,
748  NewAccessMethod,
749  OldHeapDesc,
750  NIL,
751  RELKIND_RELATION,
752  relpersistence,
753  false,
754  RelationIsMapped(OldHeap),
756  reloptions,
757  false,
758  true,
759  true,
760  OIDOldHeap,
761  NULL);
762  Assert(OIDNewHeap != InvalidOid);
763 
764  ReleaseSysCache(tuple);
765 
766  /*
767  * Advance command counter so that the newly-created relation's catalog
768  * tuples will be visible to table_open.
769  */
771 
772  /*
773  * If necessary, create a TOAST table for the new relation.
774  *
775  * If the relation doesn't have a TOAST table already, we can't need one
776  * for the new relation. The other way around is possible though: if some
777  * wide columns have been dropped, NewHeapCreateToastTable can decide that
778  * no TOAST table is needed for the new table.
779  *
780  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
781  * that the TOAST table will be visible for insertion.
782  */
783  toastid = OldHeap->rd_rel->reltoastrelid;
784  if (OidIsValid(toastid))
785  {
786  /* keep the existing toast table's reloptions, if any */
787  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
788  if (!HeapTupleIsValid(tuple))
789  elog(ERROR, "cache lookup failed for relation %u", toastid);
790  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
791  &isNull);
792  if (isNull)
793  reloptions = (Datum) 0;
794 
795  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid);
796 
797  ReleaseSysCache(tuple);
798  }
799 
800  table_close(OldHeap, NoLock);
801 
802  return OIDNewHeap;
803 }
804 
805 /*
806  * Do the physical copying of table data.
807  *
808  * There are three output parameters:
809  * *pSwapToastByContent is set true if toast tables must be swapped by content.
810  * *pFreezeXid receives the TransactionId used as freeze cutoff point.
811  * *pCutoffMulti receives the MultiXactId used as a cutoff point.
812  */
813 static void
814 copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
815  bool *pSwapToastByContent, TransactionId *pFreezeXid,
816  MultiXactId *pCutoffMulti)
817 {
818  Relation NewHeap,
819  OldHeap,
820  OldIndex;
821  Relation relRelation;
822  HeapTuple reltup;
823  Form_pg_class relform;
826  VacuumParams params;
827  struct VacuumCutoffs cutoffs;
828  bool use_sort;
829  double num_tuples = 0,
830  tups_vacuumed = 0,
831  tups_recently_dead = 0;
832  BlockNumber num_pages;
833  int elevel = verbose ? INFO : DEBUG2;
834  PGRUsage ru0;
835  char *nspname;
836 
837  pg_rusage_init(&ru0);
838 
839  /*
840  * Open the relations we need.
841  */
842  NewHeap = table_open(OIDNewHeap, AccessExclusiveLock);
843  OldHeap = table_open(OIDOldHeap, AccessExclusiveLock);
844  if (OidIsValid(OIDOldIndex))
845  OldIndex = index_open(OIDOldIndex, AccessExclusiveLock);
846  else
847  OldIndex = NULL;
848 
849  /* Store a copy of the namespace name for logging purposes */
850  nspname = get_namespace_name(RelationGetNamespace(OldHeap));
851 
852  /*
853  * Their tuple descriptors should be exactly alike, but here we only need
854  * assume that they have the same number of columns.
855  */
856  oldTupDesc = RelationGetDescr(OldHeap);
857  newTupDesc = RelationGetDescr(NewHeap);
858  Assert(newTupDesc->natts == oldTupDesc->natts);
859 
860  /*
861  * If the OldHeap has a toast table, get lock on the toast table to keep
862  * it from being vacuumed. This is needed because autovacuum processes
863  * toast tables independently of their main tables, with no lock on the
864  * latter. If an autovacuum were to start on the toast table after we
865  * compute our OldestXmin below, it would use a later OldestXmin, and then
866  * possibly remove as DEAD toast tuples belonging to main tuples we think
867  * are only RECENTLY_DEAD. Then we'd fail while trying to copy those
868  * tuples.
869  *
870  * We don't need to open the toast relation here, just lock it. The lock
871  * will be held till end of transaction.
872  */
873  if (OldHeap->rd_rel->reltoastrelid)
874  LockRelationOid(OldHeap->rd_rel->reltoastrelid, AccessExclusiveLock);
875 
876  /*
877  * If both tables have TOAST tables, perform toast swap by content. It is
878  * possible that the old table has a toast table but the new one doesn't,
879  * if toastable columns have been dropped. In that case we have to do
880  * swap by links. This is okay because swap by content is only essential
881  * for system catalogs, and we don't support schema changes for them.
882  */
883  if (OldHeap->rd_rel->reltoastrelid && NewHeap->rd_rel->reltoastrelid)
884  {
885  *pSwapToastByContent = true;
886 
887  /*
888  * When doing swap by content, any toast pointers written into NewHeap
889  * must use the old toast table's OID, because that's where the toast
890  * data will eventually be found. Set this up by setting rd_toastoid.
891  * This also tells toast_save_datum() to preserve the toast value
892  * OIDs, which we want so as not to invalidate toast pointers in
893  * system catalog caches, and to avoid making multiple copies of a
894  * single toast value.
895  *
896  * Note that we must hold NewHeap open until we are done writing data,
897  * since the relcache will not guarantee to remember this setting once
898  * the relation is closed. Also, this technique depends on the fact
899  * that no one will try to read from the NewHeap until after we've
900  * finished writing it and swapping the rels --- otherwise they could
901  * follow the toast pointers to the wrong place. (It would actually
902  * work for values copied over from the old toast table, but not for
903  * any values that we toast which were previously not toasted.)
904  */
905  NewHeap->rd_toastoid = OldHeap->rd_rel->reltoastrelid;
906  }
907  else
908  *pSwapToastByContent = false;
909 
910  /*
911  * Compute xids used to freeze and weed out dead tuples and multixacts.
912  * Since we're going to rewrite the whole table anyway, there's no reason
913  * not to be aggressive about this.
914  */
915  memset(&params, 0, sizeof(VacuumParams));
916  vacuum_get_cutoffs(OldHeap, &params, &cutoffs);
917 
918  /*
919  * FreezeXid will become the table's new relfrozenxid, and that mustn't go
920  * backwards, so take the max.
921  */
922  if (TransactionIdIsValid(OldHeap->rd_rel->relfrozenxid) &&
924  OldHeap->rd_rel->relfrozenxid))
925  cutoffs.FreezeLimit = OldHeap->rd_rel->relfrozenxid;
926 
927  /*
928  * MultiXactCutoff, similarly, shouldn't go backwards either.
929  */
930  if (MultiXactIdIsValid(OldHeap->rd_rel->relminmxid) &&
932  OldHeap->rd_rel->relminmxid))
933  cutoffs.MultiXactCutoff = OldHeap->rd_rel->relminmxid;
934 
935  /*
936  * Decide whether to use an indexscan or seqscan-and-optional-sort to scan
937  * the OldHeap. We know how to use a sort to duplicate the ordering of a
938  * btree index, and will use seqscan-and-sort for that case if the planner
939  * tells us it's cheaper. Otherwise, always indexscan if an index is
940  * provided, else plain seqscan.
941  */
942  if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID)
943  use_sort = plan_cluster_use_sort(OIDOldHeap, OIDOldIndex);
944  else
945  use_sort = false;
946 
947  /* Log what we're doing */
948  if (OldIndex != NULL && !use_sort)
949  ereport(elevel,
950  (errmsg("clustering \"%s.%s\" using index scan on \"%s\"",
951  nspname,
952  RelationGetRelationName(OldHeap),
953  RelationGetRelationName(OldIndex))));
954  else if (use_sort)
955  ereport(elevel,
956  (errmsg("clustering \"%s.%s\" using sequential scan and sort",
957  nspname,
958  RelationGetRelationName(OldHeap))));
959  else
960  ereport(elevel,
961  (errmsg("vacuuming \"%s.%s\"",
962  nspname,
963  RelationGetRelationName(OldHeap))));
964 
965  /*
966  * Hand off the actual copying to AM specific function, the generic code
967  * cannot know how to deal with visibility across AMs. Note that this
968  * routine is allowed to set FreezeXid / MultiXactCutoff to different
969  * values (e.g. because the AM doesn't use freezing).
970  */
971  table_relation_copy_for_cluster(OldHeap, NewHeap, OldIndex, use_sort,
972  cutoffs.OldestXmin, &cutoffs.FreezeLimit,
973  &cutoffs.MultiXactCutoff,
974  &num_tuples, &tups_vacuumed,
975  &tups_recently_dead);
976 
977  /* return selected values to caller, get set as relfrozenxid/minmxid */
978  *pFreezeXid = cutoffs.FreezeLimit;
979  *pCutoffMulti = cutoffs.MultiXactCutoff;
980 
981  /* Reset rd_toastoid just to be tidy --- it shouldn't be looked at again */
982  NewHeap->rd_toastoid = InvalidOid;
983 
984  num_pages = RelationGetNumberOfBlocks(NewHeap);
985 
986  /* Log what we did */
987  ereport(elevel,
988  (errmsg("\"%s.%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
989  nspname,
990  RelationGetRelationName(OldHeap),
991  tups_vacuumed, num_tuples,
992  RelationGetNumberOfBlocks(OldHeap)),
993  errdetail("%.0f dead row versions cannot be removed yet.\n"
994  "%s.",
995  tups_recently_dead,
996  pg_rusage_show(&ru0))));
997 
998  if (OldIndex != NULL)
999  index_close(OldIndex, NoLock);
1000  table_close(OldHeap, NoLock);
1001  table_close(NewHeap, NoLock);
1002 
1003  /* Update pg_class to reflect the correct values of pages and tuples. */
1004  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1005 
1006  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDNewHeap));
1007  if (!HeapTupleIsValid(reltup))
1008  elog(ERROR, "cache lookup failed for relation %u", OIDNewHeap);
1009  relform = (Form_pg_class) GETSTRUCT(reltup);
1010 
1011  relform->relpages = num_pages;
1012  relform->reltuples = num_tuples;
1013 
1014  /* Don't update the stats for pg_class. See swap_relation_files. */
1015  if (OIDOldHeap != RelationRelationId)
1016  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1017  else
1019 
1020  /* Clean up. */
1021  heap_freetuple(reltup);
1022  table_close(relRelation, RowExclusiveLock);
1023 
1024  /* Make the update visible */
1026 }
1027 
1028 /*
1029  * Swap the physical files of two given relations.
1030  *
1031  * We swap the physical identity (reltablespace, relfilenumber) while keeping
1032  * the same logical identities of the two relations. relpersistence is also
1033  * swapped, which is critical since it determines where buffers live for each
1034  * relation.
1035  *
1036  * We can swap associated TOAST data in either of two ways: recursively swap
1037  * the physical content of the toast tables (and their indexes), or swap the
1038  * TOAST links in the given relations' pg_class entries. The former is needed
1039  * to manage rewrites of shared catalogs (where we cannot change the pg_class
1040  * links) while the latter is the only way to handle cases in which a toast
1041  * table is added or removed altogether.
1042  *
1043  * Additionally, the first relation is marked with relfrozenxid set to
1044  * frozenXid. It seems a bit ugly to have this here, but the caller would
1045  * have to do it anyway, so having it here saves a heap_update. Note: in
1046  * the swap-toast-links case, we assume we don't need to change the toast
1047  * table's relfrozenxid: the new version of the toast table should already
1048  * have relfrozenxid set to RecentXmin, which is good enough.
1049  *
1050  * Lastly, if r2 and its toast table and toast index (if any) are mapped,
1051  * their OIDs are emitted into mapped_tables[]. This is hacky but beats
1052  * having to look the information up again later in finish_heap_swap.
1053  */
1054 static void
1055 swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
1056  bool swap_toast_by_content,
1057  bool is_internal,
1058  TransactionId frozenXid,
1059  MultiXactId cutoffMulti,
1060  Oid *mapped_tables)
1061 {
1062  Relation relRelation;
1063  HeapTuple reltup1,
1064  reltup2;
1065  Form_pg_class relform1,
1066  relform2;
1067  RelFileNumber relfilenumber1,
1068  relfilenumber2;
1069  RelFileNumber swaptemp;
1070  char swptmpchr;
1071  Oid relam1,
1072  relam2;
1073 
1074  /* We need writable copies of both pg_class tuples. */
1075  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1076 
1077  reltup1 = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(r1));
1078  if (!HeapTupleIsValid(reltup1))
1079  elog(ERROR, "cache lookup failed for relation %u", r1);
1080  relform1 = (Form_pg_class) GETSTRUCT(reltup1);
1081 
1082  reltup2 = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(r2));
1083  if (!HeapTupleIsValid(reltup2))
1084  elog(ERROR, "cache lookup failed for relation %u", r2);
1085  relform2 = (Form_pg_class) GETSTRUCT(reltup2);
1086 
1087  relfilenumber1 = relform1->relfilenode;
1088  relfilenumber2 = relform2->relfilenode;
1089  relam1 = relform1->relam;
1090  relam2 = relform2->relam;
1091 
1092  if (RelFileNumberIsValid(relfilenumber1) &&
1093  RelFileNumberIsValid(relfilenumber2))
1094  {
1095  /*
1096  * Normal non-mapped relations: swap relfilenumbers, reltablespaces,
1097  * relpersistence
1098  */
1099  Assert(!target_is_pg_class);
1100 
1101  swaptemp = relform1->relfilenode;
1102  relform1->relfilenode = relform2->relfilenode;
1103  relform2->relfilenode = swaptemp;
1104 
1105  swaptemp = relform1->reltablespace;
1106  relform1->reltablespace = relform2->reltablespace;
1107  relform2->reltablespace = swaptemp;
1108 
1109  swaptemp = relform1->relam;
1110  relform1->relam = relform2->relam;
1111  relform2->relam = swaptemp;
1112 
1113  swptmpchr = relform1->relpersistence;
1114  relform1->relpersistence = relform2->relpersistence;
1115  relform2->relpersistence = swptmpchr;
1116 
1117  /* Also swap toast links, if we're swapping by links */
1118  if (!swap_toast_by_content)
1119  {
1120  swaptemp = relform1->reltoastrelid;
1121  relform1->reltoastrelid = relform2->reltoastrelid;
1122  relform2->reltoastrelid = swaptemp;
1123  }
1124  }
1125  else
1126  {
1127  /*
1128  * Mapped-relation case. Here we have to swap the relation mappings
1129  * instead of modifying the pg_class columns. Both must be mapped.
1130  */
1131  if (RelFileNumberIsValid(relfilenumber1) ||
1132  RelFileNumberIsValid(relfilenumber2))
1133  elog(ERROR, "cannot swap mapped relation \"%s\" with non-mapped relation",
1134  NameStr(relform1->relname));
1135 
1136  /*
1137  * We can't change the tablespace nor persistence of a mapped rel, and
1138  * we can't handle toast link swapping for one either, because we must
1139  * not apply any critical changes to its pg_class row. These cases
1140  * should be prevented by upstream permissions tests, so these checks
1141  * are non-user-facing emergency backstop.
1142  */
1143  if (relform1->reltablespace != relform2->reltablespace)
1144  elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
1145  NameStr(relform1->relname));
1146  if (relform1->relpersistence != relform2->relpersistence)
1147  elog(ERROR, "cannot change persistence of mapped relation \"%s\"",
1148  NameStr(relform1->relname));
1149  if (relform1->relam != relform2->relam)
1150  elog(ERROR, "cannot change access method of mapped relation \"%s\"",
1151  NameStr(relform1->relname));
1152  if (!swap_toast_by_content &&
1153  (relform1->reltoastrelid || relform2->reltoastrelid))
1154  elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
1155  NameStr(relform1->relname));
1156 
1157  /*
1158  * Fetch the mappings --- shouldn't fail, but be paranoid
1159  */
1160  relfilenumber1 = RelationMapOidToFilenumber(r1, relform1->relisshared);
1161  if (!RelFileNumberIsValid(relfilenumber1))
1162  elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1163  NameStr(relform1->relname), r1);
1164  relfilenumber2 = RelationMapOidToFilenumber(r2, relform2->relisshared);
1165  if (!RelFileNumberIsValid(relfilenumber2))
1166  elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1167  NameStr(relform2->relname), r2);
1168 
1169  /*
1170  * Send replacement mappings to relmapper. Note these won't actually
1171  * take effect until CommandCounterIncrement.
1172  */
1173  RelationMapUpdateMap(r1, relfilenumber2, relform1->relisshared, false);
1174  RelationMapUpdateMap(r2, relfilenumber1, relform2->relisshared, false);
1175 
1176  /* Pass OIDs of mapped r2 tables back to caller */
1177  *mapped_tables++ = r2;
1178  }
1179 
1180  /*
1181  * Recognize that rel1's relfilenumber (swapped from rel2) is new in this
1182  * subtransaction. The rel2 storage (swapped from rel1) may or may not be
1183  * new.
1184  */
1185  {
1186  Relation rel1,
1187  rel2;
1188 
1189  rel1 = relation_open(r1, NoLock);
1190  rel2 = relation_open(r2, NoLock);
1191  rel2->rd_createSubid = rel1->rd_createSubid;
1195  relation_close(rel1, NoLock);
1196  relation_close(rel2, NoLock);
1197  }
1198 
1199  /*
1200  * In the case of a shared catalog, these next few steps will only affect
1201  * our own database's pg_class row; but that's okay, because they are all
1202  * noncritical updates. That's also an important fact for the case of a
1203  * mapped catalog, because it's possible that we'll commit the map change
1204  * and then fail to commit the pg_class update.
1205  */
1206 
1207  /* set rel1's frozen Xid and minimum MultiXid */
1208  if (relform1->relkind != RELKIND_INDEX)
1209  {
1210  Assert(!TransactionIdIsValid(frozenXid) ||
1211  TransactionIdIsNormal(frozenXid));
1212  relform1->relfrozenxid = frozenXid;
1213  relform1->relminmxid = cutoffMulti;
1214  }
1215 
1216  /* swap size statistics too, since new rel has freshly-updated stats */
1217  {
1218  int32 swap_pages;
1219  float4 swap_tuples;
1220  int32 swap_allvisible;
1221 
1222  swap_pages = relform1->relpages;
1223  relform1->relpages = relform2->relpages;
1224  relform2->relpages = swap_pages;
1225 
1226  swap_tuples = relform1->reltuples;
1227  relform1->reltuples = relform2->reltuples;
1228  relform2->reltuples = swap_tuples;
1229 
1230  swap_allvisible = relform1->relallvisible;
1231  relform1->relallvisible = relform2->relallvisible;
1232  relform2->relallvisible = swap_allvisible;
1233  }
1234 
1235  /*
1236  * Update the tuples in pg_class --- unless the target relation of the
1237  * swap is pg_class itself. In that case, there is zero point in making
1238  * changes because we'd be updating the old data that we're about to throw
1239  * away. Because the real work being done here for a mapped relation is
1240  * just to change the relation map settings, it's all right to not update
1241  * the pg_class rows in this case. The most important changes will instead
1242  * performed later, in finish_heap_swap() itself.
1243  */
1244  if (!target_is_pg_class)
1245  {
1246  CatalogIndexState indstate;
1247 
1248  indstate = CatalogOpenIndexes(relRelation);
1249  CatalogTupleUpdateWithInfo(relRelation, &reltup1->t_self, reltup1,
1250  indstate);
1251  CatalogTupleUpdateWithInfo(relRelation, &reltup2->t_self, reltup2,
1252  indstate);
1253  CatalogCloseIndexes(indstate);
1254  }
1255  else
1256  {
1257  /* no update ... but we do still need relcache inval */
1260  }
1261 
1262  /*
1263  * Now that pg_class has been updated with its relevant information for
1264  * the swap, update the dependency of the relations to point to their new
1265  * table AM, if it has changed.
1266  */
1267  if (relam1 != relam2)
1268  {
1269  if (changeDependencyFor(RelationRelationId,
1270  r1,
1271  AccessMethodRelationId,
1272  relam1,
1273  relam2) != 1)
1274  elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
1276  get_rel_name(r1));
1277  if (changeDependencyFor(RelationRelationId,
1278  r2,
1279  AccessMethodRelationId,
1280  relam2,
1281  relam1) != 1)
1282  elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
1284  get_rel_name(r2));
1285  }
1286 
1287  /*
1288  * Post alter hook for modified relations. The change to r2 is always
1289  * internal, but r1 depends on the invocation context.
1290  */
1291  InvokeObjectPostAlterHookArg(RelationRelationId, r1, 0,
1292  InvalidOid, is_internal);
1293  InvokeObjectPostAlterHookArg(RelationRelationId, r2, 0,
1294  InvalidOid, true);
1295 
1296  /*
1297  * If we have toast tables associated with the relations being swapped,
1298  * deal with them too.
1299  */
1300  if (relform1->reltoastrelid || relform2->reltoastrelid)
1301  {
1302  if (swap_toast_by_content)
1303  {
1304  if (relform1->reltoastrelid && relform2->reltoastrelid)
1305  {
1306  /* Recursively swap the contents of the toast tables */
1307  swap_relation_files(relform1->reltoastrelid,
1308  relform2->reltoastrelid,
1309  target_is_pg_class,
1310  swap_toast_by_content,
1311  is_internal,
1312  frozenXid,
1313  cutoffMulti,
1314  mapped_tables);
1315  }
1316  else
1317  {
1318  /* caller messed up */
1319  elog(ERROR, "cannot swap toast files by content when there's only one");
1320  }
1321  }
1322  else
1323  {
1324  /*
1325  * We swapped the ownership links, so we need to change dependency
1326  * data to match.
1327  *
1328  * NOTE: it is possible that only one table has a toast table.
1329  *
1330  * NOTE: at present, a TOAST table's only dependency is the one on
1331  * its owning table. If more are ever created, we'd need to use
1332  * something more selective than deleteDependencyRecordsFor() to
1333  * get rid of just the link we want.
1334  */
1335  ObjectAddress baseobject,
1336  toastobject;
1337  long count;
1338 
1339  /*
1340  * We disallow this case for system catalogs, to avoid the
1341  * possibility that the catalog we're rebuilding is one of the
1342  * ones the dependency changes would change. It's too late to be
1343  * making any data changes to the target catalog.
1344  */
1345  if (IsSystemClass(r1, relform1))
1346  elog(ERROR, "cannot swap toast files by links for system catalogs");
1347 
1348  /* Delete old dependencies */
1349  if (relform1->reltoastrelid)
1350  {
1351  count = deleteDependencyRecordsFor(RelationRelationId,
1352  relform1->reltoastrelid,
1353  false);
1354  if (count != 1)
1355  elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1356  count);
1357  }
1358  if (relform2->reltoastrelid)
1359  {
1360  count = deleteDependencyRecordsFor(RelationRelationId,
1361  relform2->reltoastrelid,
1362  false);
1363  if (count != 1)
1364  elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1365  count);
1366  }
1367 
1368  /* Register new dependencies */
1369  baseobject.classId = RelationRelationId;
1370  baseobject.objectSubId = 0;
1371  toastobject.classId = RelationRelationId;
1372  toastobject.objectSubId = 0;
1373 
1374  if (relform1->reltoastrelid)
1375  {
1376  baseobject.objectId = r1;
1377  toastobject.objectId = relform1->reltoastrelid;
1378  recordDependencyOn(&toastobject, &baseobject,
1380  }
1381 
1382  if (relform2->reltoastrelid)
1383  {
1384  baseobject.objectId = r2;
1385  toastobject.objectId = relform2->reltoastrelid;
1386  recordDependencyOn(&toastobject, &baseobject,
1388  }
1389  }
1390  }
1391 
1392  /*
1393  * If we're swapping two toast tables by content, do the same for their
1394  * valid index. The swap can actually be safely done only if the relations
1395  * have indexes.
1396  */
1397  if (swap_toast_by_content &&
1398  relform1->relkind == RELKIND_TOASTVALUE &&
1399  relform2->relkind == RELKIND_TOASTVALUE)
1400  {
1401  Oid toastIndex1,
1402  toastIndex2;
1403 
1404  /* Get valid index for each relation */
1405  toastIndex1 = toast_get_valid_index(r1,
1407  toastIndex2 = toast_get_valid_index(r2,
1409 
1410  swap_relation_files(toastIndex1,
1411  toastIndex2,
1412  target_is_pg_class,
1413  swap_toast_by_content,
1414  is_internal,
1417  mapped_tables);
1418  }
1419 
1420  /* Clean up. */
1421  heap_freetuple(reltup1);
1422  heap_freetuple(reltup2);
1423 
1424  table_close(relRelation, RowExclusiveLock);
1425 }
1426 
1427 /*
1428  * Remove the transient table that was built by make_new_heap, and finish
1429  * cleaning up (including rebuilding all indexes on the old heap).
1430  */
1431 void
1432 finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
1433  bool is_system_catalog,
1434  bool swap_toast_by_content,
1435  bool check_constraints,
1436  bool is_internal,
1437  TransactionId frozenXid,
1438  MultiXactId cutoffMulti,
1439  char newrelpersistence)
1440 {
1441  ObjectAddress object;
1442  Oid mapped_tables[4];
1443  int reindex_flags;
1444  ReindexParams reindex_params = {0};
1445  int i;
1446 
1447  /* Report that we are now swapping relation files */
1450 
1451  /* Zero out possible results from swapped_relation_files */
1452  memset(mapped_tables, 0, sizeof(mapped_tables));
1453 
1454  /*
1455  * Swap the contents of the heap relations (including any toast tables).
1456  * Also set old heap's relfrozenxid to frozenXid.
1457  */
1458  swap_relation_files(OIDOldHeap, OIDNewHeap,
1459  (OIDOldHeap == RelationRelationId),
1460  swap_toast_by_content, is_internal,
1461  frozenXid, cutoffMulti, mapped_tables);
1462 
1463  /*
1464  * If it's a system catalog, queue a sinval message to flush all catcaches
1465  * on the catalog when we reach CommandCounterIncrement.
1466  */
1467  if (is_system_catalog)
1468  CacheInvalidateCatalog(OIDOldHeap);
1469 
1470  /*
1471  * Rebuild each index on the relation (but not the toast table, which is
1472  * all-new at this point). It is important to do this before the DROP
1473  * step because if we are processing a system catalog that will be used
1474  * during DROP, we want to have its indexes available. There is no
1475  * advantage to the other order anyway because this is all transactional,
1476  * so no chance to reclaim disk space before commit. We do not need a
1477  * final CommandCounterIncrement() because reindex_relation does it.
1478  *
1479  * Note: because index_build is called via reindex_relation, it will never
1480  * set indcheckxmin true for the indexes. This is OK even though in some
1481  * sense we are building new indexes rather than rebuilding existing ones,
1482  * because the new heap won't contain any HOT chains at all, let alone
1483  * broken ones, so it can't be necessary to set indcheckxmin.
1484  */
1485  reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE;
1486  if (check_constraints)
1487  reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS;
1488 
1489  /*
1490  * Ensure that the indexes have the same persistence as the parent
1491  * relation.
1492  */
1493  if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1494  reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1495  else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1496  reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
1497 
1498  /* Report that we are now reindexing relations */
1501 
1502  reindex_relation(NULL, OIDOldHeap, reindex_flags, &reindex_params);
1503 
1504  /* Report that we are now doing clean up */
1507 
1508  /*
1509  * If the relation being rebuilt is pg_class, swap_relation_files()
1510  * couldn't update pg_class's own pg_class entry (check comments in
1511  * swap_relation_files()), thus relfrozenxid was not updated. That's
1512  * annoying because a potential reason for doing a VACUUM FULL is a
1513  * imminent or actual anti-wraparound shutdown. So, now that we can
1514  * access the new relation using its indices, update relfrozenxid.
1515  * pg_class doesn't have a toast relation, so we don't need to update the
1516  * corresponding toast relation. Not that there's little point moving all
1517  * relfrozenxid updates here since swap_relation_files() needs to write to
1518  * pg_class for non-mapped relations anyway.
1519  */
1520  if (OIDOldHeap == RelationRelationId)
1521  {
1522  Relation relRelation;
1523  HeapTuple reltup;
1524  Form_pg_class relform;
1525 
1526  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1527 
1528  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
1529  if (!HeapTupleIsValid(reltup))
1530  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1531  relform = (Form_pg_class) GETSTRUCT(reltup);
1532 
1533  relform->relfrozenxid = frozenXid;
1534  relform->relminmxid = cutoffMulti;
1535 
1536  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1537 
1538  table_close(relRelation, RowExclusiveLock);
1539  }
1540 
1541  /* Destroy new heap with old filenumber */
1542  object.classId = RelationRelationId;
1543  object.objectId = OIDNewHeap;
1544  object.objectSubId = 0;
1545 
1546  /*
1547  * The new relation is local to our transaction and we know nothing
1548  * depends on it, so DROP_RESTRICT should be OK.
1549  */
1551 
1552  /* performDeletion does CommandCounterIncrement at end */
1553 
1554  /*
1555  * Now we must remove any relation mapping entries that we set up for the
1556  * transient table, as well as its toast table and toast index if any. If
1557  * we fail to do this before commit, the relmapper will complain about new
1558  * permanent map entries being added post-bootstrap.
1559  */
1560  for (i = 0; OidIsValid(mapped_tables[i]); i++)
1561  RelationMapRemoveMapping(mapped_tables[i]);
1562 
1563  /*
1564  * At this point, everything is kosher except that, if we did toast swap
1565  * by links, the toast table's name corresponds to the transient table.
1566  * The name is irrelevant to the backend because it's referenced by OID,
1567  * but users looking at the catalogs could be confused. Rename it to
1568  * prevent this problem.
1569  *
1570  * Note no lock required on the relation, because we already hold an
1571  * exclusive lock on it.
1572  */
1573  if (!swap_toast_by_content)
1574  {
1575  Relation newrel;
1576 
1577  newrel = table_open(OIDOldHeap, NoLock);
1578  if (OidIsValid(newrel->rd_rel->reltoastrelid))
1579  {
1580  Oid toastidx;
1581  char NewToastName[NAMEDATALEN];
1582 
1583  /* Get the associated valid index to be renamed */
1584  toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
1585  NoLock);
1586 
1587  /* rename the toast table ... */
1588  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
1589  OIDOldHeap);
1590  RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1591  NewToastName, true, false);
1592 
1593  /* ... and its valid index too. */
1594  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
1595  OIDOldHeap);
1596 
1597  RenameRelationInternal(toastidx,
1598  NewToastName, true, true);
1599 
1600  /*
1601  * Reset the relrewrite for the toast. The command-counter
1602  * increment is required here as we are about to update the tuple
1603  * that is updated as part of RenameRelationInternal.
1604  */
1606  ResetRelRewrite(newrel->rd_rel->reltoastrelid);
1607  }
1608  relation_close(newrel, NoLock);
1609  }
1610 
1611  /* if it's not a catalog table, clear any missing attribute settings */
1612  if (!is_system_catalog)
1613  {
1614  Relation newrel;
1615 
1616  newrel = table_open(OIDOldHeap, NoLock);
1617  RelationClearMissing(newrel);
1618  relation_close(newrel, NoLock);
1619  }
1620 }
1621 
1622 
1623 /*
1624  * Get a list of tables that the current user owns and
1625  * have indisclustered set. Return the list in a List * of RelToCluster
1626  * (stored in the specified memory context), each one giving the tableOid
1627  * and the indexOid on which the table is already clustered.
1628  */
1629 static List *
1631 {
1632  Relation indRelation;
1633  TableScanDesc scan;
1634  ScanKeyData entry;
1635  HeapTuple indexTuple;
1637  MemoryContext old_context;
1638  List *rtcs = NIL;
1639 
1640  /*
1641  * Get all indexes that have indisclustered set and are owned by
1642  * appropriate user.
1643  */
1644  indRelation = table_open(IndexRelationId, AccessShareLock);
1645  ScanKeyInit(&entry,
1646  Anum_pg_index_indisclustered,
1647  BTEqualStrategyNumber, F_BOOLEQ,
1648  BoolGetDatum(true));
1649  scan = table_beginscan_catalog(indRelation, 1, &entry);
1650  while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1651  {
1652  RelToCluster *rtc;
1653 
1654  index = (Form_pg_index) GETSTRUCT(indexTuple);
1655 
1656  if (!object_ownercheck(RelationRelationId, index->indrelid, GetUserId()))
1657  continue;
1658 
1659  /* Use a permanent memory context for the result list */
1660  old_context = MemoryContextSwitchTo(cluster_context);
1661 
1662  rtc = (RelToCluster *) palloc(sizeof(RelToCluster));
1663  rtc->tableOid = index->indrelid;
1664  rtc->indexOid = index->indexrelid;
1665  rtcs = lappend(rtcs, rtc);
1666 
1667  MemoryContextSwitchTo(old_context);
1668  }
1669  table_endscan(scan);
1670 
1671  relation_close(indRelation, AccessShareLock);
1672 
1673  return rtcs;
1674 }
1675 
1676 /*
1677  * Given an index on a partitioned table, return a list of RelToCluster for
1678  * all the children leaves tables/indexes.
1679  *
1680  * Like expand_vacuum_rel, but here caller must hold AccessExclusiveLock
1681  * on the table containing the index.
1682  */
1683 static List *
1685 {
1686  List *inhoids;
1687  ListCell *lc;
1688  List *rtcs = NIL;
1689  MemoryContext old_context;
1690 
1691  /* Do not lock the children until they're processed */
1692  inhoids = find_all_inheritors(indexOid, NoLock, NULL);
1693 
1694  foreach(lc, inhoids)
1695  {
1696  Oid indexrelid = lfirst_oid(lc);
1697  Oid relid = IndexGetRelation(indexrelid, false);
1698  RelToCluster *rtc;
1699 
1700  /* consider only leaf indexes */
1701  if (get_rel_relkind(indexrelid) != RELKIND_INDEX)
1702  continue;
1703 
1704  /* Silently skip partitions which the user has no access to. */
1705  if (!object_ownercheck(RelationRelationId, relid, GetUserId()) &&
1706  (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) ||
1707  IsSharedRelation(relid)))
1708  continue;
1709 
1710  /* Use a permanent memory context for the result list */
1711  old_context = MemoryContextSwitchTo(cluster_context);
1712 
1713  rtc = (RelToCluster *) palloc(sizeof(RelToCluster));
1714  rtc->tableOid = relid;
1715  rtc->indexOid = indexrelid;
1716  rtcs = lappend(rtcs, rtc);
1717 
1718  MemoryContextSwitchTo(old_context);
1719  }
1720 
1721  return rtcs;
1722 }
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4132
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_CLUSTER
uint32 BlockNumber
Definition: block.h:31
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:229
#define NameStr(name)
Definition: c.h:735
signed int int32
Definition: c.h:483
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:171
TransactionId MultiXactId
Definition: c.h:651
float float4
Definition: c.h:618
uint32 TransactionId
Definition: c.h:641
#define OidIsValid(objectId)
Definition: c.h:764
bool IsSystemRelation(Relation relation)
Definition: catalog.c:75
bool IsSharedRelation(Oid relationId)
Definition: catalog.c:245
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:87
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
Definition: cluster.c:312
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:500
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:1432
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1630
static List * get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
Definition: cluster.c:1684
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:633
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
Definition: cluster.c:814
static void cluster_multiple_rels(List *rtcs, ClusterParams *params)
Definition: cluster.c:267
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition: cluster.c:688
void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
Definition: cluster.c:110
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:560
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:1055
#define CLUOPT_VERBOSE
Definition: cluster.h:23
#define CLUOPT_RECHECK_ISCLUSTERED
Definition: cluster.h:25
#define CLUOPT_RECHECK
Definition: cluster.h:24
bool defGetBoolean(DefElem *def)
Definition: define.c:108
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:329
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:136
int errdetail(const char *fmt,...)
Definition: elog.c:1208
int errcode(int sqlerrcode)
Definition: elog.c:860
int errmsg(const char *fmt,...)
Definition: elog.c:1075
#define DEBUG2
Definition: elog.h:29
#define ERROR
Definition: elog.h:39
#define INFO
Definition: elog.h:34
#define ereport(elevel,...)
Definition: elog.h:149
Oid MyDatabaseId
Definition: globals.c:90
int NewGUCNestLevel(void)
Definition: guc.c:2231
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2245
void RelationClearMissing(Relation rel)
Definition: heap.c:1927
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:1094
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1086
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
#define stmt
Definition: indent_codes.h:59
int verbose
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3549
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3913
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition: index.h:161
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition: index.h:159
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition: index.h:162
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:160
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition: indexing.c:61
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition: indexing.c:43
void CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, CatalogIndexState indstate)
Definition: indexing.c:337
void CacheInvalidateCatalog(Oid catalogId)
Definition: inval.c:1337
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1397
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:339
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:109
int LOCKMODE
Definition: lockdefs.h:26
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define AccessShareLock
Definition: lockdefs.h:36
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3321
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1980
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1929
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1905
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3556
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1862
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
void * palloc(Size size)
Definition: mcxt.c:1201
MemoryContext PortalContext
Definition: mcxt.c:150
#define AllocSetContextCreate
Definition: memutils.h:128
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:152
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:313
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:632
Oid GetUserId(void)
Definition: miscinit.c:511
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:639
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3163
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
#define InvalidMultiXactId
Definition: multixact.h:24
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:3413
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:426
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
@ DROP_RESTRICT
Definition: parsenodes.h:2169
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define NAMEDATALEN
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:456
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:300
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:256
#define lfirst(lc)
Definition: pg_list.h:172
#define NIL
Definition: pg_list.h:68
#define lfirst_oid(lc)
Definition: pg_list.h:174
const char * pg_rusage_show(const PGRUsage *ru0)
Definition: pg_rusage.c:40
void pg_rusage_init(PGRUsage *ru0)
Definition: pg_rusage.c:27
bool plan_cluster_use_sort(Oid tableOid, Oid indexOid)
Definition: planner.c:6487
#define snprintf
Definition: port.h:238
uintptr_t Datum
Definition: postgres.h:64
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3093
@ ONCOMMIT_NOOP
Definition: primnodes.h:57
#define PROGRESS_CLUSTER_PHASE
Definition: progress.h:58
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:77
#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX
Definition: progress.h:72
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:76
#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP
Definition: progress.h:73
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:57
#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES
Definition: progress.h:71
#define RelationGetRelid(relation)
Definition: rel.h:504
#define RelationGetDescr(relation)
Definition: rel.h:530
#define RelationIsMapped(relation)
Definition: rel.h:553
#define RelationGetRelationName(relation)
Definition: rel.h:538
#define RelationIsPopulated(relation)
Definition: rel.h:676
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:657
#define RelationGetNamespace(relation)
Definition: rel.h:545
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4755
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3924
void RelationMapRemoveMapping(Oid relationId)
Definition: relmapper.c:439
RelFileNumber RelationMapOidToFilenumber(Oid relationId, bool shared)
Definition: relmapper.c:166
void RelationMapUpdateMap(Oid relationId, RelFileNumber fileNumber, bool shared, bool immediate)
Definition: relmapper.c:326
Oid RelFileNumber
Definition: relpath.h:25
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:28
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:223
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:655
void PopActiveSnapshot(void)
Definition: snapmgr.c:750
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:48
#define BTEqualStrategyNumber
Definition: stratnum.h:31
bits32 options
Definition: cluster.h:30
char * defname
Definition: parsenodes.h:802
int location
Definition: parsenodes.h:806
ItemPointerData t_self
Definition: htup.h:65
bool amclusterable
Definition: amapi.h:241
Definition: pg_list.h:54
Oid indexOid
Definition: cluster.c:71
Oid tableOid
Definition: cluster.c:70
Oid rd_toastoid
Definition: rel.h:250
struct IndexAmRoutine * rd_indam
Definition: rel.h:205
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
struct HeapTupleData * rd_indextuple
Definition: rel.h:193
Form_pg_index rd_index
Definition: rel.h:191
SubTransactionId rd_newRelfilelocatorSubid
Definition: rel.h:104
SubTransactionId rd_createSubid
Definition: rel.h:103
Form_pg_class rd_rel
Definition: rel.h:111
TransactionId FreezeLimit
Definition: vacuum.h:275
TransactionId OldestXmin
Definition: vacuum.h:265
MultiXactId MultiXactCutoff
Definition: vacuum.h:276
Definition: type.h:95
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:267
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:219
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:480
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:86
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:95
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1009
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:1673
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4282
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:18108
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4335
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4192
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, Oid OIDOldToast)
Definition: toasting.c:66
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:280
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
Definition: vacuum.c:1075
void CommandCounterIncrement(void)
Definition: xact.c:1078
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3497
void StartTransactionCommand(void)
Definition: xact.c:2953
void CommitTransactionCommand(void)
Definition: xact.c:3050