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