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