PostgreSQL Source Code git master
Loading...
Searching...
No Matches
repack.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * repack.c
4 * REPACK a table; formerly known as CLUSTER. VACUUM FULL also uses
5 * parts of this code.
6 *
7 * There are two somewhat different ways to rewrite a table. In non-
8 * concurrent mode, it's easy: take AccessExclusiveLock, create a new
9 * transient relation, copy the tuples over to the relfilenode of the new
10 * relation, swap the relfilenodes, then drop the old relation.
11 *
12 * In concurrent mode, we lock the table with only ShareUpdateExclusiveLock,
13 * then do an initial copy as above. However, while the tuples are being
14 * copied, concurrent transactions could modify the table. To cope with those
15 * changes, we rely on logical decoding to obtain them from WAL. A bgworker
16 * consumes WAL while the initial copy is ongoing (to prevent excessive WAL
17 * from being reserved), and accumulates the changes in a file. Once the
18 * initial copy is complete, we read the changes from the file and re-apply
19 * them on the new heap. Then we upgrade our ShareUpdateExclusiveLock to
20 * AccessExclusiveLock and swap the relfilenodes. This way, the time we hold
21 * a strong lock on the table is much reduced, and the bloat is eliminated.
22 *
23 *
24 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
25 * Portions Copyright (c) 1994-5, Regents of the University of California
26 *
27 *
28 * IDENTIFICATION
29 * src/backend/commands/repack.c
30 *
31 *-------------------------------------------------------------------------
32 */
33#include "postgres.h"
34
35#include "access/amapi.h"
36#include "access/heapam.h"
37#include "access/multixact.h"
38#include "access/relscan.h"
39#include "access/tableam.h"
41#include "access/transam.h"
42#include "access/xact.h"
43#include "access/xlog.h"
44#include "catalog/catalog.h"
45#include "catalog/dependency.h"
46#include "catalog/heap.h"
47#include "catalog/index.h"
48#include "catalog/namespace.h"
50#include "catalog/pg_am.h"
51#include "catalog/pg_attrdef.h"
53#include "catalog/pg_inherits.h"
54#include "catalog/toasting.h"
55#include "commands/defrem.h"
56#include "commands/progress.h"
57#include "commands/repack.h"
59#include "commands/tablecmds.h"
60#include "commands/vacuum.h"
61#include "executor/executor.h"
62#include "libpq/pqformat.h"
63#include "libpq/pqmq.h"
64#include "miscadmin.h"
65#include "optimizer/optimizer.h"
67#include "pgstat.h"
69#include "storage/bufmgr.h"
70#include "storage/ipc.h"
71#include "storage/lmgr.h"
72#include "storage/predicate.h"
73#include "storage/proc.h"
74#include "utils/acl.h"
75#include "utils/fmgroids.h"
76#include "utils/guc.h"
78#include "utils/inval.h"
79#include "utils/lsyscache.h"
80#include "utils/memutils.h"
81#include "utils/pg_rusage.h"
82#include "utils/relmapper.h"
83#include "utils/snapmgr.h"
84#include "utils/syscache.h"
85#include "utils/wait_event_types.h"
86
87/*
88 * This struct is used to pass around the information on tables to be
89 * clustered. We need this so we can make a list of them when invoked without
90 * a specific table/index pair.
91 */
92typedef struct
93{
97
98/*
99 * The first file exported by the decoding worker must contain a snapshot, the
100 * following ones contain the data changes.
101 */
102#define WORKER_FILE_SNAPSHOT 0
103
104/*
105 * Information needed to apply concurrent data changes.
106 */
107typedef struct ChangeContext
108{
109 /* The relation the changes are applied to. */
111
112 /* Needed to update indexes of cc_rel. */
115
116 /*
117 * Existing tuples to UPDATE and DELETE are located via this index. We
118 * keep the scankey in partially initialized state to avoid repeated work.
119 * sk_argument is completed on the fly.
120 */
124
125 /* The latest column we need to deform to have the tuple identity */
127
128 /* Sequential number of the file containing the changes. */
131
132/*
133 * Backend-local information to control the decoding worker.
134 */
135typedef struct DecodingWorker
136{
137 /* The worker. */
139
140 /* DecodingWorkerShared is in this segment. */
142
143 /* Handle of the error queue. */
146
147/* Pointer to currently running decoding worker. */
149
150/*
151 * Is there a message sent by a repack worker that the backend needs to
152 * receive?
153 */
155
156static LOCKMODE RepackLockLevel(bool concurrent);
158 Oid indexOid, Oid userid, LOCKMODE lmode,
159 int options);
163 Oid ident_idx);
165 Snapshot snapshot,
166 bool verbose,
170static List *get_tables_to_repack(RepackCommand cmd, bool usingindex,
173 Oid relid, bool rel_is_index,
176 Oid relid, Oid userid);
177
179static void apply_concurrent_insert(Relation rel, TupleTableSlot *slot,
184static void apply_concurrent_delete(Relation rel, TupleTableSlot *slot);
185static void restore_tuple(BufFile *file, Relation relation,
186 TupleTableSlot *slot);
187static void adjust_toast_pointers(Relation relation, TupleTableSlot *dest,
188 TupleTableSlot *src);
190 TupleTableSlot *locator,
193 TupleTableSlot *locator,
195static void process_concurrent_changes(XLogRecPtr end_of_wal,
197 bool done);
199 Relation relation,
211 LOCKMODE lockmode,
212 bool isTopLevel,
213 ClusterParams *params);
214static Oid determine_clustered_index(Relation rel, bool usingindex,
215 const char *indexname);
216
217static void start_repack_decoding_worker(Oid relid);
218static void stop_repack_decoding_worker(void);
219static void stop_repack_decoding_worker_cb(int code, Datum arg);
221
222static void ProcessRepackMessage(StringInfo msg);
223static const char *RepackCommandAsString(RepackCommand cmd);
224
225
226/*
227 * The repack code allows for processing multiple tables at once. Because
228 * of this, we cannot just run everything on a single transaction, or we
229 * would be forced to acquire exclusive locks on all the tables being
230 * clustered, simultaneously --- very likely leading to deadlock.
231 *
232 * To solve this we follow a similar strategy to VACUUM code, processing each
233 * relation in a separate transaction. For this to work, we need to:
234 *
235 * - provide a separate memory context so that we can pass information in
236 * a way that survives across transactions
237 * - start a new transaction every time a new relation is clustered
238 * - check for validity of the information on to-be-clustered relations,
239 * as someone might have deleted a relation behind our back, or
240 * clustered one on a different index
241 * - end the transaction
242 *
243 * The single-relation case does not have any such overhead.
244 *
245 * We also allow a relation to be repacked following an index, but without
246 * naming a specific one. In that case, the indisclustered bit will be
247 * looked up, and an ERROR will be thrown if no so-marked index is found.
248 */
249void
251{
252 ClusterParams params = {0};
253 Relation rel = NULL;
255 LOCKMODE lockmode;
256 List *rtcs;
257 bool verbose = false;
258 bool analyze = false;
259 bool concurrently = false;
260
261 /* Parse option list */
262 foreach_node(DefElem, opt, stmt->params)
263 {
264 if (strcmp(opt->defname, "verbose") == 0)
265 verbose = defGetBoolean(opt);
266 else if (strcmp(opt->defname, "analyze") == 0 ||
267 strcmp(opt->defname, "analyse") == 0)
268 analyze = defGetBoolean(opt);
269 else if (strcmp(opt->defname, "concurrently") == 0)
270 {
271 if (stmt->command != REPACK_COMMAND_REPACK)
274 errmsg("CONCURRENTLY option not supported for %s",
275 RepackCommandAsString(stmt->command)));
277 }
278 else
281 errmsg("unrecognized %s option \"%s\"",
282 RepackCommandAsString(stmt->command),
283 opt->defname),
284 parser_errposition(pstate, opt->location));
285 }
286
287 params.options |=
288 (verbose ? CLUOPT_VERBOSE : 0) |
289 (analyze ? CLUOPT_ANALYZE : 0) |
291
292 /* Determine the lock mode to use. */
293 lockmode = RepackLockLevel((params.options & CLUOPT_CONCURRENT) != 0);
294
295 if ((params.options & CLUOPT_CONCURRENT) != 0)
296 {
297 /*
298 * Make sure we're not in a transaction block.
299 *
300 * The reason is that repack_setup_logical_decoding() could wait
301 * indefinitely for our XID to complete. (The deadlock detector would
302 * not recognize it because we'd be waiting for ourselves, i.e. no
303 * real lock conflict.) It would be possible to run in a transaction
304 * block if we had no XID, but this restriction is simpler for users
305 * to understand and we don't lose any functionality.
306 */
307 PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)");
308 }
309
310 /*
311 * If a single relation is specified, process it and we're done ... unless
312 * the relation is a partitioned table, in which case we fall through.
313 */
314 if (stmt->relation != NULL)
315 {
316 rel = process_single_relation(stmt, lockmode, isTopLevel, &params);
317 if (rel == NULL)
318 return; /* all done */
319 }
320
321 /*
322 * Don't allow ANALYZE in the multiple-relation case for now. Maybe we
323 * can add support for this later.
324 */
325 if (params.options & CLUOPT_ANALYZE)
328 errmsg("cannot execute %s on multiple tables",
329 "REPACK (ANALYZE)"));
330
331 /*
332 * By here, we know we are in a multi-table situation.
333 *
334 * Concurrent processing is currently considered rather special (e.g. in
335 * terms of resources consumed) so it is not performed in bulk.
336 */
337 if (params.options & CLUOPT_CONCURRENT)
338 {
339 if (rel != NULL)
340 {
341 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
344 errmsg("%s is not supported for partitioned tables",
345 "REPACK (CONCURRENTLY)"),
346 errhint("Consider running the command on individual partitions."));
347 }
348 else
351 errmsg("%s requires an explicit table name",
352 "REPACK (CONCURRENTLY)"));
353 }
354
355 /*
356 * In order to avoid holding locks for too long, we want to process each
357 * table in its own transaction. This forces us to disallow running
358 * inside a user transaction block.
359 */
361
362 /* Also, we need a memory context to hold our list of relations */
364 "Repack",
366
367 /*
368 * Since we open a new transaction for each relation, we have to check
369 * that the relation still is what we think it is.
370 *
371 * In single-transaction CLUSTER, we don't need the overhead.
372 */
373 params.options |= CLUOPT_RECHECK;
374
375 /*
376 * If we don't have a relation yet, determine a relation list. If we do,
377 * then it must be a partitioned table, and we want to process its
378 * partitions. Note that we don't acquire any locks on these tables, so
379 * the returned list must be treated with suspicion.
380 */
381 if (rel == NULL)
382 {
383 Assert(stmt->indexname == NULL);
384 rtcs = get_tables_to_repack(stmt->command, stmt->usingindex,
387 }
388 else
389 {
390 Oid relid;
391 bool rel_is_index;
392
393 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
394
395 /*
396 * If USING INDEX was specified, resolve the index name now and pass
397 * it down.
398 */
399 if (stmt->usingindex)
400 {
401 /*
402 * If no index name was specified when repacking a partitioned
403 * table, punt for now. Maybe we can improve this later.
404 */
405 if (!stmt->indexname)
406 {
407 if (stmt->command == REPACK_COMMAND_CLUSTER)
410 errmsg("there is no previously clustered index for table \"%s\"",
412 else
415 /*- translator: first %s is name of a SQL command, eg. REPACK */
416 errmsg("cannot execute %s on partitioned table \"%s\" USING INDEX with no index name",
417 RepackCommandAsString(stmt->command),
419 }
420
421 relid = determine_clustered_index(rel, stmt->usingindex,
422 stmt->indexname);
423 if (!OidIsValid(relid))
424 elog(ERROR, "unable to determine index to cluster on");
426
427 rel_is_index = true;
428 }
429 else
430 {
431 relid = RelationGetRelid(rel);
432 rel_is_index = false;
433 }
434
436 relid, rel_is_index,
438
439 /* close parent relation, releasing lock on it */
441 rel = NULL;
442 }
443
444 /* Commit to get out of starting transaction */
447
448 /* Cluster the tables, each in a separate transaction */
449 Assert(rel == NULL);
451 {
452 /* Start a new transaction for each relation. */
454
455 /*
456 * Open the target table. It may have been dropped or replaced with
457 * something different, in which case silently skip it.
458 */
459 rel = try_relation_open(rtc->tableOid, lockmode);
460 if (rel == NULL)
461 {
463 continue;
464 }
465 if (rel->rd_rel->relkind != RELKIND_RELATION &&
466 rel->rd_rel->relkind != RELKIND_MATVIEW)
467 {
468 relation_close(rel, lockmode);
470 continue;
471 }
472
473 /* functions in indexes may want a snapshot set */
475
476 /* Process this table */
477 cluster_rel(stmt->command, rel, rtc->indexOid, &params, isTopLevel);
478 /* cluster_rel closes the relation, but keeps lock */
479
482 }
483
484 /* Start a new transaction for the cleanup work. */
486
487 /* Clean up working storage */
489}
490
491/*
492 * In the non-concurrent case, we obtain AccessExclusiveLock throughout the
493 * operation to avoid any lock-upgrade hazards. In the concurrent case, we
494 * grab ShareUpdateExclusiveLock (just like VACUUM) for most of the
495 * processing and only acquire AccessExclusiveLock at the end, to swap the
496 * relation -- supposedly for a short time.
497 */
498static LOCKMODE
499RepackLockLevel(bool concurrent)
500{
501 if (concurrent)
503 else
504 return AccessExclusiveLock;
505}
506
507/*
508 * cluster_rel
509 *
510 * This clusters the table by creating a new, clustered table and
511 * swapping the relfilenumbers of the new table and the old table, so
512 * the OID of the original table is preserved. Thus we do not lose
513 * GRANT, inheritance nor references to this table.
514 *
515 * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading
516 * the new table, it's better to create the indexes afterwards than to fill
517 * them incrementally while we load the table.
518 *
519 * If indexOid is InvalidOid, the table will be rewritten in physical order
520 * instead of index order.
521 *
522 * Note that, in the concurrent case, the function releases the lock at some
523 * point, in order to get AccessExclusiveLock for the final steps (i.e. to
524 * swap the relation files). To make things simpler, the caller should expect
525 * OldHeap to be closed on return, regardless CLUOPT_CONCURRENT. (The
526 * AccessExclusiveLock is kept till the end of the transaction.)
527 *
528 * 'cmd' indicates which command is being executed, to be used for error
529 * messages.
530 */
531void
533 ClusterParams *params, bool isTopLevel)
534{
535 Oid tableOid = RelationGetRelid(OldHeap);
538 Oid save_userid;
539 int save_sec_context;
540 int save_nestlevel;
541 bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
542 bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
543 bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0);
545
546 /* Determine the lock mode to use. */
547 lmode = RepackLockLevel(concurrent);
548
549 /*
550 * Check some preconditions in the concurrent case. This also obtains the
551 * replica index OID.
552 */
553 if (concurrent)
555
556 /* Check for user-requested abort. */
558
561
562 /*
563 * Switch to the table owner's userid, so that any index functions are run
564 * as that user. Also lock down security-restricted operations and
565 * arrange to make GUC variable changes local to this command.
566 */
567 GetUserIdAndSecContext(&save_userid, &save_sec_context);
568 SetUserIdAndSecContext(OldHeap->rd_rel->relowner,
569 save_sec_context | SECURITY_RESTRICTED_OPERATION);
570 save_nestlevel = NewGUCNestLevel();
572
573 /*
574 * Recheck that the relation is still what it was when we started.
575 *
576 * Note that it's critical to skip this in single-relation CLUSTER;
577 * otherwise, we would reject an attempt to cluster using a
578 * not-previously-clustered index.
579 */
580 if (recheck &&
581 !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid,
582 lmode, params->options))
583 goto out;
584
585 /*
586 * We allow repacking shared catalogs only when not using an index. It
587 * would work to use an index in most respects, but the index would only
588 * get marked as indisclustered in the current database, leading to
589 * unexpected behavior if CLUSTER were later invoked in another database.
590 */
591 if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
594 /*- translator: first %s is name of a SQL command, eg. REPACK */
595 errmsg("cannot execute %s on a shared catalog",
597
598 /*
599 * The CONCURRENTLY case should have been rejected earlier because it does
600 * not support system catalogs.
601 */
602 Assert(!(OldHeap->rd_rel->relisshared && concurrent));
603
604 /*
605 * Don't process temp tables of other backends ... their local buffer
606 * manager is not going to cope.
607 */
611 /*- translator: first %s is name of a SQL command, eg. REPACK */
612 errmsg("cannot execute %s on temporary tables of other sessions",
614
615 /*
616 * Also check for active uses of the relation in the current transaction,
617 * including open scans and pending AFTER trigger events.
618 */
620
621 /* Check heap and index are valid to cluster on */
622 if (OidIsValid(indexOid))
623 {
624 /* verify the index is good and lock it */
626 /* also open it */
627 index = index_open(indexOid, NoLock);
628 }
629 else
630 index = NULL;
631
632 /*
633 * When allow_system_table_mods is turned off, we disallow repacking a
634 * catalog on a particular index unless that's already the clustered index
635 * for that catalog.
636 *
637 * XXX We don't check for this in CLUSTER, because it's historically been
638 * allowed.
639 */
640 if (cmd != REPACK_COMMAND_CLUSTER &&
641 !allowSystemTableMods && OidIsValid(indexOid) &&
642 IsCatalogRelation(OldHeap) && !index->rd_index->indisclustered)
645 errmsg("permission denied: \"%s\" is a system catalog",
647 errdetail("System catalogs can only be clustered by the index they're already clustered on, if any, unless \"%s\" is enabled.",
648 "allow_system_table_mods"));
649
650 /*
651 * Quietly ignore the request if this is a materialized view which has not
652 * been populated from its query. No harm is done because there is no data
653 * to deal with, and we don't want to throw an error if this is part of a
654 * multi-relation request -- for example, CLUSTER was run on the entire
655 * database.
656 */
657 if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
659 {
660 if (index)
663 goto out;
664 }
665
666 Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
667 OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
668 OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
669
670 /*
671 * All predicate locks on the tuples or pages are about to be made
672 * invalid, because we move tuples around. Promote them to relation
673 * locks. Predicate locks on indexes will be promoted when they are
674 * reindexed.
675 *
676 * During concurrent processing, the heap as well as its indexes stay in
677 * operation, so we postpone this step until they are locked using
678 * AccessExclusiveLock near the end of the processing.
679 */
680 if (!concurrent)
682
683 /*
684 * rebuild_relation does all the dirty work, and closes OldHeap and index,
685 * if valid.
686 *
687 * In concurrent mode, make sure the worker terminates; normally it does
688 * so by itself, but a PG_ENSURE_ERROR_CLEANUP callback ensures that this
689 * happens even in case this backend dies early on a FATAL exit. Normal
690 * mode doesn't need that overhead.
691 */
692 if (concurrent)
693 {
695 {
697 }
700 }
701 else
703
704out:
705 /* Roll back any GUC changes executed by index functions */
706 AtEOXact_GUC(false, save_nestlevel);
707
708 /* Restore userid and security context */
709 SetUserIdAndSecContext(save_userid, save_sec_context);
710
712}
713
714/*
715 * Check if the table (and its index) still meets the requirements of
716 * cluster_rel().
717 */
718static bool
720 Oid userid, LOCKMODE lmode, int options)
721{
722 Oid tableOid = RelationGetRelid(OldHeap);
723
725
726 /* Check that the user still has privileges for the relation */
727 if (!repack_is_permitted_for_relation(cmd, tableOid, userid))
728 {
730 return false;
731 }
732
733 /*
734 * Silently skip a temp table for a remote session. Only doing this check
735 * in the "recheck" case is appropriate (which currently means somebody is
736 * executing a database-wide CLUSTER or on a partitioned table), because
737 * there is another check in cluster() which will stop any attempt to
738 * cluster remote temp tables by name. There is another check in
739 * cluster_rel which is redundant, but we leave it for extra safety.
740 */
742 {
744 return false;
745 }
746
747 if (OidIsValid(indexOid))
748 {
749 /*
750 * Check that the index still exists
751 */
753 {
755 return false;
756 }
757
758 /*
759 * Check that the index is still the one with indisclustered set, if
760 * needed.
761 */
762 if ((options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
763 !get_index_isclustered(indexOid))
764 {
766 return false;
767 }
768 }
769
770 return true;
771}
772
773/*
774 * Verify that the specified heap and index are valid to cluster on
775 *
776 * Side effect: obtains lock on the index. The caller may
777 * in some cases already have a lock of the same strength on the table, but
778 * not in all cases so we can't rely on the table-level lock for
779 * protection here.
780 */
781void
783{
785
786 OldIndex = index_open(indexOid, lockmode);
787
788 /*
789 * Check that index is in fact an index on the given relation
790 */
791 if (OldIndex->rd_index == NULL ||
792 OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
795 errmsg("\"%s\" is not an index for table \"%s\"",
798
799 /* Index AM must allow clustering */
800 if (!OldIndex->rd_indam->amclusterable)
803 errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
805
806 /*
807 * Disallow clustering on incomplete indexes (those that might not index
808 * every row of the relation). We could relax this by making a separate
809 * seqscan pass over the table to copy the missing rows, but that seems
810 * expensive and tedious.
811 */
812 if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
815 errmsg("cannot cluster on partial index \"%s\"",
817
818 /*
819 * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
820 * it might well not contain entries for every heap row, or might not even
821 * be internally consistent. (But note that we don't check indcheckxmin;
822 * the worst consequence of following broken HOT chains would be that we
823 * might put recently-dead tuples out-of-order in the new table, and there
824 * is little harm in that.)
825 */
826 if (!OldIndex->rd_index->indisvalid)
829 errmsg("cannot cluster on invalid index \"%s\"",
831
832 /* Drop relcache refcnt on OldIndex, but keep lock */
834}
835
836/*
837 * mark_index_clustered: mark the specified index as the one clustered on
838 *
839 * With indexOid == InvalidOid, will mark all indexes of rel not-clustered.
840 */
841void
842mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
843{
848
849 Assert(rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE);
850
851 /*
852 * If the index is already marked clustered, no need to do anything.
853 */
854 if (OidIsValid(indexOid))
855 {
856 if (get_index_isclustered(indexOid))
857 return;
858 }
859
860 /*
861 * Check each index of the relation and set/clear the bit as needed.
862 */
864
865 foreach(index, RelationGetIndexList(rel))
866 {
868
872 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
874
875 /*
876 * Unset the bit if set. We know it's wrong because we checked this
877 * earlier.
878 */
879 if (indexForm->indisclustered)
880 {
881 indexForm->indisclustered = false;
883 }
884 else if (thisIndexOid == indexOid)
885 {
886 /* this was checked earlier, but let's be real sure */
887 if (!indexForm->indisvalid)
888 elog(ERROR, "cannot cluster on invalid index %u", indexOid);
889 indexForm->indisclustered = true;
891 }
892
894 InvalidOid, is_internal);
895
897 }
898
900}
901
902/*
903 * Check if the CONCURRENTLY option is legal for the relation.
904 *
905 * *Ident_idx_p receives OID of the identity index.
906 */
907static void
909{
910 char relpersistence,
911 replident;
913
917 errmsg("cannot execute %s in this configuration",
918 "REPACK (CONCURRENTLY)"),
919 errdetail("%s requires \"wal_level\" to be set to \"replica\" or higher.",
920 "REPACK (CONCURRENTLY)"));
921
922 /* Data changes in system relations are not logically decoded. */
923 if (IsCatalogRelation(rel))
926 errmsg("cannot execute %s on relation \"%s\"",
927 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
928 errhint("%s is not supported for catalog relations.",
929 "REPACK (CONCURRENTLY)"));
930
931 /*
932 * reorderbuffer.c does not seem to handle processing of TOAST relation
933 * alone.
934 */
935 if (IsToastRelation(rel))
938 errmsg("cannot execute %s on relation \"%s\"",
939 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
940 errhint("%s is not supported for TOAST relations.",
941 "REPACK (CONCURRENTLY)"));
942
943 relpersistence = rel->rd_rel->relpersistence;
944 if (relpersistence != RELPERSISTENCE_PERMANENT)
947 errmsg("cannot execute %s on relation \"%s\"",
948 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
949 errhint("%s is only allowed for permanent relations.",
950 "REPACK (CONCURRENTLY)"));
951
952 /*
953 * With NOTHING, WAL does not contain the old tuple; FULL is not yet
954 * supported.
955 */
956 replident = rel->rd_rel->relreplident;
957 if (replident == REPLICA_IDENTITY_NOTHING ||
958 replident == REPLICA_IDENTITY_FULL)
961 errmsg("cannot execute %s on relation \"%s\"",
962 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
963 errdetail("%s does not support tables with %s.",
964 "REPACK (CONCURRENTLY)",
965 replident == REPLICA_IDENTITY_NOTHING ?
966 "REPLICA IDENTITY NOTHING" : "REPLICA IDENTITY FULL"));
967
968 /*
969 * Obtain the replica identity index -- either one that has been set
970 * explicitly, or a non-deferrable primary key. If none of these cases
971 * apply, the table cannot be repacked concurrently. It might be possible
972 * to have repack work with a FULL replica identity; however that requires
973 * more work and is not implemented yet.
974 */
976 if (!OidIsValid(ident_idx))
977 {
978 /* This special case warrants its own error message */
979 if (OidIsValid(rel->rd_pkindex) && rel->rd_ispkdeferrable)
982 errmsg("cannot execute %s on relation \"%s\"",
983 "REPACK (CONCURRENTLY)",
985 errdetail("%s does not support deferrable primary keys.",
986 "REPACK (CONCURRENTLY)"),
987 errhint("Use ALTER TABLE ... REPLICA IDENTITY USING INDEX to designate another index as replica identity."));
988
991 errmsg("cannot execute %s on relation \"%s\"",
992 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
993 errhint("Relation \"%s\" has no identity index.",
995 }
996
998}
999
1000
1001/*
1002 * rebuild_relation: rebuild an existing relation in index or physical order
1003 *
1004 * OldHeap: table to rebuild. See cluster_rel() for comments on the required
1005 * lock strength.
1006 *
1007 * index: index to cluster by, or NULL to rewrite in physical order.
1008 *
1009 * ident_idx: identity index, to handle replaying of concurrent data changes
1010 * to the new heap. InvalidOid if there's no CONCURRENTLY option.
1011 *
1012 * On entry, heap and index (if one is given) must be open, and the
1013 * appropriate lock held on them -- AccessExclusiveLock for exclusive
1014 * processing and ShareUpdateExclusiveLock for concurrent processing.
1015 *
1016 * On exit, they are closed, but still locked with AccessExclusiveLock.
1017 * (The function handles the lock upgrade if 'concurrent' is true.)
1018 */
1019static void
1021 Oid ident_idx)
1022{
1023 Oid tableOid = RelationGetRelid(OldHeap);
1024 Oid accessMethod = OldHeap->rd_rel->relam;
1025 Oid tableSpace = OldHeap->rd_rel->reltablespace;
1028 char relpersistence;
1032 bool concurrent = OidIsValid(ident_idx);
1033 Snapshot snapshot = NULL;
1034#if USE_ASSERT_CHECKING
1036
1037 lmode = RepackLockLevel(concurrent);
1038
1041#endif
1042
1043 if (concurrent)
1044 {
1045 /*
1046 * The worker needs to be member of the locking group we're the leader
1047 * of. We ought to become the leader before the worker starts. The
1048 * worker will join the group as soon as it starts.
1049 *
1050 * This is to make sure that the deadlock described below is
1051 * detectable by deadlock.c: if the worker waits for a transaction to
1052 * complete and we are waiting for the worker output, then effectively
1053 * we (i.e. this backend) are waiting for that transaction.
1054 */
1056
1057 /*
1058 * Start the worker that decodes data changes applied while we're
1059 * copying the table contents.
1060 *
1061 * Note that the worker has to wait for all transactions with XID
1062 * already assigned to finish. If some of those transactions is
1063 * waiting for a lock conflicting with ShareUpdateExclusiveLock on our
1064 * table (e.g. it runs CREATE INDEX), we can end up in a deadlock.
1065 * Not sure this risk is worth unlocking/locking the table (and its
1066 * clustering index) and checking again if it's still eligible for
1067 * REPACK CONCURRENTLY.
1068 */
1070
1071 /*
1072 * Wait until the worker has the initial snapshot and retrieve it.
1073 */
1075
1076 PushActiveSnapshot(snapshot);
1077 }
1078
1079 /* for CLUSTER or REPACK USING INDEX, mark the index as the one to use */
1080 if (index != NULL)
1082
1083 /* Remember info about rel before closing OldHeap */
1084 relpersistence = OldHeap->rd_rel->relpersistence;
1085
1086 /*
1087 * Create the transient table that will receive the re-ordered data.
1088 *
1089 * OldHeap is already locked, so no need to lock it again. make_new_heap
1090 * obtains AccessExclusiveLock on the new heap and its toast table.
1091 */
1092 OIDNewHeap = make_new_heap(tableOid, tableSpace,
1093 accessMethod,
1094 relpersistence,
1095 NoLock);
1098
1099 /*
1100 * In concurrent mode, create a copy of the attribute defaults on the temp
1101 * table, which the executor needs when replaying concurrent data changes.
1102 */
1103 if (concurrent)
1105
1106 /* Copy the heap data into the new table in the desired order */
1109
1110 /* The historic snapshot won't be needed anymore. */
1111 if (snapshot)
1112 {
1115 }
1116
1117 if (concurrent)
1118 {
1120
1121 /*
1122 * Close the index, but keep the lock. Both heaps will be closed by
1123 * the following call.
1124 */
1125 if (index)
1127
1130
1133 }
1134 else
1135 {
1137
1138 /* Close relcache entries, but keep lock until transaction commit */
1140 if (index)
1142
1143 /*
1144 * Close the new relation so it can be dropped as soon as the storage
1145 * is swapped. The relation is not visible to others, so no need to
1146 * unlock it explicitly.
1147 */
1149
1150 /*
1151 * Swap the physical files of the target and transient tables, then
1152 * rebuild the target's indexes and throw away the transient table.
1153 */
1155 swap_toast_by_content, false, true,
1156 true, /* reindex */
1158 relpersistence);
1159 }
1160}
1161
1162
1163/*
1164 * Create the transient table that will be filled with new data during
1165 * CLUSTER, ALTER TABLE, and similar operations. The transient table
1166 * duplicates the logical structure of the OldHeap; but will have the
1167 * specified physical storage properties NewTableSpace, NewAccessMethod, and
1168 * relpersistence.
1169 *
1170 * After this, the caller should load the new heap with transferred/modified
1171 * data, then call finish_heap_swap to complete the operation.
1172 */
1173Oid
1175 char relpersistence, LOCKMODE lockmode)
1176{
1180 Oid toastid;
1182 HeapTuple tuple;
1183 Datum reloptions;
1184 bool isNull;
1186
1187 OldHeap = table_open(OIDOldHeap, lockmode);
1189
1190 /*
1191 * Note that the NewHeap will not receive any of the defaults or
1192 * constraints associated with the OldHeap; we don't need 'em, and there's
1193 * no reason to spend cycles inserting them into the catalogs only to
1194 * delete them.
1195 */
1196
1197 /*
1198 * But we do want to use reloptions of the old heap for new heap.
1199 */
1201 if (!HeapTupleIsValid(tuple))
1202 elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1203 reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
1204 &isNull);
1205 if (isNull)
1206 reloptions = (Datum) 0;
1207
1208 if (relpersistence == RELPERSISTENCE_TEMP)
1210 else
1212
1213 /*
1214 * Create the new heap, using a temporary name in the same namespace as
1215 * the existing table. NOTE: there is some risk of collision with user
1216 * relnames. Working around this seems more trouble than it's worth; in
1217 * particular, we can't create the new heap in a different namespace from
1218 * the old, or we will have problems with the TEMP status of temp tables.
1219 *
1220 * Note: the new heap is not a shared relation, even if we are rebuilding
1221 * a shared rel. However, we do make the new heap mapped if the source is
1222 * mapped. This simplifies swap_relation_files, and is absolutely
1223 * necessary for rebuilding pg_class, for reasons explained there.
1224 */
1225 snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
1226
1230 InvalidOid,
1231 InvalidOid,
1232 InvalidOid,
1233 OldHeap->rd_rel->relowner,
1236 NIL,
1238 relpersistence,
1239 false,
1242 reloptions,
1243 false,
1244 true,
1245 true,
1246 OIDOldHeap,
1247 NULL);
1249
1250 ReleaseSysCache(tuple);
1251
1252 /*
1253 * Advance command counter so that the newly-created relation's catalog
1254 * tuples will be visible to table_open.
1255 */
1257
1258 /*
1259 * If necessary, create a TOAST table for the new relation.
1260 *
1261 * If the relation doesn't have a TOAST table already, we can't need one
1262 * for the new relation. The other way around is possible though: if some
1263 * wide columns have been dropped, NewHeapCreateToastTable can decide that
1264 * no TOAST table is needed for the new table.
1265 *
1266 * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
1267 * that the TOAST table will be visible for insertion.
1268 */
1269 toastid = OldHeap->rd_rel->reltoastrelid;
1270 if (OidIsValid(toastid))
1271 {
1272 /* keep the existing toast table's reloptions, if any */
1274 if (!HeapTupleIsValid(tuple))
1275 elog(ERROR, "cache lookup failed for relation %u", toastid);
1276 reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
1277 &isNull);
1278 if (isNull)
1279 reloptions = (Datum) 0;
1280
1281 NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid);
1282
1283 ReleaseSysCache(tuple);
1284 }
1285
1287
1288 return OIDNewHeap;
1289}
1290
1291/*
1292 * Do the physical copying of table data.
1293 *
1294 * 'snapshot' and 'decoding_ctx': see table_relation_copy_for_cluster(). Pass
1295 * iff concurrent processing is required.
1296 *
1297 * There are three output parameters:
1298 * *pSwapToastByContent is set true if toast tables must be swapped by content.
1299 * *pFreezeXid receives the TransactionId used as freeze cutoff point.
1300 * *pCutoffMulti receives the MultiXactId used as a cutoff point.
1301 */
1302static void
1304 Snapshot snapshot, bool verbose, bool *pSwapToastByContent,
1306{
1312 VacuumParams params;
1313 struct VacuumCutoffs cutoffs;
1314 bool use_sort;
1315 double num_tuples = 0,
1316 tups_vacuumed = 0,
1318 BlockNumber num_pages;
1319 int elevel = verbose ? INFO : DEBUG2;
1320 PGRUsage ru0;
1321 char *nspname;
1322 bool concurrent = snapshot != NULL;
1324
1325 lmode = RepackLockLevel(concurrent);
1326
1328
1329 /* Store a copy of the namespace name for logging purposes */
1331
1332 /*
1333 * Their tuple descriptors should be exactly alike, but here we only need
1334 * assume that they have the same number of columns.
1335 */
1338 Assert(newTupDesc->natts == oldTupDesc->natts);
1339
1340 /*
1341 * If the OldHeap has a toast table, get lock on the toast table to keep
1342 * it from being vacuumed. This is needed because autovacuum processes
1343 * toast tables independently of their main tables, with no lock on the
1344 * latter. If an autovacuum were to start on the toast table after we
1345 * compute our OldestXmin below, it would use a later OldestXmin, and then
1346 * possibly remove as DEAD toast tuples belonging to main tuples we think
1347 * are only RECENTLY_DEAD. Then we'd fail while trying to copy those
1348 * tuples.
1349 *
1350 * We don't need to open the toast relation here, just lock it. The lock
1351 * will be held till end of transaction.
1352 */
1353 if (OldHeap->rd_rel->reltoastrelid)
1354 LockRelationOid(OldHeap->rd_rel->reltoastrelid, lmode);
1355
1356 /*
1357 * If both tables have TOAST tables, perform toast swap by content. It is
1358 * possible that the old table has a toast table but the new one doesn't,
1359 * if toastable columns have been dropped. In that case we have to do
1360 * swap by links. This is okay because swap by content is only essential
1361 * for system catalogs, and we don't support schema changes for them.
1362 */
1363 if (OldHeap->rd_rel->reltoastrelid && NewHeap->rd_rel->reltoastrelid &&
1364 !concurrent)
1365 {
1366 *pSwapToastByContent = true;
1367
1368 /*
1369 * When doing swap by content, any toast pointers written into NewHeap
1370 * must use the old toast table's OID, because that's where the toast
1371 * data will eventually be found. Set this up by setting rd_toastoid.
1372 * This also tells toast_save_datum() to preserve the toast value
1373 * OIDs, which we want so as not to invalidate toast pointers in
1374 * system catalog caches, and to avoid making multiple copies of a
1375 * single toast value.
1376 *
1377 * Note that we must hold NewHeap open until we are done writing data,
1378 * since the relcache will not guarantee to remember this setting once
1379 * the relation is closed. Also, this technique depends on the fact
1380 * that no one will try to read from the NewHeap until after we've
1381 * finished writing it and swapping the rels --- otherwise they could
1382 * follow the toast pointers to the wrong place. (It would actually
1383 * work for values copied over from the old toast table, but not for
1384 * any values that we toast which were previously not toasted.)
1385 *
1386 * This would not work with CONCURRENTLY because we may need to delete
1387 * TOASTed tuples from the new heap. With this hack, we'd delete them
1388 * from the old heap.
1389 */
1390 NewHeap->rd_toastoid = OldHeap->rd_rel->reltoastrelid;
1391 }
1392 else
1393 *pSwapToastByContent = false;
1394
1395 /*
1396 * Compute xids used to freeze and weed out dead tuples and multixacts.
1397 * Since we're going to rewrite the whole table anyway, there's no reason
1398 * not to be aggressive about this.
1399 */
1400 memset(&params, 0, sizeof(VacuumParams));
1401 vacuum_get_cutoffs(OldHeap, &params, &cutoffs);
1402
1403 /*
1404 * FreezeXid will become the table's new relfrozenxid, and that mustn't go
1405 * backwards, so take the max.
1406 */
1407 {
1408 TransactionId relfrozenxid = OldHeap->rd_rel->relfrozenxid;
1409
1412 cutoffs.FreezeLimit = relfrozenxid;
1413 }
1414
1415 /*
1416 * MultiXactCutoff, similarly, shouldn't go backwards either.
1417 */
1418 {
1419 MultiXactId relminmxid = OldHeap->rd_rel->relminmxid;
1420
1423 cutoffs.MultiXactCutoff = relminmxid;
1424 }
1425
1426 /*
1427 * Decide whether to use an indexscan or seqscan-and-optional-sort to scan
1428 * the OldHeap. We know how to use a sort to duplicate the ordering of a
1429 * btree index, and will use seqscan-and-sort for that case if the planner
1430 * tells us it's cheaper. Otherwise, always indexscan if an index is
1431 * provided, else plain seqscan.
1432 */
1433 if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID)
1436 else
1437 use_sort = false;
1438
1439 /* Log what we're doing */
1440 if (OldIndex != NULL && !use_sort)
1441 ereport(elevel,
1442 errmsg("repacking \"%s.%s\" using index scan on \"%s\"",
1443 nspname,
1446 else if (use_sort)
1447 ereport(elevel,
1448 errmsg("repacking \"%s.%s\" using sequential scan and sort",
1449 nspname,
1451 else
1452 ereport(elevel,
1453 errmsg("repacking \"%s.%s\" in physical order",
1454 nspname,
1456
1457 /*
1458 * Hand off the actual copying to AM specific function, the generic code
1459 * cannot know how to deal with visibility across AMs. Note that this
1460 * routine is allowed to set FreezeXid / MultiXactCutoff to different
1461 * values (e.g. because the AM doesn't use freezing).
1462 */
1464 cutoffs.OldestXmin, snapshot,
1465 &cutoffs.FreezeLimit,
1466 &cutoffs.MultiXactCutoff,
1467 &num_tuples, &tups_vacuumed,
1469
1470 /* return selected values to caller, get set as relfrozenxid/minmxid */
1471 *pFreezeXid = cutoffs.FreezeLimit;
1472 *pCutoffMulti = cutoffs.MultiXactCutoff;
1473
1474 /*
1475 * Reset rd_toastoid just to be tidy --- it shouldn't be looked at again.
1476 * In the CONCURRENTLY case, we need to set it again before applying the
1477 * concurrent changes.
1478 */
1479 NewHeap->rd_toastoid = InvalidOid;
1480
1482
1483 /* Log what we did */
1484 ereport(elevel,
1485 (errmsg("\"%s.%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
1486 nspname,
1488 tups_vacuumed, num_tuples,
1490 errdetail("%.0f dead row versions cannot be removed yet.\n"
1491 "%s.",
1493 pg_rusage_show(&ru0))));
1494
1495 /* Update pg_class to reflect the correct values of pages and tuples. */
1497
1501 elog(ERROR, "cache lookup failed for relation %u",
1504
1505 relform->relpages = num_pages;
1506 relform->reltuples = num_tuples;
1507
1508 /* Don't update the stats for pg_class. See swap_relation_files. */
1511 else
1513
1514 /* Clean up. */
1517
1518 /* Make the update visible */
1520}
1521
1522/*
1523 * Swap the physical files of two given relations.
1524 *
1525 * We swap the physical identity (reltablespace, relfilenumber) while keeping
1526 * the same logical identities of the two relations. relpersistence is also
1527 * swapped, which is critical since it determines where buffers live for each
1528 * relation.
1529 *
1530 * We can swap associated TOAST data in either of two ways: recursively swap
1531 * the physical content of the toast tables (and their indexes), or swap the
1532 * TOAST links in the given relations' pg_class entries. The former is needed
1533 * to manage rewrites of shared catalogs (where we cannot change the pg_class
1534 * links) while the latter is the only way to handle cases in which a toast
1535 * table is added or removed altogether.
1536 *
1537 * Additionally, the first relation is marked with relfrozenxid set to
1538 * frozenXid. It seems a bit ugly to have this here, but the caller would
1539 * have to do it anyway, so having it here saves a heap_update. Note: in
1540 * the swap-toast-links case, we assume we don't need to change the toast
1541 * table's relfrozenxid: the new version of the toast table should already
1542 * have relfrozenxid set to RecentXmin, which is good enough.
1543 *
1544 * Lastly, if r2 and its toast table and toast index (if any) are mapped,
1545 * their OIDs are emitted into mapped_tables[]. This is hacky but beats
1546 * having to look the information up again later in finish_heap_swap.
1547 */
1548static void
1551 bool is_internal,
1555{
1558 reltup2;
1560 relform2;
1564 char swptmpchr;
1565 Oid relam1,
1566 relam2;
1567
1568 /* We need writable copies of both pg_class tuples. */
1570
1573 elog(ERROR, "cache lookup failed for relation %u", r1);
1575
1578 elog(ERROR, "cache lookup failed for relation %u", r2);
1580
1581 relfilenumber1 = relform1->relfilenode;
1582 relfilenumber2 = relform2->relfilenode;
1583 relam1 = relform1->relam;
1584 relam2 = relform2->relam;
1585
1588 {
1589 /*
1590 * Normal non-mapped relations: swap relfilenumbers, reltablespaces,
1591 * relpersistence
1592 */
1594
1595 swaptemp = relform1->relfilenode;
1596 relform1->relfilenode = relform2->relfilenode;
1597 relform2->relfilenode = swaptemp;
1598
1599 swaptemp = relform1->reltablespace;
1600 relform1->reltablespace = relform2->reltablespace;
1601 relform2->reltablespace = swaptemp;
1602
1603 swaptemp = relform1->relam;
1604 relform1->relam = relform2->relam;
1605 relform2->relam = swaptemp;
1606
1607 swptmpchr = relform1->relpersistence;
1608 relform1->relpersistence = relform2->relpersistence;
1609 relform2->relpersistence = swptmpchr;
1610
1611 /* Also swap toast links, if we're swapping by links */
1613 {
1614 swaptemp = relform1->reltoastrelid;
1615 relform1->reltoastrelid = relform2->reltoastrelid;
1616 relform2->reltoastrelid = swaptemp;
1617 }
1618 }
1619 else
1620 {
1621 /*
1622 * Mapped-relation case. Here we have to swap the relation mappings
1623 * instead of modifying the pg_class columns. Both must be mapped.
1624 */
1627 elog(ERROR, "cannot swap mapped relation \"%s\" with non-mapped relation",
1628 NameStr(relform1->relname));
1629
1630 /*
1631 * We can't change the tablespace nor persistence of a mapped rel, and
1632 * we can't handle toast link swapping for one either, because we must
1633 * not apply any critical changes to its pg_class row. These cases
1634 * should be prevented by upstream permissions tests, so these checks
1635 * are non-user-facing emergency backstop.
1636 */
1637 if (relform1->reltablespace != relform2->reltablespace)
1638 elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
1639 NameStr(relform1->relname));
1640 if (relform1->relpersistence != relform2->relpersistence)
1641 elog(ERROR, "cannot change persistence of mapped relation \"%s\"",
1642 NameStr(relform1->relname));
1643 if (relform1->relam != relform2->relam)
1644 elog(ERROR, "cannot change access method of mapped relation \"%s\"",
1645 NameStr(relform1->relname));
1646 if (!swap_toast_by_content &&
1647 (relform1->reltoastrelid || relform2->reltoastrelid))
1648 elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
1649 NameStr(relform1->relname));
1650
1651 /*
1652 * Fetch the mappings --- shouldn't fail, but be paranoid
1653 */
1656 elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1657 NameStr(relform1->relname), r1);
1660 elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1661 NameStr(relform2->relname), r2);
1662
1663 /*
1664 * Send replacement mappings to relmapper. Note these won't actually
1665 * take effect until CommandCounterIncrement.
1666 */
1667 RelationMapUpdateMap(r1, relfilenumber2, relform1->relisshared, false);
1668 RelationMapUpdateMap(r2, relfilenumber1, relform2->relisshared, false);
1669
1670 /* Pass OIDs of mapped r2 tables back to caller */
1671 *mapped_tables++ = r2;
1672 }
1673
1674 /*
1675 * Recognize that rel1's relfilenumber (swapped from rel2) is new in this
1676 * subtransaction. The rel2 storage (swapped from rel1) may or may not be
1677 * new.
1678 */
1679 {
1680 Relation rel1,
1681 rel2;
1682
1685 rel2->rd_createSubid = rel1->rd_createSubid;
1686 rel2->rd_newRelfilelocatorSubid = rel1->rd_newRelfilelocatorSubid;
1687 rel2->rd_firstRelfilelocatorSubid = rel1->rd_firstRelfilelocatorSubid;
1691 }
1692
1693 /*
1694 * In the case of a shared catalog, these next few steps will only affect
1695 * our own database's pg_class row; but that's okay, because they are all
1696 * noncritical updates. That's also an important fact for the case of a
1697 * mapped catalog, because it's possible that we'll commit the map change
1698 * and then fail to commit the pg_class update.
1699 */
1700
1701 /* set rel1's frozen Xid and minimum MultiXid */
1702 if (relform1->relkind != RELKIND_INDEX)
1703 {
1706 relform1->relfrozenxid = frozenXid;
1707 relform1->relminmxid = cutoffMulti;
1708 }
1709
1710 /* swap size statistics too, since new rel has freshly-updated stats */
1711 {
1716
1717 swap_pages = relform1->relpages;
1718 relform1->relpages = relform2->relpages;
1719 relform2->relpages = swap_pages;
1720
1721 swap_tuples = relform1->reltuples;
1722 relform1->reltuples = relform2->reltuples;
1723 relform2->reltuples = swap_tuples;
1724
1725 swap_allvisible = relform1->relallvisible;
1726 relform1->relallvisible = relform2->relallvisible;
1727 relform2->relallvisible = swap_allvisible;
1728
1729 swap_allfrozen = relform1->relallfrozen;
1730 relform1->relallfrozen = relform2->relallfrozen;
1731 relform2->relallfrozen = swap_allfrozen;
1732 }
1733
1734 /*
1735 * Update the tuples in pg_class --- unless the target relation of the
1736 * swap is pg_class itself. In that case, there is zero point in making
1737 * changes because we'd be updating the old data that we're about to throw
1738 * away. Because the real work being done here for a mapped relation is
1739 * just to change the relation map settings, it's all right to not update
1740 * the pg_class rows in this case. The most important changes will instead
1741 * performed later, in finish_heap_swap() itself.
1742 */
1743 if (!target_is_pg_class)
1744 {
1746
1749 indstate);
1751 indstate);
1753 }
1754 else
1755 {
1756 /* no update ... but we do still need relcache inval */
1759 }
1760
1761 /*
1762 * Now that pg_class has been updated with its relevant information for
1763 * the swap, update the dependency of the relations to point to their new
1764 * table AM, if it has changed.
1765 */
1766 if (relam1 != relam2)
1767 {
1769 r1,
1771 relam1,
1772 relam2) != 1)
1773 elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
1775 get_rel_name(r1));
1777 r2,
1779 relam2,
1780 relam1) != 1)
1781 elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
1783 get_rel_name(r2));
1784 }
1785
1786 /*
1787 * Post alter hook for modified relations. The change to r2 is always
1788 * internal, but r1 depends on the invocation context.
1789 */
1791 InvalidOid, is_internal);
1793 InvalidOid, true);
1794
1795 /*
1796 * If we have toast tables associated with the relations being swapped,
1797 * deal with them too.
1798 */
1799 if (relform1->reltoastrelid || relform2->reltoastrelid)
1800 {
1802 {
1803 if (relform1->reltoastrelid && relform2->reltoastrelid)
1804 {
1805 /* Recursively swap the contents of the toast tables */
1806 swap_relation_files(relform1->reltoastrelid,
1807 relform2->reltoastrelid,
1810 is_internal,
1811 frozenXid,
1814 }
1815 else
1816 {
1817 /* caller messed up */
1818 elog(ERROR, "cannot swap toast files by content when there's only one");
1819 }
1820 }
1821 else
1822 {
1823 /*
1824 * We swapped the ownership links, so we need to change dependency
1825 * data to match.
1826 *
1827 * NOTE: it is possible that only one table has a toast table.
1828 *
1829 * NOTE: at present, a TOAST table's only dependency is the one on
1830 * its owning table. If more are ever created, we'd need to use
1831 * something more selective than deleteDependencyRecordsFor() to
1832 * get rid of just the link we want.
1833 */
1836 long count;
1837
1838 /*
1839 * We disallow this case for system catalogs, to avoid the
1840 * possibility that the catalog we're rebuilding is one of the
1841 * ones the dependency changes would change. It's too late to be
1842 * making any data changes to the target catalog.
1843 */
1845 elog(ERROR, "cannot swap toast files by links for system catalogs");
1846
1847 /* Delete old dependencies */
1848 if (relform1->reltoastrelid)
1849 {
1851 relform1->reltoastrelid,
1852 false);
1853 if (count != 1)
1854 elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1855 count);
1856 }
1857 if (relform2->reltoastrelid)
1858 {
1860 relform2->reltoastrelid,
1861 false);
1862 if (count != 1)
1863 elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1864 count);
1865 }
1866
1867 /* Register new dependencies */
1869 baseobject.objectSubId = 0;
1871 toastobject.objectSubId = 0;
1872
1873 if (relform1->reltoastrelid)
1874 {
1875 baseobject.objectId = r1;
1876 toastobject.objectId = relform1->reltoastrelid;
1879 }
1880
1881 if (relform2->reltoastrelid)
1882 {
1883 baseobject.objectId = r2;
1884 toastobject.objectId = relform2->reltoastrelid;
1887 }
1888 }
1889 }
1890
1891 /*
1892 * If we're swapping two toast tables by content, do the same for their
1893 * valid index. The swap can actually be safely done only if the relations
1894 * have indexes.
1895 */
1897 relform1->relkind == RELKIND_TOASTVALUE &&
1898 relform2->relkind == RELKIND_TOASTVALUE)
1899 {
1902
1903 /* Get valid index for each relation */
1908
1913 is_internal,
1917 }
1918
1919 /* Clean up. */
1922
1924}
1925
1926/*
1927 * Remove the transient table that was built by make_new_heap, and finish
1928 * cleaning up (including rebuilding all indexes on the old heap).
1929 */
1930void
1932 bool is_system_catalog,
1934 bool check_constraints,
1935 bool is_internal,
1936 bool reindex,
1939 char newrelpersistence)
1940{
1941 ObjectAddress object;
1942 Oid mapped_tables[4];
1943 int i;
1944
1945 /* Report that we are now swapping relation files */
1948
1949 /* Zero out possible results from swapped_relation_files */
1950 memset(mapped_tables, 0, sizeof(mapped_tables));
1951
1952 /*
1953 * Swap the contents of the heap relations (including any toast tables).
1954 * Also set old heap's relfrozenxid to frozenXid.
1955 */
1958 swap_toast_by_content, is_internal,
1960
1961 /*
1962 * If it's a system catalog, queue a sinval message to flush all catcaches
1963 * on the catalog when we reach CommandCounterIncrement.
1964 */
1967
1968 if (reindex)
1969 {
1970 int reindex_flags;
1972
1973 /*
1974 * Rebuild each index on the relation (but not the toast table, which
1975 * is all-new at this point). It is important to do this before the
1976 * DROP step because if we are processing a system catalog that will
1977 * be used during DROP, we want to have its indexes available. There
1978 * is no advantage to the other order anyway because this is all
1979 * transactional, so no chance to reclaim disk space before commit. We
1980 * do not need a final CommandCounterIncrement() because
1981 * reindex_relation does it.
1982 *
1983 * Note: because index_build is called via reindex_relation, it will
1984 * never set indcheckxmin true for the indexes. This is OK even
1985 * though in some sense we are building new indexes rather than
1986 * rebuilding existing ones, because the new heap won't contain any
1987 * HOT chains at all, let alone broken ones, so it can't be necessary
1988 * to set indcheckxmin.
1989 */
1993
1994 /*
1995 * Ensure that the indexes have the same persistence as the parent
1996 * relation.
1997 */
1998 if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
2000 else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
2002
2003 /* Report that we are now reindexing relations */
2006
2008 }
2009
2010 /* Report that we are now doing clean up */
2013
2014 /*
2015 * If the relation being rebuilt is pg_class, swap_relation_files()
2016 * couldn't update pg_class's own pg_class entry (check comments in
2017 * swap_relation_files()), thus relfrozenxid was not updated. That's
2018 * annoying because a potential reason for doing a VACUUM FULL is a
2019 * imminent or actual anti-wraparound shutdown. So, now that we can
2020 * access the new relation using its indices, update relfrozenxid.
2021 * pg_class doesn't have a toast relation, so we don't need to update the
2022 * corresponding toast relation. Not that there's little point moving all
2023 * relfrozenxid updates here since swap_relation_files() needs to write to
2024 * pg_class for non-mapped relations anyway.
2025 */
2027 {
2031
2033
2036 elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
2038
2039 relform->relfrozenxid = frozenXid;
2040 relform->relminmxid = cutoffMulti;
2041
2043
2045 }
2046
2047 /* Destroy new heap with old filenumber */
2048 object.classId = RelationRelationId;
2049 object.objectId = OIDNewHeap;
2050 object.objectSubId = 0;
2051
2052 if (!reindex)
2053 {
2054 /*
2055 * Make sure the changes in pg_class are visible. This is especially
2056 * important if !swap_toast_by_content, so that the correct TOAST
2057 * relation is dropped. (reindex_relation() above did not help in this
2058 * case))
2059 */
2061 }
2062
2063 /*
2064 * The new relation is local to our transaction and we know nothing
2065 * depends on it, so DROP_RESTRICT should be OK.
2066 */
2068
2069 /* performDeletion does CommandCounterIncrement at end */
2070
2071 /*
2072 * Now we must remove any relation mapping entries that we set up for the
2073 * transient table, as well as its toast table and toast index if any. If
2074 * we fail to do this before commit, the relmapper will complain about new
2075 * permanent map entries being added post-bootstrap.
2076 */
2077 for (i = 0; OidIsValid(mapped_tables[i]); i++)
2079
2080 /*
2081 * At this point, everything is kosher except that, if we did toast swap
2082 * by links, the toast table's name corresponds to the transient table.
2083 * The name is irrelevant to the backend because it's referenced by OID,
2084 * but users looking at the catalogs could be confused. Rename it to
2085 * prevent this problem.
2086 *
2087 * Note no lock required on the relation, because we already hold an
2088 * exclusive lock on it.
2089 */
2091 {
2093
2095 if (OidIsValid(newrel->rd_rel->reltoastrelid))
2096 {
2097 Oid toastidx;
2099
2100 /* Get the associated valid index to be renamed */
2101 toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
2103
2104 /* rename the toast table ... */
2105 snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
2106 OIDOldHeap);
2107 RenameRelationInternal(newrel->rd_rel->reltoastrelid,
2108 NewToastName, true, false);
2109
2110 /* ... and its valid index too. */
2111 snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
2112 OIDOldHeap);
2113
2115 NewToastName, true, true);
2116
2117 /*
2118 * Reset the relrewrite for the toast. The command-counter
2119 * increment is required here as we are about to update the tuple
2120 * that is updated as part of RenameRelationInternal.
2121 */
2123 ResetRelRewrite(newrel->rd_rel->reltoastrelid);
2124 }
2126 }
2127
2128 /* if it's not a catalog table, clear any missing attribute settings */
2129 if (!is_system_catalog)
2130 {
2132
2136 }
2137}
2138
2139/*
2140 * Determine which relations to process, when REPACK/CLUSTER is called
2141 * without specifying a table name. The exact process depends on whether
2142 * USING INDEX was given or not, and in any case we only return tables and
2143 * materialized views that the current user has privileges to repack/cluster.
2144 *
2145 * If USING INDEX was given, we scan pg_index to find those that have
2146 * indisclustered set; if it was not given, scan pg_class and return all
2147 * tables.
2148 *
2149 * Return it as a list of RelToCluster in the given memory context.
2150 */
2151static List *
2153{
2155 TableScanDesc scan;
2156 HeapTuple tuple;
2157 List *rtcs = NIL;
2158
2159 if (usingindex)
2160 {
2161 ScanKeyData entry;
2162
2163 /*
2164 * For USING INDEX, scan pg_index to find those with indisclustered.
2165 *
2166 * Note we don't obtain lock of any kind on the index, which means the
2167 * index or its owning table could be gone or change at any point. We
2168 * have to be extra careful when examining catalog state for them.
2169 */
2171 ScanKeyInit(&entry,
2174 BoolGetDatum(true));
2175 scan = table_beginscan_catalog(catalog, 1, &entry);
2176 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2177 {
2181 Oid relnamespace;
2182 char relpersistence;
2184
2185 index = (Form_pg_index) GETSTRUCT(tuple);
2186
2189 continue;
2190 relnamespace = ((Form_pg_class) GETSTRUCT(classtup))->relnamespace;
2191 relpersistence = ((Form_pg_class) GETSTRUCT(classtup))->relpersistence;
2193
2194 /* Skip temp relations belonging to other sessions */
2195 if (relpersistence == RELPERSISTENCE_TEMP &&
2196 !isTempOrTempToastNamespace(relnamespace))
2197 continue;
2198
2199 /* noisily skip rels which the user can't process */
2200 if (!repack_is_permitted_for_relation(cmd, index->indrelid,
2201 GetUserId()))
2202 continue;
2203
2204 /* Use a permanent memory context for the result list */
2207 rtc->tableOid = index->indrelid;
2208 rtc->indexOid = index->indexrelid;
2209 rtcs = lappend(rtcs, rtc);
2211 }
2212 }
2213 else
2214 {
2217
2218 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2219 {
2221 Form_pg_class class;
2223
2224 class = (Form_pg_class) GETSTRUCT(tuple);
2225
2226 /* Can only process plain tables and matviews */
2227 if (class->relkind != RELKIND_RELATION &&
2228 class->relkind != RELKIND_MATVIEW)
2229 continue;
2230
2231 /* Skip temp relations belonging to other sessions */
2232 if (class->relpersistence == RELPERSISTENCE_TEMP &&
2233 !isTempOrTempToastNamespace(class->relnamespace))
2234 continue;
2235
2236 /* noisily skip rels which the user can't process */
2238 GetUserId()))
2239 continue;
2240
2241 /* Use a permanent memory context for the result list */
2244 rtc->tableOid = class->oid;
2245 rtc->indexOid = InvalidOid;
2246 rtcs = lappend(rtcs, rtc);
2248 }
2249 }
2250
2251 table_endscan(scan);
2253
2254 return rtcs;
2255}
2256
2257/*
2258 * Given a partitioned table or its index, return a list of RelToCluster for
2259 * all the leaf child tables/indexes.
2260 *
2261 * 'rel_is_index' tells whether 'relid' is that of an index (true) or of the
2262 * owning relation.
2263 */
2264static List *
2267{
2268 List *inhoids;
2269 List *rtcs = NIL;
2270
2271 /*
2272 * Do not lock the children until they're processed. Note that we do hold
2273 * a lock on the parent partitioned table.
2274 */
2277 {
2278 Oid table_oid,
2279 index_oid;
2282
2283 if (rel_is_index)
2284 {
2285 /* consider only leaf indexes */
2287 continue;
2288
2291 }
2292 else
2293 {
2294 /* consider only leaf relations */
2296 continue;
2297
2300 }
2301
2302 /*
2303 * It's possible that the user does not have privileges to CLUSTER the
2304 * leaf partition despite having them on the partitioned table. Skip
2305 * if so.
2306 */
2308 continue;
2309
2310 /* Use a permanent memory context for the result list */
2313 rtc->tableOid = table_oid;
2314 rtc->indexOid = index_oid;
2315 rtcs = lappend(rtcs, rtc);
2317 }
2318
2319 return rtcs;
2320}
2321
2322
2323/*
2324 * Return whether userid has privileges to execute REPACK on relid.
2325 *
2326 * Caller may not have a lock on the relation, so it could have been
2327 * dropped concurrently. In that case, silently return false.
2328 *
2329 * If the relation does exist but the user doesn't have the required
2330 * privs, emit a WARNING and return false. Otherwise, return true.
2331 */
2332static bool
2334{
2335 bool is_missing = false;
2337 char *relname;
2338
2340
2342 if (is_missing)
2343 return false;
2344
2345 if (result == ACLCHECK_OK)
2346 return true;
2347
2348 /*
2349 * The relation can also be dropped after we tested its ACL and before we
2350 * read its relname, so be careful here.
2351 */
2352 relname = get_rel_name(relid);
2353 if (relname != NULL)
2354 {
2356 errmsg("permission denied to execute %s on \"%s\", skipping it",
2358 pfree(relname);
2359 }
2360
2361 return false;
2362}
2363
2364
2365/*
2366 * Given a RepackStmt with an indicated relation name, resolve the relation
2367 * name, obtain lock on it, then determine what to do based on the relation
2368 * type: if it's table and not partitioned, repack it as indicated (using an
2369 * existing clustered index, or following the given one), and return NULL.
2370 *
2371 * On the other hand, if the table is partitioned, do nothing further and
2372 * instead return the opened and locked relcache entry, so that caller can
2373 * process the partitions using the multiple-table handling code. In this
2374 * case, if an index name is given, it's up to the caller to resolve it.
2375 */
2376static Relation
2378 ClusterParams *params)
2379{
2380 Relation rel;
2381 Oid tableOid;
2382
2383 Assert(stmt->relation != NULL);
2384 Assert(stmt->command == REPACK_COMMAND_CLUSTER ||
2385 stmt->command == REPACK_COMMAND_REPACK);
2386
2387 /*
2388 * Make sure ANALYZE is specified if a column list is present.
2389 */
2390 if ((params->options & CLUOPT_ANALYZE) == 0 && stmt->relation->va_cols != NIL)
2391 ereport(ERROR,
2393 errmsg("ANALYZE option must be specified when a column list is provided"));
2394
2395 /* Find, lock, and check permissions on the table. */
2396 tableOid = RangeVarGetRelidExtended(stmt->relation->relation,
2397 lockmode,
2398 0,
2400 NULL);
2401 rel = table_open(tableOid, NoLock);
2402
2403 /*
2404 * Reject clustering a remote temp table ... their local buffer manager is
2405 * not going to cope.
2406 */
2407 if (RELATION_IS_OTHER_TEMP(rel))
2408 ereport(ERROR,
2410 /*- translator: first %s is name of a SQL command, eg. REPACK */
2411 errmsg("cannot execute %s on temporary tables of other sessions",
2412 RepackCommandAsString(stmt->command)));
2413
2414 /*
2415 * For partitioned tables, let caller handle this. Otherwise, process it
2416 * here and we're done.
2417 */
2418 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2419 return rel;
2420 else
2421 {
2422 Oid indexOid = InvalidOid;
2423
2424 indexOid = determine_clustered_index(rel, stmt->usingindex,
2425 stmt->indexname);
2426 if (OidIsValid(indexOid))
2427 check_index_is_clusterable(rel, indexOid, lockmode);
2428
2429 cluster_rel(stmt->command, rel, indexOid, params, isTopLevel);
2430
2431 /*
2432 * Do an analyze, if requested. We close the transaction and start a
2433 * new one, so that we don't hold the stronger lock for longer than
2434 * needed.
2435 */
2436 if (params->options & CLUOPT_ANALYZE)
2437 {
2439
2442
2445
2446 vac_params.options |= VACOPT_ANALYZE;
2447 if (params->options & CLUOPT_VERBOSE)
2448 vac_params.options |= VACOPT_VERBOSE;
2449 analyze_rel(tableOid, NULL, &vac_params,
2450 stmt->relation->va_cols, true, NULL);
2453 }
2454
2455 return NULL;
2456 }
2457}
2458
2459/*
2460 * Given a relation and the usingindex/indexname options in a
2461 * REPACK USING INDEX or CLUSTER command, return the OID of the
2462 * index to use for clustering the table.
2463 *
2464 * Caller must hold lock on the relation so that the set of indexes
2465 * doesn't change, and must call check_index_is_clusterable.
2466 */
2467static Oid
2468determine_clustered_index(Relation rel, bool usingindex, const char *indexname)
2469{
2470 Oid indexOid;
2471
2472 if (indexname == NULL && usingindex)
2473 {
2474 /*
2475 * If USING INDEX with no name is given, find a clustered index, or
2476 * error out if none.
2477 */
2478 indexOid = InvalidOid;
2480 {
2482 {
2483 indexOid = idxoid;
2484 break;
2485 }
2486 }
2487
2488 if (!OidIsValid(indexOid))
2489 ereport(ERROR,
2491 errmsg("there is no previously clustered index for table \"%s\"",
2493 }
2494 else if (indexname != NULL)
2495 {
2496 /* An index was specified; obtain its OID. */
2497 indexOid = get_relname_relid(indexname, rel->rd_rel->relnamespace);
2498 if (!OidIsValid(indexOid))
2499 ereport(ERROR,
2501 errmsg("index \"%s\" for table \"%s\" does not exist",
2502 indexname, RelationGetRelationName(rel)));
2503 }
2504 else
2505 indexOid = InvalidOid;
2506
2507 return indexOid;
2508}
2509
2510static const char *
2512{
2513 switch (cmd)
2514 {
2516 return "REPACK";
2518 return "VACUUM";
2520 return "CLUSTER";
2521 }
2522 return "???"; /* keep compiler quiet */
2523}
2524
2525/*
2526 * Apply all the changes stored in 'file'.
2527 */
2528static void
2530{
2531 ConcurrentChangeKind kind = '\0';
2532 Relation rel = chgcxt->cc_rel;
2536 bool have_old_tuple = false;
2538
2540 &TTSOpsVirtual);
2544 &TTSOpsVirtual);
2545
2547
2548 while (true)
2549 {
2550 size_t nread;
2552
2554
2555 nread = BufFileReadMaybeEOF(file, &kind, 1, true);
2556 if (nread == 0) /* done with the file? */
2557 break;
2558
2559 /*
2560 * If this is the old tuple for an update, read it into the tuple slot
2561 * and go to the next one. The update itself will be executed on the
2562 * next iteration, when we receive the NEW tuple.
2563 */
2564 if (kind == CHANGE_UPDATE_OLD)
2565 {
2566 restore_tuple(file, rel, old_update_tuple);
2567 have_old_tuple = true;
2568 continue;
2569 }
2570
2571 /*
2572 * Just before an UPDATE or DELETE, we must update the command
2573 * counter, because the change could refer to a tuple that we have
2574 * just inserted; and before an INSERT, we have to do this also if the
2575 * previous command was either update or delete.
2576 *
2577 * With this approach we don't spend so many CCIs for long strings of
2578 * only INSERTs, which can't affect one another.
2579 */
2580 if (kind == CHANGE_UPDATE_NEW || kind == CHANGE_DELETE ||
2581 (kind == CHANGE_INSERT && (prevkind == CHANGE_UPDATE_NEW ||
2583 {
2586 }
2587
2588 /*
2589 * Now restore the tuple into the slot and execute the change.
2590 */
2591 restore_tuple(file, rel, spilled_tuple);
2592
2593 if (kind == CHANGE_INSERT)
2594 {
2596 }
2597 else if (kind == CHANGE_DELETE)
2598 {
2599 bool found;
2600
2601 /* Find the tuple to be deleted */
2603 if (!found)
2604 elog(ERROR, "could not find target tuple");
2606 }
2607 else if (kind == CHANGE_UPDATE_NEW)
2608 {
2609 TupleTableSlot *key;
2610 bool found;
2611
2612 if (have_old_tuple)
2613 key = old_update_tuple;
2614 else
2615 key = spilled_tuple;
2616
2617 /* Find the tuple to be updated or deleted. */
2618 found = find_target_tuple(rel, chgcxt, key, ondisk_tuple);
2619 if (!found)
2620 elog(ERROR, "could not find target tuple");
2621
2622 /*
2623 * If 'tup' contains TOAST pointers, they point to the old
2624 * relation's toast. Copy the corresponding TOAST pointers for the
2625 * new relation from the existing tuple. (The fact that we
2626 * received a TOAST pointer here implies that the attribute hasn't
2627 * changed.)
2628 */
2630
2632
2634 have_old_tuple = false;
2635 }
2636 else
2637 elog(ERROR, "unrecognized kind of change: %d", kind);
2638
2639 ResetPerTupleExprContext(chgcxt->cc_estate);
2640 }
2641
2642 /* Cleanup. */
2646
2648}
2649
2650/*
2651 * Apply an insert from the spill of concurrent changes to the new copy of the
2652 * table.
2653 */
2654static void
2657{
2658 /* Put the tuple in the table, but make sure it won't be decoded */
2659 table_tuple_insert(rel, slot, GetCurrentCommandId(true),
2661
2662 /* Update indexes with this new tuple. */
2664 chgcxt->cc_estate,
2665 0,
2666 slot,
2667 NIL, NULL);
2669}
2670
2671/*
2672 * Apply an update from the spill of concurrent changes to the new copy of the
2673 * table.
2674 */
2675static void
2679{
2680 LockTupleMode lockmode;
2681 TM_FailureData tmfd;
2683 TM_Result res;
2684
2685 /*
2686 * Carry out the update, skipping logical decoding for it.
2687 */
2688 res = table_tuple_update(rel, &(ondisk_tuple->tts_tid), spilled_tuple,
2689 GetCurrentCommandId(true),
2693 false,
2694 &tmfd, &lockmode, &update_indexes);
2695 if (res != TM_Ok)
2696 ereport(ERROR,
2698 errmsg("could not apply concurrent %s on relation \"%s\"",
2699 "UPDATE", RelationGetRelationName(rel)));
2700
2701 if (update_indexes != TU_None)
2702 {
2703 uint32 flags = EIIT_IS_UPDATE;
2704
2706 flags |= EIIT_ONLY_SUMMARIZING;
2708 chgcxt->cc_estate,
2709 flags,
2711 NIL, NULL);
2712 }
2713
2715}
2716
2717static void
2719{
2720 TM_Result res;
2721 TM_FailureData tmfd;
2722
2723 /*
2724 * Delete tuple from the new heap, skipping logical decoding for it.
2725 */
2726 res = table_tuple_delete(rel, &(slot->tts_tid),
2727 GetCurrentCommandId(true),
2730 false,
2731 &tmfd);
2732
2733 if (res != TM_Ok)
2734 ereport(ERROR,
2736 errmsg("could not apply concurrent %s on relation \"%s\"",
2737 "DELETE", RelationGetRelationName(rel)));
2738
2740}
2741
2742/*
2743 * Read tuple from file and put it in the input slot. All memory is allocated
2744 * in the current memory context; caller is responsible for freeing it as
2745 * appropriate.
2746 *
2747 * External attributes are stored in separate memory chunks, in order to avoid
2748 * exceeding MaxAllocSize - that could happen if the individual attributes are
2749 * smaller than MaxAllocSize but the whole tuple is bigger.
2750 */
2751static void
2753{
2754 uint32 t_len;
2755 HeapTuple tup;
2756 int natt_ext;
2757
2758 /* Read the tuple. */
2759 BufFileReadExact(file, &t_len, sizeof(t_len));
2760 tup = (HeapTuple) palloc(HEAPTUPLESIZE + t_len);
2761 tup->t_data = (HeapTupleHeader) ((char *) tup + HEAPTUPLESIZE);
2762 BufFileReadExact(file, tup->t_data, t_len);
2763 tup->t_len = t_len;
2764 ItemPointerSetInvalid(&tup->t_self);
2765 tup->t_tableOid = RelationGetRelid(relation);
2766
2767 /*
2768 * Put the tuple we read in a slot. This deforms it, so that we can hack
2769 * the external attributes in place.
2770 */
2771 ExecForceStoreHeapTuple(tup, slot, false);
2772
2773 /*
2774 * Next, read any attributes we stored separately into the tts_values
2775 * array elements expecting them, if any. This matches
2776 * repack_store_change.
2777 */
2778 BufFileReadExact(file, &natt_ext, sizeof(natt_ext));
2779 if (natt_ext > 0)
2780 {
2781 TupleDesc desc = slot->tts_tupleDescriptor;
2782
2783 for (int i = 0; i < desc->natts; i++)
2784 {
2786 varlena *varlen;
2788 void *value;
2789 Size varlensz;
2790
2791 if (attr->attisdropped || attr->attlen != -1)
2792 continue;
2793 if (slot_attisnull(slot, i + 1))
2794 continue;
2797 continue;
2798 slot_getsomeattrs(slot, i + 1);
2799
2802
2805 BufFileReadExact(file, (char *) value + VARHDRSZ, varlensz - VARHDRSZ);
2806
2808 natt_ext--;
2809 if (natt_ext < 0)
2810 elog(ERROR, "insufficient number of attributes stored separately");
2811 }
2812
2813 if (natt_ext != 0)
2814 elog(ERROR,
2815 "unexpected number of attributes stored separately (%d remaining)",
2816 natt_ext);
2817 }
2818}
2819
2820/*
2821 * Adjust 'dest' replacing any EXTERNAL_ONDISK toast pointers with the
2822 * corresponding ones from 'src'.
2823 */
2824static void
2826{
2827 TupleDesc desc = dest->tts_tupleDescriptor;
2828
2829 for (int i = 0; i < desc->natts; i++)
2830 {
2833
2834 if (attr->attisdropped)
2835 continue;
2836 if (attr->attlen != -1)
2837 continue;
2838 if (slot_attisnull(dest, i + 1))
2839 continue;
2840
2841 slot_getsomeattrs(dest, i + 1);
2842
2843 varlena_dst = (varlena *) DatumGetPointer(dest->tts_values[i]);
2845 continue;
2846 slot_getsomeattrs(src, i + 1);
2847
2848 dest->tts_values[i] = src->tts_values[i];
2849 }
2850}
2851
2852/*
2853 * Find the tuple to be updated or deleted by the given data change, whose
2854 * tuple has already been loaded into locator.
2855 *
2856 * If the tuple is found, put it in retrieved and return true. If the tuple is
2857 * not found, return false.
2858 */
2859static bool
2862{
2863 Form_pg_index idx = chgcxt->cc_ident_index->rd_index;
2864 IndexScanDesc scan;
2865 bool retval = false;
2866
2867 /*
2868 * Scan key is passed by caller, so it does not have to be constructed
2869 * multiple times. Key entries have all fields initialized, except for
2870 * sk_argument.
2871 *
2872 * Use the incoming tuple to finalize the scan key.
2873 */
2874 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
2875 {
2876 ScanKey entry = &chgcxt->cc_ident_key[i];
2877 AttrNumber attno = idx->indkey.values[i];
2878
2879 entry->sk_argument = locator->tts_values[attno - 1];
2880 Assert(!locator->tts_isnull[attno - 1]);
2881 }
2882
2883 /* XXX no instrumentation for now */
2884 scan = index_beginscan(rel, chgcxt->cc_ident_index, GetActiveSnapshot(),
2885 NULL, chgcxt->cc_ident_key_nentries, 0, 0);
2886 index_rescan(scan, chgcxt->cc_ident_key, chgcxt->cc_ident_key_nentries, NULL, 0);
2888 {
2889 /* Be wary of temporal constraints */
2890 if (scan->xs_recheck && !identity_key_equal(chgcxt, locator, retrieved))
2891 {
2893 continue;
2894 }
2895
2896 retval = true;
2897 break;
2898 }
2899 index_endscan(scan);
2900
2901 return retval;
2902}
2903
2904/*
2905 * Check whether the candidate tuple matches the locator tuple on all replica
2906 * identity key columns, using the same equality operators as the identity
2907 * index scan. The locator tuple has already been loaded into cc_ident_key.
2908 *
2909 * This is needed to filter lossy index matches, such as GiST multirange scans
2910 * used for temporal constraints.
2911 */
2912static bool
2915{
2916 slot_getsomeattrs(locator, chgcxt->cc_last_key_attno);
2917 slot_getsomeattrs(candidate, chgcxt->cc_last_key_attno);
2918
2919 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
2920 {
2921 ScanKey entry = &chgcxt->cc_ident_key[i];
2922 AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
2923
2924 Assert(attno > 0);
2925
2926 if (locator->tts_isnull[attno - 1] != candidate->tts_isnull[attno - 1])
2927 return false;
2928
2929 if (locator->tts_isnull[attno - 1])
2930 continue;
2931
2933 entry->sk_collation,
2934 candidate->tts_values[attno - 1],
2935 entry->sk_argument)))
2936 return false;
2937 }
2938
2939 return true;
2940}
2941
2942/*
2943 * Decode and apply concurrent changes, up to (and including) the record whose
2944 * LSN is 'end_of_wal'.
2945 *
2946 * XXX the names "process_concurrent_changes" and "apply_concurrent_changes"
2947 * are far too similar to each other.
2948 */
2949static void
2951{
2952 DecodingWorkerShared *shared;
2953 char fname[MAXPGPATH];
2954 BufFile *file;
2955
2958
2959 /* Ask the worker for the file. */
2961 SpinLockAcquire(&shared->mutex);
2962 shared->lsn_upto = end_of_wal;
2963 shared->done = done;
2964 SpinLockRelease(&shared->mutex);
2965
2966 /*
2967 * The worker needs to finish processing of the current WAL record. Even
2968 * if it's idle, it'll need to close the output file. Thus we're likely to
2969 * wait, so prepare for sleep.
2970 */
2972 for (;;)
2973 {
2974 int last_exported;
2975
2976 SpinLockAcquire(&shared->mutex);
2977 last_exported = shared->last_exported;
2978 SpinLockRelease(&shared->mutex);
2979
2980 /*
2981 * Has the worker exported the file we are waiting for?
2982 */
2983 if (last_exported == chgcxt->cc_file_seq)
2984 break;
2985
2987 }
2989
2990 /* Open the file. */
2991 DecodingWorkerFileName(fname, shared->relid, chgcxt->cc_file_seq);
2992 file = BufFileOpenFileSet(&shared->sfs.fs, fname, O_RDONLY, false);
2994
2995 BufFileClose(file);
2996
2997 /* Get ready for the next file. */
2998 chgcxt->cc_file_seq++;
2999}
3000
3001/*
3002 * Initialize the ChangeContext struct for the given relation, with
3003 * the given index as identity index.
3004 */
3005static void
3007 Relation relation, Oid ident_index_id)
3008{
3009 chgcxt->cc_rel = relation;
3010
3011 /* Only initialize fields needed by ExecInsertIndexTuples(). */
3012 chgcxt->cc_estate = CreateExecutorState();
3013
3014 /*
3015 * Set up a range table for the executor, containing our repacked table as
3016 * its only member.
3017 */
3018 {
3020 TupleDesc desc = RelationGetDescr(relation);
3021 List *perminfos = NIL;
3022 Bitmapset *updatedCols = NULL;
3024
3025 /*
3026 * For our use, the RTE only needs to have perminfoindex initialized,
3027 * but there's no reason to not set the fields whose values we have at
3028 * hand.
3029 */
3031 rte->rtekind = RTE_RELATION;
3032 rte->relid = RelationGetRelid(relation);
3033 rte->relkind = RelationGetForm(relation)->relkind;
3034 /* Create the RTEPermissionInfo instance (and set ->perminfoindex). */
3036
3037 /*
3038 * Initialize updatedCols to show that all columns are updated. This
3039 * is of course not necessarily true, and we cannot know this early;
3040 * but this is only used by ExecInsertIndexTuples to flag index
3041 * updates with no logical value changes, so if it's wrong, nothing
3042 * terribly bad happens. We may want to improve this someday though.
3043 *
3044 * Don't claim that dropped columns are changed though.
3045 */
3046 for (int i = 0; i < desc->natts; i++)
3047 {
3049
3050 if (attr->attisdropped)
3051 continue;
3052 updatedCols = bms_add_member(updatedCols,
3054 }
3055
3056 /* install updatedCols in the right place */
3058 perminfo->updatedCols = updatedCols;
3059
3060 /* finally we can initialize the range table proper */
3063 }
3064
3065 /* Set up our ResultRelInfo to use for index updates */
3066 chgcxt->cc_rri = makeNode(ResultRelInfo);
3067 InitResultRelInfo(chgcxt->cc_rri, relation, 1, NULL, 0);
3068 ExecOpenIndices(chgcxt->cc_rri, false);
3069
3070 /*
3071 * The table's relcache entry already has the relcache entry for the
3072 * identity index; find that.
3073 */
3074 chgcxt->cc_ident_index = NULL;
3075 for (int i = 0; i < chgcxt->cc_rri->ri_NumIndices; i++)
3076 {
3078
3079 ind_rel = chgcxt->cc_rri->ri_IndexRelationDescs[i];
3080 if (ind_rel->rd_id == ident_index_id)
3081 {
3082 chgcxt->cc_ident_index = ind_rel;
3083 break;
3084 }
3085 }
3086 if (chgcxt->cc_ident_index == NULL)
3087 elog(ERROR, "could not find identity index");
3088
3089 /* Set up for scanning said identity index */
3090 {
3092
3093 indexForm = chgcxt->cc_ident_index->rd_index;
3094 chgcxt->cc_ident_key_nentries = indexForm->indnkeyatts;
3095 chgcxt->cc_ident_key = (ScanKey) palloc_array(ScanKeyData, indexForm->indnkeyatts);
3096 for (int i = 0; i < indexForm->indnkeyatts; i++)
3097 {
3098 ScanKey entry;
3099 Oid opfamily,
3100 opcintype,
3101 opno,
3102 opcode;
3104
3105 entry = &chgcxt->cc_ident_key[i];
3106
3107 opfamily = chgcxt->cc_ident_index->rd_opfamily[i];
3108 opcintype = chgcxt->cc_ident_index->rd_opcintype[i];
3110 chgcxt->cc_ident_index->rd_rel->relam,
3111 opfamily, false);
3113 elog(ERROR, "could not find equality strategy for index operator family %u for type %u",
3114 opfamily, opcintype);
3115 opno = get_opfamily_member(opfamily, opcintype, opcintype,
3116 eq_strategy);
3117 if (!OidIsValid(opno))
3118 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
3119 eq_strategy, opcintype, opcintype, opfamily);
3120 opcode = get_opcode(opno);
3121 if (!OidIsValid(opcode))
3122 elog(ERROR, "missing oprcode for operator %u", opno);
3123
3124 /* Initialize everything but argument. */
3125 ScanKeyInit(entry,
3126 i + 1,
3127 eq_strategy, opcode,
3128 (Datum) 0);
3129 entry->sk_collation = chgcxt->cc_ident_index->rd_indcollation[i];
3130 }
3131 }
3132
3133 /* Determine the last column we must deform to read the identity */
3134 chgcxt->cc_last_key_attno = InvalidAttrNumber;
3135 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
3136 {
3137 AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
3138
3139 Assert(attno > 0);
3140 chgcxt->cc_last_key_attno = Max(chgcxt->cc_last_key_attno, attno);
3141 }
3142
3143 chgcxt->cc_file_seq = WORKER_FILE_SNAPSHOT + 1;
3144}
3145
3146/*
3147 * Free up resources taken by a ChangeContext.
3148 */
3149static void
3151{
3152 ExecCloseIndices(chgcxt->cc_rri);
3153 FreeExecutorState(chgcxt->cc_estate);
3154 /* XXX are these pfrees necessary? */
3155 pfree(chgcxt->cc_rri);
3156 pfree(chgcxt->cc_ident_key);
3157}
3158
3159/*
3160 * The final steps of rebuild_relation() for concurrent processing.
3161 *
3162 * On entry, NewHeap is locked in AccessExclusiveLock mode. OldHeap and its
3163 * clustering index (if one is passed) are still locked in a mode that allows
3164 * concurrent data changes. On exit, both tables and their indexes are closed,
3165 * but locked in AccessExclusiveLock mode.
3166 */
3167static void
3171{
3176 ListCell *lc,
3177 *lc2;
3178 char relpersistence;
3179 bool is_system_catalog;
3181 XLogRecPtr end_of_wal;
3182 List *indexrels;
3184
3187
3188 /*
3189 * Unlike the exclusive case, we build new indexes for the new relation
3190 * rather than swapping the storage and reindexing the old relation. The
3191 * point is that the index build can take some time, so we do it before we
3192 * get AccessExclusiveLock on the old heap and therefore we cannot swap
3193 * the heap storage yet.
3194 *
3195 * index_create() will lock the new indexes using AccessExclusiveLock - no
3196 * need to change that. At the same time, we use ShareUpdateExclusiveLock
3197 * to lock the existing indexes - that should be enough to prevent others
3198 * from changing them while we're repacking the relation. The lock on
3199 * table should prevent others from changing the index column list, but
3200 * might not be enough for commands like ALTER INDEX ... SET ... (Those
3201 * are not necessarily dangerous, but can make user confused if the
3202 * changes they do get lost due to REPACK.)
3203 */
3205
3206 /*
3207 * The identity index in the new relation appears in the same relative
3208 * position as the corresponding index in the old relation. Find it.
3209 */
3212 {
3213 if (identIdx == ind_old)
3214 {
3215 int pos = foreach_current_index(ind_old);
3216
3217 if (list_length(ind_oids_new) <= pos)
3218 elog(ERROR, "list of new indexes too short");
3220 break;
3221 }
3222 }
3224 elog(ERROR, "could not find index matching \"%s\" at the new relation",
3226
3227 /* Gather information to apply concurrent changes. */
3229
3230 /*
3231 * During testing, wait for another backend to perform concurrent data
3232 * changes which we will process below.
3233 */
3234 INJECTION_POINT("repack-concurrently-before-lock", NULL);
3235
3236 /*
3237 * Flush all WAL records inserted so far (possibly except for the last
3238 * incomplete page; see GetInsertRecPtr), to minimize the amount of data
3239 * we need to flush while holding exclusive lock on the source table.
3240 */
3242 end_of_wal = GetFlushRecPtr(NULL);
3243
3244 /*
3245 * Apply concurrent changes first time, to minimize the time we need to
3246 * hold AccessExclusiveLock. (Quite some amount of WAL could have been
3247 * written during the data copying and index creation.)
3248 */
3249 process_concurrent_changes(end_of_wal, &chgcxt, false);
3250
3251 /*
3252 * Acquire AccessExclusiveLock on the table, its TOAST relation (if there
3253 * is one), all its indexes, so that we can swap the files.
3254 */
3256
3257 /*
3258 * Lock all indexes now, not only the clustering one: all indexes need to
3259 * have their files swapped. While doing that, store their relation
3260 * references in a zero-terminated array, to handle predicate locks below.
3261 */
3262 indexrels = NIL;
3264 {
3266
3268
3269 /*
3270 * Some things about the index may have changed before we locked the
3271 * index, such as ALTER INDEX RENAME. We don't need to do anything
3272 * here to absorb those changes in the new index.
3273 */
3275 }
3276
3277 /*
3278 * Lock the OldHeap's TOAST relation exclusively - again, the lock is
3279 * needed to swap the files.
3280 */
3281 if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
3282 LockRelationOid(OldHeap->rd_rel->reltoastrelid, AccessExclusiveLock);
3283
3284 /*
3285 * Tuples and pages of the old heap will be gone, but the heap will stay.
3286 */
3289 {
3292 }
3294
3295 /*
3296 * Flush WAL again, to make sure that all changes committed while we were
3297 * waiting for the exclusive lock are available for decoding.
3298 */
3300 end_of_wal = GetFlushRecPtr(NULL);
3301
3302 /*
3303 * Apply the concurrent changes again. Indicate that the decoding worker
3304 * won't be needed anymore.
3305 */
3306 process_concurrent_changes(end_of_wal, &chgcxt, true);
3307
3308 /* Remember info about rel before closing OldHeap */
3309 relpersistence = OldHeap->rd_rel->relpersistence;
3311
3314
3315 /*
3316 * Even ShareUpdateExclusiveLock should have prevented others from
3317 * creating / dropping indexes (even using the CONCURRENTLY option), so we
3318 * do not need to check whether the lists match.
3319 */
3321 {
3324 Oid mapped_tables[4] = {0};
3325
3328 false, /* swap_toast_by_content */
3329 true,
3333
3334#ifdef USE_ASSERT_CHECKING
3335
3336 /*
3337 * Concurrent processing is not supported for system relations, so
3338 * there should be no mapped tables.
3339 */
3340 for (int i = 0; i < 4; i++)
3342#endif
3343 }
3344
3345 /* The new indexes must be visible for deletion. */
3347
3348 /* Close the old heap but keep lock until transaction commit. */
3350 /* Close the new heap. (We didn't have to open its indexes). */
3352
3353 /* Cleanup what we don't need anymore. (And close the identity index.) */
3355
3356 /*
3357 * Swap the relations and their TOAST relations and TOAST indexes. This
3358 * also drops the new relation and its indexes.
3359 *
3360 * (System catalogs are currently not supported.)
3361 */
3365 false, /* swap_toast_by_content */
3366 false,
3367 true,
3368 false, /* reindex */
3370 relpersistence);
3371}
3372
3373/*
3374 * Build indexes on NewHeap according to those on OldHeap.
3375 *
3376 * OldIndexes is the list of index OIDs on OldHeap. The contained indexes end
3377 * up locked using ShareUpdateExclusiveLock.
3378 *
3379 * A list of OIDs of the corresponding indexes created on NewHeap is
3380 * returned. The order of items does match, so we can use these arrays to swap
3381 * index storage.
3382 */
3383static List *
3415
3416/*
3417 * Create a transient copy of a constraint -- supported by a transient
3418 * copy of the index that supports the original constraint.
3419 *
3420 * When repacking a table that contains exclusion constraints, the executor
3421 * relies on these constraints being properly catalogued. These copies are
3422 * to support that.
3423 *
3424 * We don't need the constraints for anything else (the original constraints
3425 * will be there once repack completes), so we add pg_depend entries so that
3426 * they are dropped when the transient table is dropped.
3427 */
3428static void
3430{
3432 Relation rel;
3433 TupleDesc desc;
3434 SysScanDesc scan;
3435 HeapTuple tup;
3437
3440
3441 /*
3442 * Retrieve the constraints supported by the old index and create an
3443 * identical one that points to the new index.
3444 */
3448 ObjectIdGetDatum(old_index->rd_index->indrelid));
3450 NULL, 1, &skey);
3451 desc = RelationGetDescr(rel);
3452 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3453 {
3455 Oid oid;
3457 bool nulls[Natts_pg_constraint] = {0};
3458 bool replaces[Natts_pg_constraint] = {0};
3461
3462 if (conform->conindid != RelationGetRelid(old_index))
3463 continue;
3464
3468 replaces[Anum_pg_constraint_oid - 1] = true;
3473
3474 new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
3475
3476 /* Insert it into the catalog. */
3478
3479 /* Create a dependency so it's removed when we drop the new heap. */
3482 }
3483 systable_endscan(scan);
3484
3486
3488}
3489
3490/*
3491 * Create a transient copy of attribute defaults.
3492 *
3493 * When repacking a table that has stored generated columns, the executor
3494 * relies on these entries to generate the values for them during apply of
3495 * concurrent operations. These copies are there to support that.
3496 *
3497 * We don't need the defaults for anything else, so we add pg_depend entries
3498 * so that they are dropped when the transient table is dropped.
3499 */
3500static void
3502{
3504 Relation rel;
3506 SysScanDesc scan;
3509
3512
3514
3519 scan = systable_beginscan(rel, AttrDefaultIndexId, true,
3520 NULL, 1, &skey);
3522 {
3524 Oid oid;
3527 bool def_replaces[Natts_pg_attrdef] = {0};
3530 bool att_replaces[Natts_pg_attribute] = {0};
3532 att_tup,
3535
3537 Assert(adform->adrelid == old_heap_oid);
3538
3539 /*
3540 * Insert a new tuple that's identical to the existing one, other than
3541 * its OID and the relation it refers to.
3542 */
3546 def_nulls[Anum_pg_attrdef_oid - 1] = false;
3554
3555 /* Set atthasdef for this attribute in the transient table */
3558 ObjectIdGetDatum(adform->adnum));
3560 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
3561 adform->adnum, new_heap_oid);
3569
3570 /* Add a pg_depend record so it's removed with the transient table */
3573 }
3574 systable_endscan(scan);
3575
3578
3580}
3581
3582/*
3583 * Try to start a background worker to perform logical decoding of data
3584 * changes applied to relation while REPACK CONCURRENTLY is copying its
3585 * contents to a new table.
3586 */
3587static void
3589{
3590 Size size;
3591 DecodingWorkerShared *shared;
3592 shm_mq *mq;
3594
3596
3597 /* Setup shared memory. */
3598 size = BUFFERALIGN(offsetof(DecodingWorkerShared, error_queue)) +
3600 decoding_worker->seg = dsm_create(size, 0);
3601
3603 shared->initialized = false;
3604 shared->lsn_upto = InvalidXLogRecPtr;
3605 shared->done = false;
3607 shared->last_exported = -1;
3608 SpinLockInit(&shared->mutex);
3609 shared->dbid = MyDatabaseId;
3610
3611 /*
3612 * This is the UserId set in cluster_rel(). Security context shouldn't be
3613 * needed for decoding worker.
3614 */
3615 shared->roleid = GetUserId();
3616 shared->relid = relid;
3617 ConditionVariableInit(&shared->cv);
3618 shared->backend_proc = MyProc;
3619 shared->backend_pid = MyProcPid;
3621
3622 mq = shm_mq_create((char *) BUFFERALIGN(shared->error_queue),
3625
3627
3628 memset(&bgw, 0, sizeof(bgw));
3629 snprintf(bgw.bgw_name, BGW_MAXLEN,
3630 "REPACK decoding worker for relation \"%s\"",
3631 get_rel_name(relid));
3632 snprintf(bgw.bgw_type, BGW_MAXLEN, "REPACK decoding worker");
3633 bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
3635 bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
3636 bgw.bgw_restart_time = BGW_NEVER_RESTART;
3637 snprintf(bgw.bgw_library_name, MAXPGPATH, "postgres");
3638 snprintf(bgw.bgw_function_name, BGW_MAXLEN, "RepackWorkerMain");
3640 bgw.bgw_notify_pid = MyProcPid;
3641
3643 ereport(ERROR,
3645 errmsg("out of background worker slots"),
3646 errhint("You might need to increase \"%s\".", "max_worker_processes"));
3647
3648 /*
3649 * The decoding setup must be done before the caller can have XID assigned
3650 * for any reason, otherwise the worker might end up in a deadlock,
3651 * waiting for the caller's transaction to end. Therefore wait here until
3652 * the worker indicates that it has the logical decoding initialized.
3653 */
3655 for (;;)
3656 {
3657 bool initialized;
3658
3659 SpinLockAcquire(&shared->mutex);
3660 initialized = shared->initialized;
3661 SpinLockRelease(&shared->mutex);
3662
3663 if (initialized)
3664 break;
3665
3667 }
3669}
3670
3671/*
3672 * Stop the decoding worker and cleanup the related resources.
3673 *
3674 * The worker stops on its own when it knows there is no more work to do, but
3675 * we need to stop it explicitly at least on ERROR in the launching backend.
3676 */
3677static void
3679{
3680 /* Nothing to do if no worker was set up. */
3681 if (decoding_worker == NULL)
3682 return;
3683
3684 /* Terminate the worker process, if one is running. */
3685 if (decoding_worker->handle != NULL)
3686 {
3687 BgwHandleStatus status;
3688
3690 /* The worker should really exit before the REPACK command does. */
3694
3695 if (status == BGWH_POSTMASTER_DIED)
3696 ereport(FATAL,
3698 errmsg("postmaster exited during REPACK command"));
3699 }
3700
3701 /*
3702 * Now detach from our shared memory segment. In error cases there might
3703 * still be messages from the worker in the queue, which ProcessInterrupts
3704 * would try to read; this is pointless (and causes an assertion failure),
3705 * so set the global pointer to NULL to have ProcessRepackMessages ignore
3706 * them.
3707 *
3708 * We must also cancel the current sleep, if one is still set up. This is
3709 * critical because the CV lives in the DSM that we're about to detach, so
3710 * if we omit it, later automatic cleanup tries to clear freed memory.
3711 */
3715 if (decoding_worker->seg != NULL)
3719}
3720
3721/* stop_repack_decoding_worker, wrapped as a before_shmem_exit callback */
3722static void
3727
3728/*
3729 * Get the initial snapshot from the decoding worker.
3730 */
3731static Snapshot
3733{
3734 DecodingWorkerShared *shared;
3735 char fname[MAXPGPATH];
3736 BufFile *file;
3738 char *snap_space;
3739 Snapshot snapshot;
3740
3741 shared = (DecodingWorkerShared *) dsm_segment_address(worker->seg);
3742
3743 /*
3744 * The worker needs to initialize the logical decoding, which usually
3745 * takes some time. Therefore it makes sense to prepare for the sleep
3746 * first.
3747 */
3749 for (;;)
3750 {
3751 int last_exported;
3752
3753 SpinLockAcquire(&shared->mutex);
3754 last_exported = shared->last_exported;
3755 SpinLockRelease(&shared->mutex);
3756
3757 /*
3758 * Has the worker exported the file we are waiting for?
3759 */
3760 if (last_exported == WORKER_FILE_SNAPSHOT)
3761 break;
3762
3764 }
3766
3767 /* Read the snapshot from a file. */
3769 file = BufFileOpenFileSet(&shared->sfs.fs, fname, O_RDONLY, false);
3770 BufFileReadExact(file, &snap_size, sizeof(snap_size));
3771 snap_space = (char *) palloc(snap_size);
3773 BufFileClose(file);
3774
3775 /* Restore it. */
3776 snapshot = RestoreSnapshot(snap_space);
3778
3779 return snapshot;
3780}
3781
3782/*
3783 * Generate worker's file name into 'fname', which must be of size MAXPGPATH.
3784 * If relations of the same 'relid' happen to be processed at the same time,
3785 * they must be from different databases and therefore different backends must
3786 * be involved.
3787 */
3788void
3790{
3791 /* The PID is already present in the fileset name, so we needn't add it */
3792 snprintf(fname, MAXPGPATH, "%u-%u", relid, seq);
3793}
3794
3795/*
3796 * Handle receipt of an interrupt indicating a repack worker message.
3797 *
3798 * Note: this is called within a signal handler! All we can do is set
3799 * a flag that will cause the next CHECK_FOR_INTERRUPTS() to invoke
3800 * ProcessRepackMessages().
3801 */
3802void
3804{
3805 InterruptPending = true;
3806 RepackMessagePending = true;
3808}
3809
3810/*
3811 * Process any queued protocol messages received from the repack worker.
3812 */
3813void
3815{
3816 MemoryContext oldcontext;
3818
3819 /*
3820 * Nothing to do if we haven't launched the worker yet or have already
3821 * terminated it.
3822 */
3823 if (decoding_worker == NULL)
3824 return;
3825
3826 /*
3827 * This is invoked from ProcessInterrupts(), and since some of the
3828 * functions it calls contain CHECK_FOR_INTERRUPTS(), there is a potential
3829 * for recursive calls if more signals are received while this runs. It's
3830 * unclear that recursive entry would be safe, and it doesn't seem useful
3831 * even if it is safe, so let's block interrupts until done.
3832 */
3834
3835 /*
3836 * Moreover, CurrentMemoryContext might be pointing almost anywhere. We
3837 * don't want to risk leaking data into long-lived contexts, so let's do
3838 * our work here in a private context that we can reset on each use.
3839 */
3840 if (hpm_context == NULL) /* first time through? */
3842 "ProcessRepackMessages",
3844 else
3846
3847 oldcontext = MemoryContextSwitchTo(hpm_context);
3848
3849 /* OK to process messages. Reset the flag saying there are more to do. */
3850 RepackMessagePending = false;
3851
3852 /*
3853 * Read as many messages as we can from the worker, but stop when no more
3854 * messages can be read from the worker without blocking.
3855 */
3856 while (true)
3857 {
3858 shm_mq_result res;
3859 Size nbytes;
3860 void *data;
3861
3863 &data, true);
3864 if (res == SHM_MQ_WOULD_BLOCK)
3865 break;
3866 else if (res == SHM_MQ_SUCCESS)
3867 {
3868 StringInfoData msg;
3869
3870 initStringInfo(&msg);
3871 appendBinaryStringInfo(&msg, data, nbytes);
3873 pfree(msg.data);
3874 }
3875 else
3876 {
3877 /*
3878 * The decoding worker is special in that it exits as soon as it
3879 * has its work done. Thus the DETACHED result code is fine.
3880 */
3881 Assert(res == SHM_MQ_DETACHED);
3882
3883 break;
3884 }
3885 }
3886
3887 MemoryContextSwitchTo(oldcontext);
3888
3889 /* Might as well clear the context on our way out */
3891
3893}
3894
3895/*
3896 * Process a single protocol message received from a single parallel worker.
3897 */
3898static void
3900{
3901 char msgtype;
3902
3903 msgtype = pq_getmsgbyte(msg);
3904
3905 switch (msgtype)
3906 {
3909 {
3911
3912 /* Parse ErrorResponse or NoticeResponse. */
3914
3915 /* Death of a worker isn't enough justification for suicide. */
3916 edata.elevel = Min(edata.elevel, ERROR);
3917
3918 /*
3919 * Add a context line to show that this is a message
3920 * propagated from the worker. Otherwise, it can sometimes be
3921 * confusing to understand what actually happened.
3922 */
3923 if (edata.context)
3924 edata.context = psprintf("%s\n%s", edata.context,
3925 _("REPACK decoding worker"));
3926 else
3927 edata.context = pstrdup(_("REPACK decoding worker"));
3928
3929 /* Rethrow error or print notice. */
3931
3932 break;
3933 }
3934
3935 default:
3936 {
3937 elog(ERROR, "unrecognized message type received from decoding worker: %c (message length %d bytes)",
3938 msgtype, msg->len);
3939 }
3940 }
3941}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
AclResult pg_class_aclcheck_ext(Oid table_oid, Oid roleid, AclMode mode, bool *is_missing)
Definition aclchk.c:4115
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition amapi.c:161
int16 AttrNumber
Definition attnum.h:21
#define InvalidAttrNumber
Definition attnum.h:23
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_incr_param(int index, int64 incr)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_REPACK
void TerminateBackgroundWorker(BackgroundWorkerHandle *handle)
Definition bgworker.c:1319
BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
Definition bgworker.c:1280
bool RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle)
Definition bgworker.c:1068
#define BGW_NEVER_RESTART
Definition bgworker.h:92
BgwHandleStatus
Definition bgworker.h:111
@ BGWH_POSTMASTER_DIED
Definition bgworker.h:115
@ BgWorkerStart_RecoveryFinished
Definition bgworker.h:88
#define BGWORKER_BACKEND_DATABASE_CONNECTION
Definition bgworker.h:60
#define BGWORKER_SHMEM_ACCESS
Definition bgworker.h:53
#define BGW_MAXLEN
Definition bgworker.h:93
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:217
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
uint32 BlockNumber
Definition block.h:31
static Datum values[MAXATTR]
Definition bootstrap.c:190
BufFile * BufFileOpenFileSet(FileSet *fileset, const char *name, int mode, bool missing_ok)
Definition buffile.c:292
void BufFileReadExact(BufFile *file, void *ptr, size_t size)
Definition buffile.c:659
size_t BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
Definition buffile.c:669
void BufFileClose(BufFile *file)
Definition buffile.c:413
#define RelationGetNumberOfBlocks(reln)
Definition bufmgr.h:309
#define NameStr(name)
Definition c.h:894
#define Min(x, y)
Definition c.h:1131
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
#define Max(x, y)
Definition c.h:1125
#define BUFFERALIGN(LEN)
Definition c.h:957
#define VARHDRSZ
Definition c.h:840
#define Assert(condition)
Definition c.h:1002
TransactionId MultiXactId
Definition c.h:805
int32_t int32
Definition c.h:679
uint64_t uint64
Definition c.h:684
uint32_t uint32
Definition c.h:683
float float4
Definition c.h:772
uint32 TransactionId
Definition c.h:795
#define OidIsValid(objectId)
Definition c.h:917
size_t Size
Definition c.h:748
bool IsToastRelation(Relation relation)
Definition catalog.c:208
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:475
bool IsCatalogRelation(Relation relation)
Definition catalog.c:106
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition catalog.c:86
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
@ COMPARE_EQ
Definition cmptype.h:36
void analyze_rel(Oid relid, RangeVar *relation, const VacuumParams *params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
Definition analyze.c:110
bool ConditionVariableCancelSleep(void)
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
void ConditionVariableInit(ConditionVariable *cv)
void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
bool defGetBoolean(DefElem *def)
Definition define.c:93
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:279
@ DEPENDENCY_AUTO
Definition dependency.h:34
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
#define PERFORM_DELETION_INTERNAL
Definition dependency.h:92
dsm_handle dsm_segment_handle(dsm_segment *seg)
Definition dsm.c:1131
void dsm_detach(dsm_segment *seg)
Definition dsm.c:811
void * dsm_segment_address(dsm_segment *seg)
Definition dsm.c:1103
dsm_segment * dsm_create(Size size, int flags)
Definition dsm.c:524
Datum arg
Definition elog.c:1323
void ThrowErrorData(ErrorData *edata)
Definition elog.c:2091
int errcode(int sqlerrcode)
Definition elog.c:875
#define _(x)
Definition elog.c:96
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define FATAL
Definition elog.h:42
#define WARNING
Definition elog.h:37
#define DEBUG2
Definition elog.h:30
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define INFO
Definition elog.h:35
#define ereport(elevel,...)
Definition elog.h:152
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, EState *estate, uint32 flags, TupleTableSlot *slot, List *arbiterIndexes, bool *specConflict)
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition execMain.c:1308
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
void ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos, Bitmapset *unpruned_relids)
Definition execUtils.c:799
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define ResetPerTupleExprContext(estate)
Definition executor.h:674
#define EIIT_IS_UPDATE
Definition executor.h:755
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
#define EIIT_ONLY_SUMMARIZING
Definition executor.h:757
#define palloc_object(type)
Definition fe_memutils.h:89
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define palloc0_object(type)
Definition fe_memutils.h:90
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition fmgr.c:1151
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
volatile sig_atomic_t InterruptPending
Definition globals.c:32
int MyProcPid
Definition globals.c:49
ProcNumber MyProcNumber
Definition globals.c:92
bool allowSystemTableMods
Definition globals.c:132
struct Latch * MyLatch
Definition globals.c:65
Oid MyDatabaseId
Definition globals.c:96
int NewGUCNestLevel(void)
Definition guc.c:2142
void RestrictSearchPath(void)
Definition guc.c:2153
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
void RelationClearMissing(Relation rel)
Definition heap.c:1984
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:1140
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1436
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1118
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
#define HEAPTUPLESIZE
Definition htup.h:73
HeapTupleData * HeapTuple
Definition htup.h:71
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
#define stmt
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3604
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition index.c:3969
Oid index_create_copy(Relation heapRelation, uint16 flags, Oid oldIndexId, Oid tablespaceOid, const char *newName)
Definition index.c:1306
#define INDEX_CREATE_SUPPRESS_PROGRESS
Definition index.h:74
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition index.h:169
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition index.h:167
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition index.h:170
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition index.h:168
bool index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
Definition indexam.c:698
IndexScanDesc index_beginscan(Relation heapRelation, Relation indexRelation, Snapshot snapshot, IndexScanInstrumentation *instrument, int nkeys, int norderbys, uint32 flags)
Definition indexam.c:257
void index_close(Relation relation, LOCKMODE lockmode)
Definition indexam.c:178
void index_endscan(IndexScanDesc scan)
Definition indexam.c:394
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition indexam.c:134
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition indexam.c:368
char * ChooseRelationName(const char *name1, const char *name2, const char *label, Oid namespaceid, bool isconstraint)
Definition indexcmds.c:2644
void CatalogTupleUpdateWithInfo(Relation heapRel, const ItemPointerData *otid, HeapTuple tup, CatalogIndexState indstate)
Definition indexing.c:337
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition indexing.c:61
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition indexing.c:43
static struct @175 value
#define INJECTION_POINT(name, arg)
void CacheInvalidateCatalog(Oid catalogId)
Definition inval.c:1609
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666
#define PG_ENSURE_ERROR_CLEANUP(cleanup_function, arg)
Definition ipc.h:47
#define PG_END_ENSURE_ERROR_CLEANUP(cleanup_function, arg)
Definition ipc.h:52
int i
Definition isn.c:77
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
void SetLatch(Latch *latch)
Definition latch.c:290
List * lappend(List *list, void *datum)
Definition list.c:339
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
void list_free(List *list)
Definition list.c:1546
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 ShareUpdateExclusiveLock
Definition lockdefs.h:39
#define RowExclusiveLock
Definition lockdefs.h:38
LockTupleMode
Definition lockoptions.h:51
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2317
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2266
RegProcedure get_opcode(Oid opno)
Definition lsyscache.c:1585
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3962
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:170
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3682
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2199
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
MemoryContext TopMemoryContext
Definition mcxt.c:167
void * palloc(Size size)
Definition mcxt.c:1390
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
MemoryContext PortalContext
Definition mcxt.c:176
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define RESUME_INTERRUPTS()
Definition miscadmin.h:138
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:322
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
#define HOLD_INTERRUPTS()
Definition miscadmin.h:136
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
Oid GetUserId(void)
Definition miscinit.c:470
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition multixact.c:2865
#define MultiXactIdIsValid(multi)
Definition multixact.h:29
#define InvalidMultiXactId
Definition multixact.h:25
bool isTempOrTempToastNamespace(Oid namespaceId)
Definition namespace.c:3745
Oid LookupCreationNamespace(const char *nspname)
Definition namespace.c:3500
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition namespace.c:442
#define makeNode(_type_)
Definition nodes.h:159
static char * errmsg
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
#define ObjectAddressSet(addr, class_id, object_id)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
int parser_errposition(ParseState *pstate, int location)
Definition parse_node.c:106
RTEPermissionInfo * getRTEPermissionInfo(List *rteperminfos, RangeTblEntry *rte)
RTEPermissionInfo * addRTEPermissionInfo(List **rteperminfos, RangeTblEntry *rte)
RepackCommand
@ REPACK_COMMAND_REPACK
@ REPACK_COMMAND_CLUSTER
@ REPACK_COMMAND_VACUUMFULL
#define ACL_MAINTAIN
Definition parsenodes.h:90
@ RTE_RELATION
@ DROP_RESTRICT
END_CATALOG_STRUCT typedef FormData_pg_attrdef * Form_pg_attrdef
Definition pg_attrdef.h:53
static int verbose
NameData relname
Definition pg_class.h:40
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
#define NAMEDATALEN
#define MAXPGPATH
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
const void * data
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:51
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:470
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:314
END_CATALOG_STRUCT typedef FormData_pg_index * Form_pg_index
Definition pg_index.h:74
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
#define foreach_current_index(var_or_cell)
Definition pg_list.h:435
static Oid list_nth_oid(const List *list, int n)
Definition pg_list.h:353
#define list_make1(x1)
Definition pg_list.h:244
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define foreach_node(type, var, lst)
Definition pg_list.h:528
#define foreach_oid(var, lst)
Definition pg_list.h:503
#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
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
bool plan_cluster_use_sort(Oid tableOid, Oid indexOid)
Definition planner.c:7090
#define snprintf
Definition port.h:261
static bool DatumGetBool(Datum X)
Definition postgres.h:100
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static Datum UInt32GetDatum(uint32 X)
Definition postgres.h:232
#define PointerGetDatum(X)
Definition postgres.h:354
#define InvalidOid
unsigned int Oid
int pq_getmsgbyte(StringInfo msg)
Definition pqformat.c:398
void pq_parse_errornotice(StringInfo msg, ErrorData *edata)
Definition pqmq.c:228
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition predicate.c:3052
static int fb(int x)
@ ONCOMMIT_NOOP
Definition primnodes.h:59
#define PROGRESS_REPACK_PHASE_CATCH_UP
Definition progress.h:103
#define PROGRESS_REPACK_PHASE
Definition progress.h:86
#define PROGRESS_REPACK_COMMAND
Definition progress.h:85
#define PROGRESS_REPACK_PHASE_SWAP_REL_FILES
Definition progress.h:104
#define PROGRESS_REPACK_HEAP_TUPLES_DELETED
Definition progress.h:91
#define PROGRESS_REPACK_HEAP_TUPLES_UPDATED
Definition progress.h:90
#define PROGRESS_REPACK_PHASE_FINAL_CLEANUP
Definition progress.h:106
#define PROGRESS_REPACK_PHASE_REBUILD_INDEX
Definition progress.h:105
#define PROGRESS_REPACK_HEAP_TUPLES_INSERTED
Definition progress.h:89
#define PqMsg_ErrorResponse
Definition protocol.h:44
#define PqMsg_NoticeResponse
Definition protocol.h:49
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
static long analyze(struct nfa *nfa)
Definition regc_nfa.c:3051
#define RelationGetForm(relation)
Definition rel.h:510
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetDescr(relation)
Definition rel.h:542
#define RelationIsMapped(relation)
Definition rel.h:565
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationIsPopulated(relation)
Definition rel.h:697
#define RELATION_IS_OTHER_TEMP(relation)
Definition rel.h:678
#define RelationGetNamespace(relation)
Definition rel.h:557
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4848
void RelationAssumeNewRelfilelocator(Relation relation)
Definition relcache.c:3982
void RelationMapRemoveMapping(Oid relationId)
Definition relmapper.c:439
RelFileNumber RelationMapOidToFilenumber(Oid relationId, bool shared)
Definition relmapper.c:166
void RelationMapUpdateMap(Oid relationId, RelFileNumber fileNumber, bool shared, bool immediate)
Definition relmapper.c:326
Oid RelFileNumber
Definition relpath.h:25
#define RelFileNumberIsValid(relnumber)
Definition relpath.h:27
static bool cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid userid, LOCKMODE lmode, int options)
Definition repack.c:719
static void restore_tuple(BufFile *file, Relation relation, TupleTableSlot *slot)
Definition repack.c:2752
static void start_repack_decoding_worker(Oid relid)
Definition repack.c:3588
static void check_concurrent_repack_requirements(Relation rel, Oid *ident_idx_p)
Definition repack.c:908
static bool find_target_tuple(Relation rel, ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *retrieved)
Definition repack.c:2860
static List * get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid, bool rel_is_index, MemoryContext permcxt)
Definition repack.c:2265
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, bool reindex, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition repack.c:1931
static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, ClusterParams *params)
Definition repack.c:2377
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:782
static void release_change_context(ChangeContext *chgcxt)
Definition repack.c:3150
static bool repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
Definition repack.c:2333
void ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
Definition repack.c:250
static void stop_repack_decoding_worker(void)
Definition repack.c:3678
static LOCKMODE RepackLockLevel(bool concurrent)
Definition repack.c:499
static void apply_concurrent_delete(Relation rel, TupleTableSlot *slot)
Definition repack.c:2718
static void ProcessRepackMessage(StringInfo msg)
Definition repack.c:3899
volatile sig_atomic_t RepackMessagePending
Definition repack.c:154
void cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, ClusterParams *params, bool isTopLevel)
Definition repack.c:532
static List * get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
Definition repack.c:2152
static const char * RepackCommandAsString(RepackCommand cmd)
Definition repack.c:2511
void DecodingWorkerFileName(char *fname, Oid relid, uint32 seq)
Definition repack.c:3789
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition repack.c:1174
static void initialize_change_context(ChangeContext *chgcxt, Relation relation, Oid ident_index_id)
Definition repack.c:3006
static bool identity_key_equal(ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *candidate)
Definition repack.c:2913
static void process_concurrent_changes(XLogRecPtr end_of_wal, ChangeContext *chgcxt, bool done)
Definition repack.c:2950
static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, Snapshot snapshot, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
Definition repack.c:1303
static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHeap, Oid identIdx, TransactionId frozenXid, MultiXactId cutoffMulti)
Definition repack.c:3168
static void copy_attribute_defaults(Oid old_heap_oid, Oid new_heap_oid)
Definition repack.c:3501
static void apply_concurrent_insert(Relation rel, TupleTableSlot *slot, ChangeContext *chgcxt)
Definition repack.c:2655
static void stop_repack_decoding_worker_cb(int code, Datum arg)
Definition repack.c:3723
static void apply_concurrent_changes(BufFile *file, ChangeContext *chgcxt)
Definition repack.c:2529
static void rebuild_relation(Relation OldHeap, Relation index, bool verbose, Oid ident_idx)
Definition repack.c:1020
void HandleRepackMessageInterrupt(void)
Definition repack.c:3803
static Snapshot get_initial_snapshot(DecodingWorker *worker)
Definition repack.c:3732
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition repack.c:842
static void adjust_toast_pointers(Relation relation, TupleTableSlot *dest, TupleTableSlot *src)
Definition repack.c:2825
#define WORKER_FILE_SNAPSHOT
Definition repack.c:102
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 repack.c:1549
static Oid determine_clustered_index(Relation rel, bool usingindex, const char *indexname)
Definition repack.c:2468
void ProcessRepackMessages(void)
Definition repack.c:3814
static void copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
Definition repack.c:3429
static void apply_concurrent_update(Relation rel, TupleTableSlot *spilled_tuple, TupleTableSlot *ondisk_tuple, ChangeContext *chgcxt)
Definition repack.c:2676
static DecodingWorker * decoding_worker
Definition repack.c:148
static List * build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
Definition repack.c:3384
#define CLUOPT_VERBOSE
Definition repack.h:25
#define CLUOPT_ANALYZE
Definition repack.h:28
#define CLUOPT_CONCURRENT
Definition repack.h:29
#define CLUOPT_RECHECK_ISCLUSTERED
Definition repack.h:27
#define CLUOPT_RECHECK
Definition repack.h:26
#define CHANGE_UPDATE_OLD
#define CHANGE_DELETE
#define CHANGE_UPDATE_NEW
char ConcurrentChangeKind
#define CHANGE_INSERT
#define REPACK_ERROR_QUEUE_SIZE
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
@ ForwardScanDirection
Definition sdir.h:28
void SharedFileSetInit(SharedFileSet *fileset, dsm_segment *seg)
shm_mq * shm_mq_create(void *address, Size size)
Definition shm_mq.c:179
void shm_mq_detach(shm_mq_handle *mqh)
Definition shm_mq.c:845
void shm_mq_set_receiver(shm_mq *mq, PGPROC *proc)
Definition shm_mq.c:208
shm_mq_result shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
Definition shm_mq.c:574
shm_mq_handle * shm_mq_attach(shm_mq *mq, dsm_segment *seg, BackgroundWorkerHandle *handle)
Definition shm_mq.c:292
shm_mq_result
Definition shm_mq.h:39
@ SHM_MQ_SUCCESS
Definition shm_mq.h:40
@ SHM_MQ_WOULD_BLOCK
Definition shm_mq.h:41
@ SHM_MQ_DETACHED
Definition shm_mq.h:42
ScanKeyData * ScanKey
Definition skey.h:75
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
Snapshot RestoreSnapshot(char *start_address)
Definition snapmgr.c:1792
void UpdateActiveSnapshotCommandId(void)
Definition snapmgr.c:744
void PopActiveSnapshot(void)
Definition snapmgr.c:775
Snapshot GetActiveSnapshot(void)
Definition snapmgr.c:800
#define InvalidSnapshot
Definition snapshot.h:119
static void SpinLockRelease(volatile slock_t *lock)
Definition spin.h:62
static void SpinLockAcquire(volatile slock_t *lock)
Definition spin.h:56
static void SpinLockInit(volatile slock_t *lock)
Definition spin.h:50
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:89
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
Oid GetRelationIdentityOrPK(Relation rel)
Definition relation.c:904
PGPROC * MyProc
Definition proc.c:71
void BecomeLockGroupLeader(void)
Definition proc.c:2106
uint16 StrategyNumber
Definition stratnum.h:22
#define InvalidStrategy
Definition stratnum.h:24
#define BTEqualStrategyNumber
Definition stratnum.h:31
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition stringinfo.c:281
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
int cc_file_seq
Definition repack.c:129
int cc_ident_key_nentries
Definition repack.c:123
Relation cc_rel
Definition repack.c:110
AttrNumber cc_last_key_attno
Definition repack.c:126
Relation cc_ident_index
Definition repack.c:121
ScanKey cc_ident_key
Definition repack.c:122
EState * cc_estate
Definition repack.c:114
ResultRelInfo * cc_rri
Definition repack.c:113
uint32 options
Definition repack.h:34
bool attisdropped
Definition tupdesc.h:78
ConditionVariable cv
char error_queue[FLEXIBLE_ARRAY_MEMBER]
dsm_segment * seg
Definition repack.c:141
BackgroundWorkerHandle * handle
Definition repack.c:138
shm_mq_handle * error_mqh
Definition repack.c:144
Definition pg_list.h:54
Oid indexOid
Definition repack.c:95
Oid tableOid
Definition repack.c:94
bool rd_ispkdeferrable
Definition rel.h:154
Oid rd_pkindex
Definition rel.h:153
Form_pg_class rd_rel
Definition rel.h:111
Datum sk_argument
Definition skey.h:72
FmgrInfo sk_func
Definition skey.h:71
Oid sk_collation
Definition skey.h:70
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
bool * tts_isnull
Definition tuptable.h:133
ItemPointerData tts_tid
Definition tuptable.h:142
Datum * tts_values
Definition tuptable.h:131
TransactionId FreezeLimit
Definition vacuum.h:288
TransactionId OldestXmin
Definition vacuum.h:278
TransactionId relfrozenxid
Definition vacuum.h:262
MultiXactId relminmxid
Definition vacuum.h:263
MultiXactId MultiXactCutoff
Definition vacuum.h:289
Definition type.h:97
Definition c.h:835
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:231
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596
#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, ScanKeyData *key)
Definition tableam.c:113
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition tableam.c:59
TU_UpdateIndexes
Definition tableam.h:133
@ TU_Summarizing
Definition tableam.h:141
@ TU_None
Definition tableam.h:135
static void table_endscan(TableScanDesc scan)
Definition tableam.h:1061
#define TABLE_INSERT_NO_LOGICAL
Definition tableam.h:286
TM_Result
Definition tableam.h:95
@ TM_Ok
Definition tableam.h:100
static void table_relation_copy_for_cluster(Relation OldTable, Relation NewTable, Relation OldIndex, bool use_sort, TransactionId OldestXmin, Snapshot snapshot, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead)
Definition tableam.h:1746
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, uint32 options, BulkInsertStateData *bistate)
Definition tableam.h:1458
#define TABLE_DELETE_NO_LOGICAL
Definition tableam.h:290
#define TABLE_UPDATE_NO_LOGICAL
Definition tableam.h:293
static TM_Result table_tuple_update(Relation rel, ItemPointer otid, TupleTableSlot *slot, CommandId cid, uint32 options, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, TU_UpdateIndexes *update_indexes)
Definition tableam.h:1598
static TM_Result table_tuple_delete(Relation rel, ItemPointer tid, CommandId cid, uint32 options, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd)
Definition tableam.h:1549
void ResetRelRewrite(Oid myrelid)
Definition tablecmds.c:4455
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition tablecmds.c:4508
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition tablecmds.c:4362
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, Oid OIDOldToast)
Definition toasting.c:65
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdIsValid(xid)
Definition transam.h:41
#define TransactionIdIsNormal(xid)
Definition transam.h:42
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition transam.h:263
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static void slot_getsomeattrs(TupleTableSlot *slot, int attnum)
Definition tuptable.h:376
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition tuptable.h:403
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
Definition vacuum.c:1106
#define VACOPT_VERBOSE
Definition vacuum.h:181
#define VACOPT_ANALYZE
Definition vacuum.h:180
static bool VARATT_IS_EXTERNAL_ONDISK(const void *PTR)
Definition varatt.h:361
static Size VARSIZE_ANY(const void *PTR)
Definition varatt.h:460
static bool VARATT_IS_EXTERNAL_INDIRECT(const void *PTR)
Definition varatt.h:368
static bool initialized
Definition win32ntdll.c:36
void CommandCounterIncrement(void)
Definition xact.c:1130
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition xact.c:3701
void StartTransactionCommand(void)
Definition xact.c:3112
void CommitTransactionCommand(void)
Definition xact.c:3210
CommandId GetCurrentCommandId(bool used)
Definition xact.c:831
int wal_level
Definition xlog.c:138
XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI)
Definition xlog.c:7000
XLogRecPtr GetXLogInsertEndRecPtr(void)
Definition xlog.c:10111
void XLogFlush(XLogRecPtr record)
Definition xlog.c:2800
@ WAL_LEVEL_REPLICA
Definition xlog.h:77
uint64 XLogRecPtr
Definition xlogdefs.h:21
#define InvalidXLogRecPtr
Definition xlogdefs.h:28