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