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-2022, 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_inherits.h"
38 #include "catalog/toasting.h"
39 #include "commands/cluster.h"
40 #include "commands/defrem.h"
41 #include "commands/progress.h"
42 #include "commands/tablecmds.h"
43 #include "commands/vacuum.h"
44 #include "miscadmin.h"
45 #include "optimizer/optimizer.h"
46 #include "pgstat.h"
47 #include "storage/bufmgr.h"
48 #include "storage/lmgr.h"
49 #include "storage/predicate.h"
50 #include "utils/acl.h"
51 #include "utils/fmgroids.h"
52 #include "utils/inval.h"
53 #include "utils/lsyscache.h"
54 #include "utils/memutils.h"
55 #include "utils/pg_rusage.h"
56 #include "utils/relmapper.h"
57 #include "utils/snapmgr.h"
58 #include "utils/syscache.h"
59 #include "utils/tuplesort.h"
60 
61 /*
62  * This struct is used to pass around the information on tables to be
63  * clustered. We need this so we can make a list of them when invoked without
64  * a specific table/index pair.
65  */
66 typedef struct
67 {
70 } RelToCluster;
71 
72 
73 static void cluster_multiple_rels(List *rtcs, ClusterParams *params);
74 static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose);
75 static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
76  bool verbose, bool *pSwapToastByContent,
77  TransactionId *pFreezeXid, MultiXactId *pCutoffMulti);
78 static List *get_tables_to_cluster(MemoryContext cluster_context);
80  Oid indexOid);
81 
82 
83 /*---------------------------------------------------------------------------
84  * This cluster code allows for clustering multiple tables at once. Because
85  * of this, we cannot just run everything on a single transaction, or we
86  * would be forced to acquire exclusive locks on all the tables being
87  * clustered, simultaneously --- very likely leading to deadlock.
88  *
89  * To solve this we follow a similar strategy to VACUUM code,
90  * clustering each relation in a separate transaction. For this to work,
91  * we need to:
92  * - provide a separate memory context so that we can pass information in
93  * a way that survives across transactions
94  * - start a new transaction every time a new relation is clustered
95  * - check for validity of the information on to-be-clustered relations,
96  * as someone might have deleted a relation behind our back, or
97  * clustered one on a different index
98  * - end the transaction
99  *
100  * The single-relation case does not have any such overhead.
101  *
102  * We also allow a relation to be specified without index. In that case,
103  * the indisclustered bit will be looked up, and an ERROR will be thrown
104  * if there is no index with the bit set.
105  *---------------------------------------------------------------------------
106  */
107 void
108 cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
109 {
110  ListCell *lc;
111  ClusterParams params = {0};
112  bool verbose = false;
113  Relation rel = NULL;
114  Oid indexOid = InvalidOid;
115  MemoryContext cluster_context;
116  List *rtcs;
117 
118  /* Parse option list */
119  foreach(lc, stmt->params)
120  {
121  DefElem *opt = (DefElem *) lfirst(lc);
122 
123  if (strcmp(opt->defname, "verbose") == 0)
124  verbose = defGetBoolean(opt);
125  else
126  ereport(ERROR,
127  (errcode(ERRCODE_SYNTAX_ERROR),
128  errmsg("unrecognized CLUSTER option \"%s\"",
129  opt->defname),
130  parser_errposition(pstate, opt->location)));
131  }
132 
133  params.options = (verbose ? CLUOPT_VERBOSE : 0);
134 
135  if (stmt->relation != NULL)
136  {
137  /* This is the single-relation case. */
138  Oid tableOid;
139 
140  /*
141  * Find, lock, and check permissions on the table. We obtain
142  * AccessExclusiveLock right away to avoid lock-upgrade hazard in the
143  * single-transaction case.
144  */
145  tableOid = RangeVarGetRelidExtended(stmt->relation,
147  0,
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 relfilenodes 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();
354 
355  /*
356  * Since we may open a new transaction for each relation, we have to check
357  * that the relation still is what we think it is.
358  *
359  * If this is a single-transaction CLUSTER, we can skip these tests. We
360  * *must* skip the one on indisclustered since it would reject an attempt
361  * to cluster a not-previously-clustered index.
362  */
363  if (recheck)
364  {
365  /* Check that the user still owns the relation */
366  if (!pg_class_ownercheck(tableOid, save_userid))
367  {
369  goto out;
370  }
371 
372  /*
373  * Silently skip a temp table for a remote session. Only doing this
374  * check in the "recheck" case is appropriate (which currently means
375  * somebody is executing a database-wide CLUSTER or on a partitioned
376  * table), because there is another check in cluster() which will stop
377  * any attempt to cluster remote temp tables by name. There is
378  * another check in cluster_rel which is redundant, but we leave it
379  * for extra safety.
380  */
381  if (RELATION_IS_OTHER_TEMP(OldHeap))
382  {
384  goto out;
385  }
386 
387  if (OidIsValid(indexOid))
388  {
389  /*
390  * Check that the index still exists
391  */
393  {
395  goto out;
396  }
397 
398  /*
399  * Check that the index is still the one with indisclustered set,
400  * if needed.
401  */
402  if ((params->options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
403  !get_index_isclustered(indexOid))
404  {
406  goto out;
407  }
408  }
409  }
410 
411  /*
412  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
413  * would work in most respects, but the index would only get marked as
414  * indisclustered in the current database, leading to unexpected behavior
415  * if CLUSTER were later invoked in another database.
416  */
417  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
418  ereport(ERROR,
419  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
420  errmsg("cannot cluster a shared catalog")));
421 
422  /*
423  * Don't process temp tables of other backends ... their local buffer
424  * manager is not going to cope.
425  */
426  if (RELATION_IS_OTHER_TEMP(OldHeap))
427  {
428  if (OidIsValid(indexOid))
429  ereport(ERROR,
430  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
431  errmsg("cannot cluster temporary tables of other sessions")));
432  else
433  ereport(ERROR,
434  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
435  errmsg("cannot vacuum temporary tables of other sessions")));
436  }
437 
438  /*
439  * Also check for active uses of the relation in the current transaction,
440  * including open scans and pending AFTER trigger events.
441  */
442  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
443 
444  /* Check heap and index are valid to cluster on */
445  if (OidIsValid(indexOid))
447 
448  /*
449  * Quietly ignore the request if this is a materialized view which has not
450  * been populated from its query. No harm is done because there is no data
451  * to deal with, and we don't want to throw an error if this is part of a
452  * multi-relation request -- for example, CLUSTER was run on the entire
453  * database.
454  */
455  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
456  !RelationIsPopulated(OldHeap))
457  {
459  goto out;
460  }
461 
462  Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
463  OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
464  OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
465 
466  /*
467  * All predicate locks on the tuples or pages are about to be made
468  * invalid, because we move tuples around. Promote them to relation
469  * locks. Predicate locks on indexes will be promoted when they are
470  * reindexed.
471  */
473 
474  /* rebuild_relation does all the dirty work */
475  rebuild_relation(OldHeap, indexOid, verbose);
476 
477  /* NB: rebuild_relation does table_close() on OldHeap */
478 
479 out:
480  /* Roll back any GUC changes executed by index functions */
481  AtEOXact_GUC(false, save_nestlevel);
482 
483  /* Restore userid and security context */
484  SetUserIdAndSecContext(save_userid, save_sec_context);
485 
487 }
488 
489 /*
490  * Verify that the specified heap and index are valid to cluster on
491  *
492  * Side effect: obtains lock on the index. The caller may
493  * in some cases already have AccessExclusiveLock on the table, but
494  * not in all cases so we can't rely on the table-level lock for
495  * protection here.
496  */
497 void
498 check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
499 {
500  Relation OldIndex;
501 
502  OldIndex = index_open(indexOid, lockmode);
503 
504  /*
505  * Check that index is in fact an index on the given relation
506  */
507  if (OldIndex->rd_index == NULL ||
508  OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
509  ereport(ERROR,
510  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
511  errmsg("\"%s\" is not an index for table \"%s\"",
512  RelationGetRelationName(OldIndex),
513  RelationGetRelationName(OldHeap))));
514 
515  /* Index AM must allow clustering */
516  if (!OldIndex->rd_indam->amclusterable)
517  ereport(ERROR,
518  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
519  errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
520  RelationGetRelationName(OldIndex))));
521 
522  /*
523  * Disallow clustering on incomplete indexes (those that might not index
524  * every row of the relation). We could relax this by making a separate
525  * seqscan pass over the table to copy the missing rows, but that seems
526  * expensive and tedious.
527  */
528  if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
529  ereport(ERROR,
530  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
531  errmsg("cannot cluster on partial index \"%s\"",
532  RelationGetRelationName(OldIndex))));
533 
534  /*
535  * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
536  * it might well not contain entries for every heap row, or might not even
537  * be internally consistent. (But note that we don't check indcheckxmin;
538  * the worst consequence of following broken HOT chains would be that we
539  * might put recently-dead tuples out-of-order in the new table, and there
540  * is little harm in that.)
541  */
542  if (!OldIndex->rd_index->indisvalid)
543  ereport(ERROR,
544  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
545  errmsg("cannot cluster on invalid index \"%s\"",
546  RelationGetRelationName(OldIndex))));
547 
548  /* Drop relcache refcnt on OldIndex, but keep lock */
549  index_close(OldIndex, NoLock);
550 }
551 
552 /*
553  * mark_index_clustered: mark the specified index as the one clustered on
554  *
555  * With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
556  */
557 void
558 mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
559 {
560  HeapTuple indexTuple;
561  Form_pg_index indexForm;
562  Relation pg_index;
563  ListCell *index;
564 
565  /* Disallow applying to a partitioned table */
566  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
567  ereport(ERROR,
568  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
569  errmsg("cannot mark index clustered in partitioned table")));
570 
571  /*
572  * If the index is already marked clustered, no need to do anything.
573  */
574  if (OidIsValid(indexOid))
575  {
576  if (get_index_isclustered(indexOid))
577  return;
578  }
579 
580  /*
581  * Check each index of the relation and set/clear the bit as needed.
582  */
583  pg_index = table_open(IndexRelationId, RowExclusiveLock);
584 
585  foreach(index, RelationGetIndexList(rel))
586  {
587  Oid thisIndexOid = lfirst_oid(index);
588 
589  indexTuple = SearchSysCacheCopy1(INDEXRELID,
590  ObjectIdGetDatum(thisIndexOid));
591  if (!HeapTupleIsValid(indexTuple))
592  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
593  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
594 
595  /*
596  * Unset the bit if set. We know it's wrong because we checked this
597  * earlier.
598  */
599  if (indexForm->indisclustered)
600  {
601  indexForm->indisclustered = false;
602  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
603  }
604  else if (thisIndexOid == indexOid)
605  {
606  /* this was checked earlier, but let's be real sure */
607  if (!indexForm->indisvalid)
608  elog(ERROR, "cannot cluster on invalid index %u", indexOid);
609  indexForm->indisclustered = true;
610  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
611  }
612 
613  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
614  InvalidOid, is_internal);
615 
616  heap_freetuple(indexTuple);
617  }
618 
619  table_close(pg_index, RowExclusiveLock);
620 }
621 
622 /*
623  * rebuild_relation: rebuild an existing relation in index or physical order
624  *
625  * OldHeap: table to rebuild --- must be opened and exclusive-locked!
626  * indexOid: index to cluster by, or InvalidOid to rewrite in physical order.
627  *
628  * NB: this routine closes OldHeap at the right time; caller should not.
629  */
630 static void
631 rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
632 {
633  Oid tableOid = RelationGetRelid(OldHeap);
634  Oid accessMethod = OldHeap->rd_rel->relam;
635  Oid tableSpace = OldHeap->rd_rel->reltablespace;
636  Oid OIDNewHeap;
637  char relpersistence;
638  bool is_system_catalog;
639  bool swap_toast_by_content;
640  TransactionId frozenXid;
641  MultiXactId cutoffMulti;
642 
643  if (OidIsValid(indexOid))
644  /* Mark the correct index as clustered */
645  mark_index_clustered(OldHeap, indexOid, true);
646 
647  /* Remember info about rel before closing OldHeap */
648  relpersistence = OldHeap->rd_rel->relpersistence;
649  is_system_catalog = IsSystemRelation(OldHeap);
650 
651  /* Close relcache entry, but keep lock until transaction commit */
652  table_close(OldHeap, NoLock);
653 
654  /* Create the transient table that will receive the re-ordered data */
655  OIDNewHeap = make_new_heap(tableOid, tableSpace,
656  accessMethod,
657  relpersistence,
659 
660  /* Copy the heap data into the new table in the desired order */
661  copy_table_data(OIDNewHeap, tableOid, indexOid, verbose,
662  &swap_toast_by_content, &frozenXid, &cutoffMulti);
663 
664  /*
665  * Swap the physical files of the target and transient tables, then
666  * rebuild the target's indexes and throw away the transient table.
667  */
668  finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog,
669  swap_toast_by_content, false, true,
670  frozenXid, cutoffMulti,
671  relpersistence);
672 }
673 
674 
675 /*
676  * Create the transient table that will be filled with new data during
677  * CLUSTER, ALTER TABLE, and similar operations. The transient table
678  * duplicates the logical structure of the OldHeap; but will have the
679  * specified physical storage properties NewTableSpace, NewAccessMethod, and
680  * relpersistence.
681  *
682  * After this, the caller should load the new heap with transferred/modified
683  * data, then call finish_heap_swap to complete the operation.
684  */
685 Oid
686 make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod,
687  char relpersistence, LOCKMODE lockmode)
688 {
689  TupleDesc OldHeapDesc;
690  char NewHeapName[NAMEDATALEN];
691  Oid OIDNewHeap;
692  Oid toastid;
693  Relation OldHeap;
694  HeapTuple tuple;
695  Datum reloptions;
696  bool isNull;
697  Oid namespaceid;
698 
699  OldHeap = table_open(OIDOldHeap, lockmode);
700  OldHeapDesc = RelationGetDescr(OldHeap);
701 
702  /*
703  * Note that the NewHeap will not receive any of the defaults or
704  * constraints associated with the OldHeap; we don't need 'em, and there's
705  * no reason to spend cycles inserting them into the catalogs only to
706  * delete them.
707  */
708 
709  /*
710  * But we do want to use reloptions of the old heap for new heap.
711  */
712  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
713  if (!HeapTupleIsValid(tuple))
714  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
715  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
716  &isNull);
717  if (isNull)
718  reloptions = (Datum) 0;
719 
720  if (relpersistence == RELPERSISTENCE_TEMP)
721  namespaceid = LookupCreationNamespace("pg_temp");
722  else
723  namespaceid = RelationGetNamespace(OldHeap);
724 
725  /*
726  * Create the new heap, using a temporary name in the same namespace as
727  * the existing table. NOTE: there is some risk of collision with user
728  * relnames. Working around this seems more trouble than it's worth; in
729  * particular, we can't create the new heap in a different namespace from
730  * the old, or we will have problems with the TEMP status of temp tables.
731  *
732  * Note: the new heap is not a shared relation, even if we are rebuilding
733  * a shared rel. However, we do make the new heap mapped if the source is
734  * mapped. This simplifies swap_relation_files, and is absolutely
735  * necessary for rebuilding pg_class, for reasons explained there.
736  */
737  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
738 
739  OIDNewHeap = heap_create_with_catalog(NewHeapName,
740  namespaceid,
741  NewTableSpace,
742  InvalidOid,
743  InvalidOid,
744  InvalidOid,
745  OldHeap->rd_rel->relowner,
746  NewAccessMethod,
747  OldHeapDesc,
748  NIL,
749  RELKIND_RELATION,
750  relpersistence,
751  false,
752  RelationIsMapped(OldHeap),
754  reloptions,
755  false,
756  true,
757  true,
758  OIDOldHeap,
759  NULL);
760  Assert(OIDNewHeap != InvalidOid);
761 
762  ReleaseSysCache(tuple);
763 
764  /*
765  * Advance command counter so that the newly-created relation's catalog
766  * tuples will be visible to table_open.
767  */
769 
770  /*
771  * If necessary, create a TOAST table for the new relation.
772  *
773  * If the relation doesn't have a TOAST table already, we can't need one
774  * for the new relation. The other way around is possible though: if some
775  * wide columns have been dropped, NewHeapCreateToastTable can decide that
776  * no TOAST table is needed for the new table.
777  *
778  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
779  * that the TOAST table will be visible for insertion.
780  */
781  toastid = OldHeap->rd_rel->reltoastrelid;
782  if (OidIsValid(toastid))
783  {
784  /* keep the existing toast table's reloptions, if any */
785  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
786  if (!HeapTupleIsValid(tuple))
787  elog(ERROR, "cache lookup failed for relation %u", toastid);
788  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
789  &isNull);
790  if (isNull)
791  reloptions = (Datum) 0;
792 
793  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid);
794 
795  ReleaseSysCache(tuple);
796  }
797 
798  table_close(OldHeap, NoLock);
799 
800  return OIDNewHeap;
801 }
802 
803 /*
804  * Do the physical copying of table data.
805  *
806  * There are three output parameters:
807  * *pSwapToastByContent is set true if toast tables must be swapped by content.
808  * *pFreezeXid receives the TransactionId used as freeze cutoff point.
809  * *pCutoffMulti receives the MultiXactId used as a cutoff point.
810  */
811 static void
812 copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
813  bool *pSwapToastByContent, TransactionId *pFreezeXid,
814  MultiXactId *pCutoffMulti)
815 {
816  Relation NewHeap,
817  OldHeap,
818  OldIndex;
819  Relation relRelation;
820  HeapTuple reltup;
821  Form_pg_class relform;
824  TransactionId OldestXmin,
825  FreezeXid;
826  MultiXactId OldestMxact,
827  MultiXactCutoff;
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  vacuum_set_xid_limits(OldHeap, 0, 0, 0, 0, &OldestXmin, &OldestMxact,
916  &FreezeXid, &MultiXactCutoff);
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) &&
923  TransactionIdPrecedes(FreezeXid, OldHeap->rd_rel->relfrozenxid))
924  FreezeXid = OldHeap->rd_rel->relfrozenxid;
925 
926  /*
927  * MultiXactCutoff, similarly, shouldn't go backwards either.
928  */
929  if (MultiXactIdIsValid(OldHeap->rd_rel->relminmxid) &&
930  MultiXactIdPrecedes(MultiXactCutoff, OldHeap->rd_rel->relminmxid))
931  MultiXactCutoff = OldHeap->rd_rel->relminmxid;
932 
933  /*
934  * Decide whether to use an indexscan or seqscan-and-optional-sort to scan
935  * the OldHeap. We know how to use a sort to duplicate the ordering of a
936  * btree index, and will use seqscan-and-sort for that case if the planner
937  * tells us it's cheaper. Otherwise, always indexscan if an index is
938  * provided, else plain seqscan.
939  */
940  if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID)
941  use_sort = plan_cluster_use_sort(OIDOldHeap, OIDOldIndex);
942  else
943  use_sort = false;
944 
945  /* Log what we're doing */
946  if (OldIndex != NULL && !use_sort)
947  ereport(elevel,
948  (errmsg("clustering \"%s.%s\" using index scan on \"%s\"",
949  nspname,
950  RelationGetRelationName(OldHeap),
951  RelationGetRelationName(OldIndex))));
952  else if (use_sort)
953  ereport(elevel,
954  (errmsg("clustering \"%s.%s\" using sequential scan and sort",
955  nspname,
956  RelationGetRelationName(OldHeap))));
957  else
958  ereport(elevel,
959  (errmsg("vacuuming \"%s.%s\"",
960  nspname,
961  RelationGetRelationName(OldHeap))));
962 
963  /*
964  * Hand off the actual copying to AM specific function, the generic code
965  * cannot know how to deal with visibility across AMs. Note that this
966  * routine is allowed to set FreezeXid / MultiXactCutoff to different
967  * values (e.g. because the AM doesn't use freezing).
968  */
969  table_relation_copy_for_cluster(OldHeap, NewHeap, OldIndex, use_sort,
970  OldestXmin, &FreezeXid, &MultiXactCutoff,
971  &num_tuples, &tups_vacuumed,
972  &tups_recently_dead);
973 
974  /* return selected values to caller, get set as relfrozenxid/minmxid */
975  *pFreezeXid = FreezeXid;
976  *pCutoffMulti = MultiXactCutoff;
977 
978  /* Reset rd_toastoid just to be tidy --- it shouldn't be looked at again */
979  NewHeap->rd_toastoid = InvalidOid;
980 
981  num_pages = RelationGetNumberOfBlocks(NewHeap);
982 
983  /* Log what we did */
984  ereport(elevel,
985  (errmsg("\"%s.%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
986  nspname,
987  RelationGetRelationName(OldHeap),
988  tups_vacuumed, num_tuples,
989  RelationGetNumberOfBlocks(OldHeap)),
990  errdetail("%.0f dead row versions cannot be removed yet.\n"
991  "%s.",
992  tups_recently_dead,
993  pg_rusage_show(&ru0))));
994 
995  if (OldIndex != NULL)
996  index_close(OldIndex, NoLock);
997  table_close(OldHeap, NoLock);
998  table_close(NewHeap, NoLock);
999 
1000  /* Update pg_class to reflect the correct values of pages and tuples. */
1001  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1002 
1003  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDNewHeap));
1004  if (!HeapTupleIsValid(reltup))
1005  elog(ERROR, "cache lookup failed for relation %u", OIDNewHeap);
1006  relform = (Form_pg_class) GETSTRUCT(reltup);
1007 
1008  relform->relpages = num_pages;
1009  relform->reltuples = num_tuples;
1010 
1011  /* Don't update the stats for pg_class. See swap_relation_files. */
1012  if (OIDOldHeap != RelationRelationId)
1013  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1014  else
1016 
1017  /* Clean up. */
1018  heap_freetuple(reltup);
1019  table_close(relRelation, RowExclusiveLock);
1020 
1021  /* Make the update visible */
1023 }
1024 
1025 /*
1026  * Swap the physical files of two given relations.
1027  *
1028  * We swap the physical identity (reltablespace, relfilenode) while keeping the
1029  * same logical identities of the two relations. relpersistence is also
1030  * swapped, which is critical since it determines where buffers live for each
1031  * relation.
1032  *
1033  * We can swap associated TOAST data in either of two ways: recursively swap
1034  * the physical content of the toast tables (and their indexes), or swap the
1035  * TOAST links in the given relations' pg_class entries. The former is needed
1036  * to manage rewrites of shared catalogs (where we cannot change the pg_class
1037  * links) while the latter is the only way to handle cases in which a toast
1038  * table is added or removed altogether.
1039  *
1040  * Additionally, the first relation is marked with relfrozenxid set to
1041  * frozenXid. It seems a bit ugly to have this here, but the caller would
1042  * have to do it anyway, so having it here saves a heap_update. Note: in
1043  * the swap-toast-links case, we assume we don't need to change the toast
1044  * table's relfrozenxid: the new version of the toast table should already
1045  * have relfrozenxid set to RecentXmin, which is good enough.
1046  *
1047  * Lastly, if r2 and its toast table and toast index (if any) are mapped,
1048  * their OIDs are emitted into mapped_tables[]. This is hacky but beats
1049  * having to look the information up again later in finish_heap_swap.
1050  */
1051 static void
1052 swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
1053  bool swap_toast_by_content,
1054  bool is_internal,
1055  TransactionId frozenXid,
1056  MultiXactId cutoffMulti,
1057  Oid *mapped_tables)
1058 {
1059  Relation relRelation;
1060  HeapTuple reltup1,
1061  reltup2;
1062  Form_pg_class relform1,
1063  relform2;
1064  Oid relfilenode1,
1065  relfilenode2;
1066  Oid swaptemp;
1067  char swptmpchr;
1068 
1069  /* We need writable copies of both pg_class tuples. */
1070  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1071 
1073  if (!HeapTupleIsValid(reltup1))
1074  elog(ERROR, "cache lookup failed for relation %u", r1);
1075  relform1 = (Form_pg_class) GETSTRUCT(reltup1);
1076 
1078  if (!HeapTupleIsValid(reltup2))
1079  elog(ERROR, "cache lookup failed for relation %u", r2);
1080  relform2 = (Form_pg_class) GETSTRUCT(reltup2);
1081 
1082  relfilenode1 = relform1->relfilenode;
1083  relfilenode2 = relform2->relfilenode;
1084 
1085  if (OidIsValid(relfilenode1) && OidIsValid(relfilenode2))
1086  {
1087  /*
1088  * Normal non-mapped relations: swap relfilenodes, reltablespaces,
1089  * relpersistence
1090  */
1091  Assert(!target_is_pg_class);
1092 
1093  swaptemp = relform1->relfilenode;
1094  relform1->relfilenode = relform2->relfilenode;
1095  relform2->relfilenode = swaptemp;
1096 
1097  swaptemp = relform1->reltablespace;
1098  relform1->reltablespace = relform2->reltablespace;
1099  relform2->reltablespace = swaptemp;
1100 
1101  swaptemp = relform1->relam;
1102  relform1->relam = relform2->relam;
1103  relform2->relam = swaptemp;
1104 
1105  swptmpchr = relform1->relpersistence;
1106  relform1->relpersistence = relform2->relpersistence;
1107  relform2->relpersistence = swptmpchr;
1108 
1109  /* Also swap toast links, if we're swapping by links */
1110  if (!swap_toast_by_content)
1111  {
1112  swaptemp = relform1->reltoastrelid;
1113  relform1->reltoastrelid = relform2->reltoastrelid;
1114  relform2->reltoastrelid = swaptemp;
1115  }
1116  }
1117  else
1118  {
1119  /*
1120  * Mapped-relation case. Here we have to swap the relation mappings
1121  * instead of modifying the pg_class columns. Both must be mapped.
1122  */
1123  if (OidIsValid(relfilenode1) || OidIsValid(relfilenode2))
1124  elog(ERROR, "cannot swap mapped relation \"%s\" with non-mapped relation",
1125  NameStr(relform1->relname));
1126 
1127  /*
1128  * We can't change the tablespace nor persistence of a mapped rel, and
1129  * we can't handle toast link swapping for one either, because we must
1130  * not apply any critical changes to its pg_class row. These cases
1131  * should be prevented by upstream permissions tests, so these checks
1132  * are non-user-facing emergency backstop.
1133  */
1134  if (relform1->reltablespace != relform2->reltablespace)
1135  elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
1136  NameStr(relform1->relname));
1137  if (relform1->relpersistence != relform2->relpersistence)
1138  elog(ERROR, "cannot change persistence of mapped relation \"%s\"",
1139  NameStr(relform1->relname));
1140  if (relform1->relam != relform2->relam)
1141  elog(ERROR, "cannot change access method of mapped relation \"%s\"",
1142  NameStr(relform1->relname));
1143  if (!swap_toast_by_content &&
1144  (relform1->reltoastrelid || relform2->reltoastrelid))
1145  elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
1146  NameStr(relform1->relname));
1147 
1148  /*
1149  * Fetch the mappings --- shouldn't fail, but be paranoid
1150  */
1151  relfilenode1 = RelationMapOidToFilenode(r1, relform1->relisshared);
1152  if (!OidIsValid(relfilenode1))
1153  elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1154  NameStr(relform1->relname), r1);
1155  relfilenode2 = RelationMapOidToFilenode(r2, relform2->relisshared);
1156  if (!OidIsValid(relfilenode2))
1157  elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1158  NameStr(relform2->relname), r2);
1159 
1160  /*
1161  * Send replacement mappings to relmapper. Note these won't actually
1162  * take effect until CommandCounterIncrement.
1163  */
1164  RelationMapUpdateMap(r1, relfilenode2, relform1->relisshared, false);
1165  RelationMapUpdateMap(r2, relfilenode1, relform2->relisshared, false);
1166 
1167  /* Pass OIDs of mapped r2 tables back to caller */
1168  *mapped_tables++ = r2;
1169  }
1170 
1171  /*
1172  * Recognize that rel1's relfilenode (swapped from rel2) is new in this
1173  * subtransaction. The rel2 storage (swapped from rel1) may or may not be
1174  * new.
1175  */
1176  {
1177  Relation rel1,
1178  rel2;
1179 
1180  rel1 = relation_open(r1, NoLock);
1181  rel2 = relation_open(r2, NoLock);
1182  rel2->rd_createSubid = rel1->rd_createSubid;
1186  relation_close(rel1, NoLock);
1187  relation_close(rel2, NoLock);
1188  }
1189 
1190  /*
1191  * In the case of a shared catalog, these next few steps will only affect
1192  * our own database's pg_class row; but that's okay, because they are all
1193  * noncritical updates. That's also an important fact for the case of a
1194  * mapped catalog, because it's possible that we'll commit the map change
1195  * and then fail to commit the pg_class update.
1196  */
1197 
1198  /* set rel1's frozen Xid and minimum MultiXid */
1199  if (relform1->relkind != RELKIND_INDEX)
1200  {
1201  Assert(!TransactionIdIsValid(frozenXid) ||
1202  TransactionIdIsNormal(frozenXid));
1203  relform1->relfrozenxid = frozenXid;
1204  relform1->relminmxid = cutoffMulti;
1205  }
1206 
1207  /* swap size statistics too, since new rel has freshly-updated stats */
1208  {
1209  int32 swap_pages;
1210  float4 swap_tuples;
1211  int32 swap_allvisible;
1212 
1213  swap_pages = relform1->relpages;
1214  relform1->relpages = relform2->relpages;
1215  relform2->relpages = swap_pages;
1216 
1217  swap_tuples = relform1->reltuples;
1218  relform1->reltuples = relform2->reltuples;
1219  relform2->reltuples = swap_tuples;
1220 
1221  swap_allvisible = relform1->relallvisible;
1222  relform1->relallvisible = relform2->relallvisible;
1223  relform2->relallvisible = swap_allvisible;
1224  }
1225 
1226  /*
1227  * Update the tuples in pg_class --- unless the target relation of the
1228  * swap is pg_class itself. In that case, there is zero point in making
1229  * changes because we'd be updating the old data that we're about to throw
1230  * away. Because the real work being done here for a mapped relation is
1231  * just to change the relation map settings, it's all right to not update
1232  * the pg_class rows in this case. The most important changes will instead
1233  * performed later, in finish_heap_swap() itself.
1234  */
1235  if (!target_is_pg_class)
1236  {
1237  CatalogIndexState indstate;
1238 
1239  indstate = CatalogOpenIndexes(relRelation);
1240  CatalogTupleUpdateWithInfo(relRelation, &reltup1->t_self, reltup1,
1241  indstate);
1242  CatalogTupleUpdateWithInfo(relRelation, &reltup2->t_self, reltup2,
1243  indstate);
1244  CatalogCloseIndexes(indstate);
1245  }
1246  else
1247  {
1248  /* no update ... but we do still need relcache inval */
1251  }
1252 
1253  /*
1254  * Post alter hook for modified relations. The change to r2 is always
1255  * internal, but r1 depends on the invocation context.
1256  */
1257  InvokeObjectPostAlterHookArg(RelationRelationId, r1, 0,
1258  InvalidOid, is_internal);
1259  InvokeObjectPostAlterHookArg(RelationRelationId, r2, 0,
1260  InvalidOid, true);
1261 
1262  /*
1263  * If we have toast tables associated with the relations being swapped,
1264  * deal with them too.
1265  */
1266  if (relform1->reltoastrelid || relform2->reltoastrelid)
1267  {
1268  if (swap_toast_by_content)
1269  {
1270  if (relform1->reltoastrelid && relform2->reltoastrelid)
1271  {
1272  /* Recursively swap the contents of the toast tables */
1273  swap_relation_files(relform1->reltoastrelid,
1274  relform2->reltoastrelid,
1275  target_is_pg_class,
1276  swap_toast_by_content,
1277  is_internal,
1278  frozenXid,
1279  cutoffMulti,
1280  mapped_tables);
1281  }
1282  else
1283  {
1284  /* caller messed up */
1285  elog(ERROR, "cannot swap toast files by content when there's only one");
1286  }
1287  }
1288  else
1289  {
1290  /*
1291  * We swapped the ownership links, so we need to change dependency
1292  * data to match.
1293  *
1294  * NOTE: it is possible that only one table has a toast table.
1295  *
1296  * NOTE: at present, a TOAST table's only dependency is the one on
1297  * its owning table. If more are ever created, we'd need to use
1298  * something more selective than deleteDependencyRecordsFor() to
1299  * get rid of just the link we want.
1300  */
1301  ObjectAddress baseobject,
1302  toastobject;
1303  long count;
1304 
1305  /*
1306  * We disallow this case for system catalogs, to avoid the
1307  * possibility that the catalog we're rebuilding is one of the
1308  * ones the dependency changes would change. It's too late to be
1309  * making any data changes to the target catalog.
1310  */
1311  if (IsSystemClass(r1, relform1))
1312  elog(ERROR, "cannot swap toast files by links for system catalogs");
1313 
1314  /* Delete old dependencies */
1315  if (relform1->reltoastrelid)
1316  {
1317  count = deleteDependencyRecordsFor(RelationRelationId,
1318  relform1->reltoastrelid,
1319  false);
1320  if (count != 1)
1321  elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1322  count);
1323  }
1324  if (relform2->reltoastrelid)
1325  {
1326  count = deleteDependencyRecordsFor(RelationRelationId,
1327  relform2->reltoastrelid,
1328  false);
1329  if (count != 1)
1330  elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1331  count);
1332  }
1333 
1334  /* Register new dependencies */
1335  baseobject.classId = RelationRelationId;
1336  baseobject.objectSubId = 0;
1337  toastobject.classId = RelationRelationId;
1338  toastobject.objectSubId = 0;
1339 
1340  if (relform1->reltoastrelid)
1341  {
1342  baseobject.objectId = r1;
1343  toastobject.objectId = relform1->reltoastrelid;
1344  recordDependencyOn(&toastobject, &baseobject,
1346  }
1347 
1348  if (relform2->reltoastrelid)
1349  {
1350  baseobject.objectId = r2;
1351  toastobject.objectId = relform2->reltoastrelid;
1352  recordDependencyOn(&toastobject, &baseobject,
1354  }
1355  }
1356  }
1357 
1358  /*
1359  * If we're swapping two toast tables by content, do the same for their
1360  * valid index. The swap can actually be safely done only if the relations
1361  * have indexes.
1362  */
1363  if (swap_toast_by_content &&
1364  relform1->relkind == RELKIND_TOASTVALUE &&
1365  relform2->relkind == RELKIND_TOASTVALUE)
1366  {
1367  Oid toastIndex1,
1368  toastIndex2;
1369 
1370  /* Get valid index for each relation */
1371  toastIndex1 = toast_get_valid_index(r1,
1373  toastIndex2 = toast_get_valid_index(r2,
1375 
1376  swap_relation_files(toastIndex1,
1377  toastIndex2,
1378  target_is_pg_class,
1379  swap_toast_by_content,
1380  is_internal,
1383  mapped_tables);
1384  }
1385 
1386  /* Clean up. */
1387  heap_freetuple(reltup1);
1388  heap_freetuple(reltup2);
1389 
1390  table_close(relRelation, RowExclusiveLock);
1391 
1392  /*
1393  * Close both relcache entries' smgr links. We need this kluge because
1394  * both links will be invalidated during upcoming CommandCounterIncrement.
1395  * Whichever of the rels is the second to be cleared will have a dangling
1396  * reference to the other's smgr entry. Rather than trying to avoid this
1397  * by ordering operations just so, it's easiest to close the links first.
1398  * (Fortunately, since one of the entries is local in our transaction,
1399  * it's sufficient to clear out our own relcache this way; the problem
1400  * cannot arise for other backends when they see our update on the
1401  * non-transient relation.)
1402  *
1403  * Caution: the placement of this step interacts with the decision to
1404  * handle toast rels by recursion. When we are trying to rebuild pg_class
1405  * itself, the smgr close on pg_class must happen after all accesses in
1406  * this function.
1407  */
1410 }
1411 
1412 /*
1413  * Remove the transient table that was built by make_new_heap, and finish
1414  * cleaning up (including rebuilding all indexes on the old heap).
1415  */
1416 void
1417 finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
1418  bool is_system_catalog,
1419  bool swap_toast_by_content,
1420  bool check_constraints,
1421  bool is_internal,
1422  TransactionId frozenXid,
1423  MultiXactId cutoffMulti,
1424  char newrelpersistence)
1425 {
1426  ObjectAddress object;
1427  Oid mapped_tables[4];
1428  int reindex_flags;
1429  ReindexParams reindex_params = {0};
1430  int i;
1431 
1432  /* Report that we are now swapping relation files */
1435 
1436  /* Zero out possible results from swapped_relation_files */
1437  memset(mapped_tables, 0, sizeof(mapped_tables));
1438 
1439  /*
1440  * Swap the contents of the heap relations (including any toast tables).
1441  * Also set old heap's relfrozenxid to frozenXid.
1442  */
1443  swap_relation_files(OIDOldHeap, OIDNewHeap,
1444  (OIDOldHeap == RelationRelationId),
1445  swap_toast_by_content, is_internal,
1446  frozenXid, cutoffMulti, mapped_tables);
1447 
1448  /*
1449  * If it's a system catalog, queue a sinval message to flush all catcaches
1450  * on the catalog when we reach CommandCounterIncrement.
1451  */
1452  if (is_system_catalog)
1453  CacheInvalidateCatalog(OIDOldHeap);
1454 
1455  /*
1456  * Rebuild each index on the relation (but not the toast table, which is
1457  * all-new at this point). It is important to do this before the DROP
1458  * step because if we are processing a system catalog that will be used
1459  * during DROP, we want to have its indexes available. There is no
1460  * advantage to the other order anyway because this is all transactional,
1461  * so no chance to reclaim disk space before commit. We do not need a
1462  * final CommandCounterIncrement() because reindex_relation does it.
1463  *
1464  * Note: because index_build is called via reindex_relation, it will never
1465  * set indcheckxmin true for the indexes. This is OK even though in some
1466  * sense we are building new indexes rather than rebuilding existing ones,
1467  * because the new heap won't contain any HOT chains at all, let alone
1468  * broken ones, so it can't be necessary to set indcheckxmin.
1469  */
1470  reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE;
1471  if (check_constraints)
1472  reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS;
1473 
1474  /*
1475  * Ensure that the indexes have the same persistence as the parent
1476  * relation.
1477  */
1478  if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1479  reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1480  else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1481  reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
1482 
1483  /* Report that we are now reindexing relations */
1486 
1487  reindex_relation(OIDOldHeap, reindex_flags, &reindex_params);
1488 
1489  /* Report that we are now doing clean up */
1492 
1493  /*
1494  * If the relation being rebuilt is pg_class, swap_relation_files()
1495  * couldn't update pg_class's own pg_class entry (check comments in
1496  * swap_relation_files()), thus relfrozenxid was not updated. That's
1497  * annoying because a potential reason for doing a VACUUM FULL is a
1498  * imminent or actual anti-wraparound shutdown. So, now that we can
1499  * access the new relation using its indices, update relfrozenxid.
1500  * pg_class doesn't have a toast relation, so we don't need to update the
1501  * corresponding toast relation. Not that there's little point moving all
1502  * relfrozenxid updates here since swap_relation_files() needs to write to
1503  * pg_class for non-mapped relations anyway.
1504  */
1505  if (OIDOldHeap == RelationRelationId)
1506  {
1507  Relation relRelation;
1508  HeapTuple reltup;
1509  Form_pg_class relform;
1510 
1511  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1512 
1513  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
1514  if (!HeapTupleIsValid(reltup))
1515  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1516  relform = (Form_pg_class) GETSTRUCT(reltup);
1517 
1518  relform->relfrozenxid = frozenXid;
1519  relform->relminmxid = cutoffMulti;
1520 
1521  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1522 
1523  table_close(relRelation, RowExclusiveLock);
1524  }
1525 
1526  /* Destroy new heap with old filenode */
1527  object.classId = RelationRelationId;
1528  object.objectId = OIDNewHeap;
1529  object.objectSubId = 0;
1530 
1531  /*
1532  * The new relation is local to our transaction and we know nothing
1533  * depends on it, so DROP_RESTRICT should be OK.
1534  */
1536 
1537  /* performDeletion does CommandCounterIncrement at end */
1538 
1539  /*
1540  * Now we must remove any relation mapping entries that we set up for the
1541  * transient table, as well as its toast table and toast index if any. If
1542  * we fail to do this before commit, the relmapper will complain about new
1543  * permanent map entries being added post-bootstrap.
1544  */
1545  for (i = 0; OidIsValid(mapped_tables[i]); i++)
1546  RelationMapRemoveMapping(mapped_tables[i]);
1547 
1548  /*
1549  * At this point, everything is kosher except that, if we did toast swap
1550  * by links, the toast table's name corresponds to the transient table.
1551  * The name is irrelevant to the backend because it's referenced by OID,
1552  * but users looking at the catalogs could be confused. Rename it to
1553  * prevent this problem.
1554  *
1555  * Note no lock required on the relation, because we already hold an
1556  * exclusive lock on it.
1557  */
1558  if (!swap_toast_by_content)
1559  {
1560  Relation newrel;
1561 
1562  newrel = table_open(OIDOldHeap, NoLock);
1563  if (OidIsValid(newrel->rd_rel->reltoastrelid))
1564  {
1565  Oid toastidx;
1566  char NewToastName[NAMEDATALEN];
1567 
1568  /* Get the associated valid index to be renamed */
1569  toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
1570  NoLock);
1571 
1572  /* rename the toast table ... */
1573  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
1574  OIDOldHeap);
1575  RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1576  NewToastName, true, false);
1577 
1578  /* ... and its valid index too. */
1579  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
1580  OIDOldHeap);
1581 
1582  RenameRelationInternal(toastidx,
1583  NewToastName, true, true);
1584 
1585  /*
1586  * Reset the relrewrite for the toast. The command-counter
1587  * increment is required here as we are about to update the tuple
1588  * that is updated as part of RenameRelationInternal.
1589  */
1591  ResetRelRewrite(newrel->rd_rel->reltoastrelid);
1592  }
1593  relation_close(newrel, NoLock);
1594  }
1595 
1596  /* if it's not a catalog table, clear any missing attribute settings */
1597  if (!is_system_catalog)
1598  {
1599  Relation newrel;
1600 
1601  newrel = table_open(OIDOldHeap, NoLock);
1602  RelationClearMissing(newrel);
1603  relation_close(newrel, NoLock);
1604  }
1605 }
1606 
1607 
1608 /*
1609  * Get a list of tables that the current user owns and
1610  * have indisclustered set. Return the list in a List * of RelToCluster
1611  * (stored in the specified memory context), each one giving the tableOid
1612  * and the indexOid on which the table is already clustered.
1613  */
1614 static List *
1616 {
1617  Relation indRelation;
1618  TableScanDesc scan;
1619  ScanKeyData entry;
1620  HeapTuple indexTuple;
1622  MemoryContext old_context;
1623  List *rtcs = NIL;
1624 
1625  /*
1626  * Get all indexes that have indisclustered set and are owned by
1627  * appropriate user.
1628  */
1629  indRelation = table_open(IndexRelationId, AccessShareLock);
1630  ScanKeyInit(&entry,
1631  Anum_pg_index_indisclustered,
1632  BTEqualStrategyNumber, F_BOOLEQ,
1633  BoolGetDatum(true));
1634  scan = table_beginscan_catalog(indRelation, 1, &entry);
1635  while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
1636  {
1637  RelToCluster *rtc;
1638 
1639  index = (Form_pg_index) GETSTRUCT(indexTuple);
1640 
1641  if (!pg_class_ownercheck(index->indrelid, GetUserId()))
1642  continue;
1643 
1644  /* Use a permanent memory context for the result list */
1645  old_context = MemoryContextSwitchTo(cluster_context);
1646 
1647  rtc = (RelToCluster *) palloc(sizeof(RelToCluster));
1648  rtc->tableOid = index->indrelid;
1649  rtc->indexOid = index->indexrelid;
1650  rtcs = lappend(rtcs, rtc);
1651 
1652  MemoryContextSwitchTo(old_context);
1653  }
1654  table_endscan(scan);
1655 
1656  relation_close(indRelation, AccessShareLock);
1657 
1658  return rtcs;
1659 }
1660 
1661 /*
1662  * Given an index on a partitioned table, return a list of RelToCluster for
1663  * all the children leaves tables/indexes.
1664  *
1665  * Like expand_vacuum_rel, but here caller must hold AccessExclusiveLock
1666  * on the table containing the index.
1667  */
1668 static List *
1670 {
1671  List *inhoids;
1672  ListCell *lc;
1673  List *rtcs = NIL;
1674  MemoryContext old_context;
1675 
1676  /* Do not lock the children until they're processed */
1677  inhoids = find_all_inheritors(indexOid, NoLock, NULL);
1678 
1679  foreach(lc, inhoids)
1680  {
1681  Oid indexrelid = lfirst_oid(lc);
1682  Oid relid = IndexGetRelation(indexrelid, false);
1683  RelToCluster *rtc;
1684 
1685  /* consider only leaf indexes */
1686  if (get_rel_relkind(indexrelid) != RELKIND_INDEX)
1687  continue;
1688 
1689  /* Silently skip partitions which the user has no access to. */
1690  if (!pg_class_ownercheck(relid, GetUserId()) &&
1692  IsSharedRelation(relid)))
1693  continue;
1694 
1695  /* Use a permanent memory context for the result list */
1696  old_context = MemoryContextSwitchTo(cluster_context);
1697 
1698  rtc = (RelToCluster *) palloc(sizeof(RelToCluster));
1699  rtc->tableOid = relid;
1700  rtc->indexOid = indexrelid;
1701  rtcs = lappend(rtcs, rtc);
1702 
1703  MemoryContextSwitchTo(old_context);
1704  }
1705 
1706  return rtcs;
1707 }
bool pg_database_ownercheck(Oid db_oid, Oid roleid)
Definition: aclchk.c:5589
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:5171
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:216
#define NameStr(name)
Definition: c.h:681
signed int int32
Definition: c.h:429
TransactionId MultiXactId
Definition: c.h:597
float float4
Definition: c.h:564
uint32 TransactionId
Definition: c.h:587
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:155
#define OidIsValid(objectId)
Definition: c.h:710
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:310
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:498
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:1417
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1615
static List * get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
Definition: cluster.c:1669
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:631
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
Definition: cluster.c:812
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:686
void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
Definition: cluster.c:108
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:558
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:1052
#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:317
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:135
int errdetail(const char *fmt,...)
Definition: elog.c:1037
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define DEBUG2
Definition: elog.h:23
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define INFO
Definition: elog.h:28
#define ereport(elevel,...)
Definition: elog.h:143
Oid MyDatabaseId
Definition: globals.c:89
int NewGUCNestLevel(void)
Definition: guc.c:6467
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:6481
void RelationClearMissing(Relation rel)
Definition: heap.c:1939
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:1088
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1296
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:359
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3520
bool reindex_relation(Oid relid, int flags, ReindexParams *params)
Definition: index.c:3859
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition: index.h:158
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition: index.h:156
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition: index.h:159
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:157
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
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:324
void CacheInvalidateCatalog(Oid catalogId)
Definition: inval.c:1339
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1399
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:336
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:3326
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:1984
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3565
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1866
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
void * palloc(Size size)
Definition: mcxt.c:1068
MemoryContext PortalContext
Definition: mcxt.c:57
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:313
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:603
Oid GetUserId(void)
Definition: miscinit.c:492
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:610
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition: multixact.c:3159
#define MultiXactIdIsValid(multi)
Definition: multixact.h:28
#define InvalidMultiXactId
Definition: multixact.h:24
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2982
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:238
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:198
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:110
@ DROP_RESTRICT
Definition: parsenodes.h:2207
static int verbose
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 deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:243
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:169
#define NIL
Definition: pg_list.h:65
#define lfirst_oid(lc)
Definition: pg_list.h:171
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:5962
#define snprintf
Definition: port.h:225
uintptr_t Datum
Definition: postgres.h:411
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define BoolGetDatum(X)
Definition: postgres.h:446
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3148
@ ONCOMMIT_NOOP
Definition: primnodes.h:49
#define PROGRESS_CLUSTER_PHASE
Definition: progress.h:56
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:75
#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX
Definition: progress.h:70
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:74
#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP
Definition: progress.h:71
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:55
#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES
Definition: progress.h:69
#define RelationGetRelid(relation)
Definition: rel.h:489
#define RelationGetDescr(relation)
Definition: rel.h:515
#define RelationIsMapped(relation)
Definition: rel.h:538
#define RelationGetRelationName(relation)
Definition: rel.h:523
#define RelationIsPopulated(relation)
Definition: rel.h:662
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:643
#define RelationGetNamespace(relation)
Definition: rel.h:530
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4675
void RelationCloseSmgrByOid(Oid relationId)
Definition: relcache.c:3033
void RelationAssumeNewRelfilenode(Relation relation)
Definition: relcache.c:3844
void RelationMapRemoveMapping(Oid relationId)
Definition: relmapper.c:432
void RelationMapUpdateMap(Oid relationId, Oid fileNode, bool shared, bool immediate)
Definition: relmapper.c:320
Oid RelationMapOidToFilenode(Oid relationId, bool shared)
Definition: relmapper.c:161
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:26
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
void PopActiveSnapshot(void)
Definition: snapmgr.c:776
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:682
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 * indexname
Definition: parsenodes.h:3699
RangeVar * relation
Definition: parsenodes.h:3698
List * params
Definition: parsenodes.h:3700
char * defname
Definition: parsenodes.h:765
int location
Definition: parsenodes.h:769
ItemPointerData t_self
Definition: htup.h:65
bool amclusterable
Definition: amapi.h:238
Definition: pg_list.h:51
char * relname
Definition: primnodes.h:68
Oid indexOid
Definition: cluster.c:69
Oid tableOid
Definition: cluster.c:68
Oid rd_toastoid
Definition: rel.h:247
struct IndexAmRoutine * rd_indam
Definition: rel.h:202
SubTransactionId rd_newRelfilenodeSubid
Definition: rel.h:103
SubTransactionId rd_firstRelfilenodeSubid
Definition: rel.h:105
struct HeapTupleData * rd_indextuple
Definition: rel.h:190
Form_pg_index rd_index
Definition: rel.h:188
SubTransactionId rd_createSubid
Definition: rel.h:102
Form_pg_class rd_rel
Definition: rel.h:109
Definition: type.h:90
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1221
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1173
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1434
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:179
@ INDEXRELID
Definition: syscache.h:66
@ RELOID
Definition: syscache.h:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:188
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:993
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:1648
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:3936
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:16923
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3989
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:3846
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:300
#define InvalidTransactionId
Definition: transam.h:31
#define TransactionIdIsValid(xid)
Definition: transam.h:41
#define TransactionIdIsNormal(xid)
Definition: transam.h:42
bool vacuum_set_xid_limits(Relation rel, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, TransactionId *oldestXmin, MultiXactId *oldestMxact, TransactionId *freezeLimit, MultiXactId *multiXactCutoff)
Definition: vacuum.c:959
void CommandCounterIncrement(void)
Definition: xact.c:1074
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3462
void StartTransactionCommand(void)
Definition: xact.c:2925
void CommitTransactionCommand(void)
Definition: xact.c:3022