PostgreSQL Source Code git master
Loading...
Searching...
No Matches
repack.c File Reference
#include "postgres.h"
#include "access/amapi.h"
#include "access/heapam.h"
#include "access/multixact.h"
#include "access/relscan.h"
#include "access/tableam.h"
#include "access/toast_internals.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/toasting.h"
#include "commands/defrem.h"
#include "commands/progress.h"
#include "commands/repack.h"
#include "commands/repack_internal.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
#include "executor/executor.h"
#include "libpq/pqformat.h"
#include "libpq/pqmq.h"
#include "miscadmin.h"
#include "optimizer/optimizer.h"
#include "pgstat.h"
#include "replication/logicalrelation.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "storage/proc.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/injection_point.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/relmapper.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/wait_event_types.h"
Include dependency graph for repack.c:

Go to the source code of this file.

Data Structures

struct  RelToCluster
 
struct  ChangeContext
 
struct  DecodingWorker
 

Macros

#define WORKER_FILE_SNAPSHOT   0
 

Typedefs

typedef struct ChangeContext ChangeContext
 
typedef struct DecodingWorker DecodingWorker
 

Functions

static LOCKMODE RepackLockLevel (bool concurrent)
 
static bool cluster_rel_recheck (RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid userid, LOCKMODE lmode, int options)
 
static void check_concurrent_repack_requirements (Relation rel, Oid *ident_idx_p)
 
static void rebuild_relation (Relation OldHeap, Relation index, bool verbose, Oid ident_idx)
 
static void copy_table_data (Relation NewHeap, Relation OldHeap, Relation OldIndex, Snapshot snapshot, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
 
static Listget_tables_to_repack (RepackCommand cmd, bool usingindex, MemoryContext permcxt)
 
static Listget_tables_to_repack_partitioned (RepackCommand cmd, Oid relid, bool rel_is_index, MemoryContext permcxt)
 
static bool repack_is_permitted_for_relation (RepackCommand cmd, Oid relid, Oid userid)
 
static void apply_concurrent_changes (BufFile *file, ChangeContext *chgcxt)
 
static void apply_concurrent_insert (Relation rel, TupleTableSlot *slot, ChangeContext *chgcxt)
 
static void apply_concurrent_update (Relation rel, TupleTableSlot *spilled_tuple, TupleTableSlot *ondisk_tuple, ChangeContext *chgcxt)
 
static void apply_concurrent_delete (Relation rel, TupleTableSlot *slot)
 
static void restore_tuple (BufFile *file, Relation relation, TupleTableSlot *slot)
 
static void adjust_toast_pointers (Relation relation, TupleTableSlot *dest, TupleTableSlot *src)
 
static bool find_target_tuple (Relation rel, ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *retrieved)
 
static bool identity_key_equal (ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *candidate)
 
static void process_concurrent_changes (XLogRecPtr end_of_wal, ChangeContext *chgcxt, bool done)
 
static void initialize_change_context (ChangeContext *chgcxt, Relation relation, Oid ident_index_id)
 
static void release_change_context (ChangeContext *chgcxt)
 
static void rebuild_relation_finish_concurrent (Relation NewHeap, Relation OldHeap, Oid identIdx, TransactionId frozenXid, MultiXactId cutoffMulti)
 
static Listbuild_new_indexes (Relation NewHeap, Relation OldHeap, List *OldIndexes)
 
static void copy_index_constraints (Relation old_index, Oid new_index_id, Oid new_heap_id)
 
static Relation process_single_relation (RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, ClusterParams *params)
 
static Oid determine_clustered_index (Relation rel, bool usingindex, const char *indexname)
 
static void start_repack_decoding_worker (Oid relid)
 
static void stop_repack_decoding_worker (void)
 
static Snapshot get_initial_snapshot (DecodingWorker *worker)
 
static void ProcessRepackMessage (StringInfo msg)
 
static const charRepackCommandAsString (RepackCommand cmd)
 
void ExecRepack (ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
 
void cluster_rel (RepackCommand cmd, Relation OldHeap, Oid indexOid, ClusterParams *params, bool isTopLevel)
 
void check_index_is_clusterable (Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
 
void mark_index_clustered (Relation rel, Oid indexOid, bool is_internal)
 
Oid make_new_heap (Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
 
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)
 
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)
 
void DecodingWorkerFileName (char *fname, Oid relid, uint32 seq)
 
void HandleRepackMessageInterrupt (void)
 
void ProcessRepackMessages (void)
 

Variables

static DecodingWorkerdecoding_worker = NULL
 
volatile sig_atomic_t RepackMessagePending = false
 

Macro Definition Documentation

◆ WORKER_FILE_SNAPSHOT

#define WORKER_FILE_SNAPSHOT   0

Definition at line 98 of file repack.c.

Typedef Documentation

◆ ChangeContext

◆ DecodingWorker

Function Documentation

◆ adjust_toast_pointers()

static void adjust_toast_pointers ( Relation  relation,
TupleTableSlot dest,
TupleTableSlot src 
)
static

Definition at line 2788 of file repack.c.

2789{
2790 TupleDesc desc = dest->tts_tupleDescriptor;
2791
2792 for (int i = 0; i < desc->natts; i++)
2793 {
2796
2797 if (attr->attisdropped)
2798 continue;
2799 if (attr->attlen != -1)
2800 continue;
2801 if (slot_attisnull(dest, i + 1))
2802 continue;
2803
2804 slot_getsomeattrs(dest, i + 1);
2805
2806 varlena_dst = (varlena *) DatumGetPointer(dest->tts_values[i]);
2808 continue;
2809 slot_getsomeattrs(src, i + 1);
2810
2811 dest->tts_values[i] = src->tts_values[i];
2812 }
2813}
int i
Definition isn.c:77
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static int fb(int x)
bool attisdropped
Definition tupdesc.h:78
Datum * tts_values
Definition tuptable.h:131
Definition c.h:776
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static void slot_getsomeattrs(TupleTableSlot *slot, int attnum)
Definition tuptable.h:376
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition tuptable.h:403
static bool VARATT_IS_EXTERNAL_ONDISK(const void *PTR)
Definition varatt.h:361

References CompactAttribute::attisdropped, CompactAttribute::attlen, DatumGetPointer(), fb(), i, TupleDescData::natts, slot_attisnull(), slot_getsomeattrs(), TupleTableSlot::tts_values, TupleDescCompactAttr(), and VARATT_IS_EXTERNAL_ONDISK().

Referenced by apply_concurrent_changes().

◆ apply_concurrent_changes()

static void apply_concurrent_changes ( BufFile file,
ChangeContext chgcxt 
)
static

Definition at line 2499 of file repack.c.

2500{
2501 ConcurrentChangeKind kind = '\0';
2502 Relation rel = chgcxt->cc_rel;
2506 bool have_old_tuple = false;
2508
2510 &TTSOpsVirtual);
2514 &TTSOpsVirtual);
2515
2517
2518 while (true)
2519 {
2520 size_t nread;
2522
2524
2525 nread = BufFileReadMaybeEOF(file, &kind, 1, true);
2526 if (nread == 0) /* done with the file? */
2527 break;
2528
2529 /*
2530 * If this is the old tuple for an update, read it into the tuple slot
2531 * and go to the next one. The update itself will be executed on the
2532 * next iteration, when we receive the NEW tuple.
2533 */
2534 if (kind == CHANGE_UPDATE_OLD)
2535 {
2536 restore_tuple(file, rel, old_update_tuple);
2537 have_old_tuple = true;
2538 continue;
2539 }
2540
2541 /*
2542 * Just before an UPDATE or DELETE, we must update the command
2543 * counter, because the change could refer to a tuple that we have
2544 * just inserted; and before an INSERT, we have to do this also if the
2545 * previous command was either update or delete.
2546 *
2547 * With this approach we don't spend so many CCIs for long strings of
2548 * only INSERTs, which can't affect one another.
2549 */
2550 if (kind == CHANGE_UPDATE_NEW || kind == CHANGE_DELETE ||
2551 (kind == CHANGE_INSERT && (prevkind == CHANGE_UPDATE_NEW ||
2553 {
2556 }
2557
2558 /*
2559 * Now restore the tuple into the slot and execute the change.
2560 */
2561 restore_tuple(file, rel, spilled_tuple);
2562
2563 if (kind == CHANGE_INSERT)
2564 {
2566 }
2567 else if (kind == CHANGE_DELETE)
2568 {
2569 bool found;
2570
2571 /* Find the tuple to be deleted */
2573 if (!found)
2574 elog(ERROR, "failed to find target tuple");
2576 }
2577 else if (kind == CHANGE_UPDATE_NEW)
2578 {
2580 bool found;
2581
2582 if (have_old_tuple)
2584 else
2586
2587 /* Find the tuple to be updated or deleted. */
2588 found = find_target_tuple(rel, chgcxt, key, ondisk_tuple);
2589 if (!found)
2590 elog(ERROR, "failed to find target tuple");
2591
2592 /*
2593 * If 'tup' contains TOAST pointers, they point to the old
2594 * relation's toast. Copy the corresponding TOAST pointers for the
2595 * new relation from the existing tuple. (The fact that we
2596 * received a TOAST pointer here implies that the attribute hasn't
2597 * changed.)
2598 */
2600
2602
2604 have_old_tuple = false;
2605 }
2606 else
2607 elog(ERROR, "unrecognized kind of change: %d", kind);
2608
2609 ResetPerTupleExprContext(chgcxt->cc_estate);
2610 }
2611
2612 /* Cleanup. */
2616
2618}
size_t BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
Definition buffile.c:665
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
#define ResetPerTupleExprContext(estate)
Definition executor.h:676
#define GetPerTupleMemoryContext(estate)
Definition executor.h:672
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define RelationGetDescr(relation)
Definition rel.h:542
static void restore_tuple(BufFile *file, Relation relation, TupleTableSlot *slot)
Definition repack.c:2718
static bool find_target_tuple(Relation rel, ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *retrieved)
Definition repack.c:2823
static void apply_concurrent_delete(Relation rel, TupleTableSlot *slot)
Definition repack.c:2686
static void apply_concurrent_insert(Relation rel, TupleTableSlot *slot, ChangeContext *chgcxt)
Definition repack.c:2625
static void adjust_toast_pointers(Relation relation, TupleTableSlot *dest, TupleTableSlot *src)
Definition repack.c:2788
static void apply_concurrent_update(Relation rel, TupleTableSlot *spilled_tuple, TupleTableSlot *ondisk_tuple, ChangeContext *chgcxt)
Definition repack.c:2646
#define CHANGE_UPDATE_OLD
#define CHANGE_DELETE
#define CHANGE_UPDATE_NEW
char ConcurrentChangeKind
#define CHANGE_INSERT
void UpdateActiveSnapshotCommandId(void)
Definition snapmgr.c:744
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition tableam.c:59
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
void CommandCounterIncrement(void)
Definition xact.c:1130

References adjust_toast_pointers(), apply_concurrent_delete(), apply_concurrent_insert(), apply_concurrent_update(), BufFileReadMaybeEOF(), CHANGE_DELETE, CHANGE_INSERT, CHANGE_UPDATE_NEW, CHANGE_UPDATE_OLD, CHECK_FOR_INTERRUPTS, CommandCounterIncrement(), elog, ERROR, ExecClearTuple(), ExecDropSingleTupleTableSlot(), fb(), find_target_tuple(), GetPerTupleMemoryContext, MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), RelationGetDescr, ResetPerTupleExprContext, restore_tuple(), table_slot_callbacks(), TTSOpsVirtual, and UpdateActiveSnapshotCommandId().

Referenced by process_concurrent_changes().

◆ apply_concurrent_delete()

static void apply_concurrent_delete ( Relation  rel,
TupleTableSlot slot 
)
static

Definition at line 2686 of file repack.c.

2687{
2688 TM_Result res;
2689 TM_FailureData tmfd;
2690
2691 /*
2692 * Delete tuple from the new heap, skipping logical decoding for it.
2693 */
2694 res = table_tuple_delete(rel, &(slot->tts_tid),
2695 GetCurrentCommandId(true),
2698 false,
2699 &tmfd);
2700
2701 if (res != TM_Ok)
2702 ereport(ERROR,
2703 errmsg("failed to apply concurrent DELETE"));
2704
2706}
void pgstat_progress_incr_param(int index, int64 incr)
#define ereport(elevel,...)
Definition elog.h:152
static char * errmsg
#define PROGRESS_REPACK_HEAP_TUPLES_DELETED
Definition progress.h:91
#define InvalidSnapshot
Definition snapshot.h:119
ItemPointerData tts_tid
Definition tuptable.h:142
TM_Result
Definition tableam.h:95
@ TM_Ok
Definition tableam.h:100
#define TABLE_DELETE_NO_LOGICAL
Definition tableam.h:290
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
CommandId GetCurrentCommandId(bool used)
Definition xact.c:831

References ereport, errmsg, ERROR, GetCurrentCommandId(), InvalidSnapshot, pgstat_progress_incr_param(), PROGRESS_REPACK_HEAP_TUPLES_DELETED, TABLE_DELETE_NO_LOGICAL, table_tuple_delete(), TM_Ok, and TupleTableSlot::tts_tid.

Referenced by apply_concurrent_changes().

◆ apply_concurrent_insert()

static void apply_concurrent_insert ( Relation  rel,
TupleTableSlot slot,
ChangeContext chgcxt 
)
static

Definition at line 2625 of file repack.c.

2627{
2628 /* Put the tuple in the table, but make sure it won't be decoded */
2629 table_tuple_insert(rel, slot, GetCurrentCommandId(true),
2631
2632 /* Update indexes with this new tuple. */
2634 chgcxt->cc_estate,
2635 0,
2636 slot,
2637 NIL, NULL);
2639}
List * ExecInsertIndexTuples(ResultRelInfo *resultRelInfo, EState *estate, uint32 flags, TupleTableSlot *slot, List *arbiterIndexes, bool *specConflict)
#define NIL
Definition pg_list.h:68
#define PROGRESS_REPACK_HEAP_TUPLES_INSERTED
Definition progress.h:89
#define TABLE_INSERT_NO_LOGICAL
Definition tableam.h:286
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, uint32 options, BulkInsertStateData *bistate)
Definition tableam.h:1458

References ExecInsertIndexTuples(), fb(), GetCurrentCommandId(), NIL, pgstat_progress_incr_param(), PROGRESS_REPACK_HEAP_TUPLES_INSERTED, TABLE_INSERT_NO_LOGICAL, and table_tuple_insert().

Referenced by apply_concurrent_changes().

◆ apply_concurrent_update()

static void apply_concurrent_update ( Relation  rel,
TupleTableSlot spilled_tuple,
TupleTableSlot ondisk_tuple,
ChangeContext chgcxt 
)
static

Definition at line 2646 of file repack.c.

2649{
2650 LockTupleMode lockmode;
2651 TM_FailureData tmfd;
2653 TM_Result res;
2654
2655 /*
2656 * Carry out the update, skipping logical decoding for it.
2657 */
2658 res = table_tuple_update(rel, &(ondisk_tuple->tts_tid), spilled_tuple,
2659 GetCurrentCommandId(true),
2663 false,
2664 &tmfd, &lockmode, &update_indexes);
2665 if (res != TM_Ok)
2666 ereport(ERROR,
2667 errmsg("failed to apply concurrent UPDATE"));
2668
2669 if (update_indexes != TU_None)
2670 {
2671 uint32 flags = EIIT_IS_UPDATE;
2672
2674 flags |= EIIT_ONLY_SUMMARIZING;
2676 chgcxt->cc_estate,
2677 flags,
2679 NIL, NULL);
2680 }
2681
2683}
uint32_t uint32
Definition c.h:624
#define EIIT_IS_UPDATE
Definition executor.h:757
#define EIIT_ONLY_SUMMARIZING
Definition executor.h:759
LockTupleMode
Definition lockoptions.h:51
#define PROGRESS_REPACK_HEAP_TUPLES_UPDATED
Definition progress.h:90
TU_UpdateIndexes
Definition tableam.h:133
@ TU_Summarizing
Definition tableam.h:141
@ TU_None
Definition tableam.h:135
#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

References EIIT_IS_UPDATE, EIIT_ONLY_SUMMARIZING, ereport, errmsg, ERROR, ExecInsertIndexTuples(), fb(), GetCurrentCommandId(), InvalidSnapshot, NIL, pgstat_progress_incr_param(), PROGRESS_REPACK_HEAP_TUPLES_UPDATED, table_tuple_update(), TABLE_UPDATE_NO_LOGICAL, TM_Ok, TU_None, and TU_Summarizing.

Referenced by apply_concurrent_changes().

◆ build_new_indexes()

static List * build_new_indexes ( Relation  NewHeap,
Relation  OldHeap,
List OldIndexes 
)
static

Definition at line 3295 of file repack.c.

3296{
3297 List *result = NIL;
3298
3301
3303 {
3304 Oid newindex;
3305 char *newName;
3306 Relation ind;
3307
3309
3311 NULL,
3312 "repacknew",
3313 get_rel_namespace(ind->rd_index->indrelid),
3314 false);
3316 oldindex, ind->rd_rel->reltablespace,
3317 newName);
3320
3322 }
3323
3324 return result;
3325}
void pgstat_progress_update_param(int index, int64 val)
uint32 result
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
void index_close(Relation relation, LOCKMODE lockmode)
Definition indexam.c:178
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition indexam.c:134
char * ChooseRelationName(const char *name1, const char *name2, const char *label, Oid namespaceid, bool isconstraint)
Definition indexcmds.c:2634
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
#define NoLock
Definition lockdefs.h:34
#define ShareUpdateExclusiveLock
Definition lockdefs.h:39
char * get_rel_name(Oid relid)
Definition lsyscache.c:2159
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2183
#define foreach_oid(var, lst)
Definition pg_list.h:503
unsigned int Oid
#define PROGRESS_REPACK_PHASE
Definition progress.h:86
#define PROGRESS_REPACK_PHASE_REBUILD_INDEX
Definition progress.h:105
#define RelationGetRelid(relation)
Definition rel.h:516
static void copy_index_constraints(Relation old_index, Oid new_index_id, Oid new_heap_id)
Definition repack.c:3340
Definition pg_list.h:54

References ChooseRelationName(), copy_index_constraints(), fb(), foreach_oid, get_rel_name(), get_rel_namespace(), index_close(), index_create_copy(), INDEX_CREATE_SUPPRESS_PROGRESS, index_open(), lappend_oid(), NIL, NoLock, pgstat_progress_update_param(), PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PHASE_REBUILD_INDEX, RelationGetRelid, result, and ShareUpdateExclusiveLock.

Referenced by rebuild_relation_finish_concurrent().

◆ check_concurrent_repack_requirements()

static void check_concurrent_repack_requirements ( Relation  rel,
Oid ident_idx_p 
)
static

Definition at line 893 of file repack.c.

894{
895 char relpersistence,
896 replident;
898
899 /* Data changes in system relations are not logically decoded. */
900 if (IsCatalogRelation(rel))
903 errmsg("cannot repack relation \"%s\"",
905 errhint("%s is not supported for catalog relations.",
906 "REPACK (CONCURRENTLY)"));
907
908 /*
909 * reorderbuffer.c does not seem to handle processing of TOAST relation
910 * alone.
911 */
912 if (IsToastRelation(rel))
915 errmsg("cannot repack relation \"%s\"",
917 errhint("%s is not supported for TOAST relations.",
918 "REPACK (CONCURRENTLY)"));
919
920 relpersistence = rel->rd_rel->relpersistence;
921 if (relpersistence != RELPERSISTENCE_PERMANENT)
924 errmsg("cannot repack relation \"%s\"",
926 errhint("%s is only allowed for permanent relations.",
927 "REPACK (CONCURRENTLY)"));
928
929 /* With NOTHING, WAL does not contain the old tuple. */
930 replident = rel->rd_rel->relreplident;
931 if (replident == REPLICA_IDENTITY_NOTHING)
934 errmsg("cannot repack relation \"%s\"",
936 errhint("Relation \"%s\" has insufficient replication identity.",
938
939 /*
940 * Obtain the replica identity index -- either one that has been set
941 * explicitly, or a non-deferrable primary key. If none of these cases
942 * apply, the table cannot be repacked concurrently. It might be possible
943 * to have repack work with a FULL replica identity; however that requires
944 * more work and is not implemented yet.
945 */
947 if (!OidIsValid(ident_idx))
950 errmsg("cannot process relation \"%s\"",
952 errhint("Relation \"%s\" has no identity index.",
954
956}
#define OidIsValid(objectId)
Definition c.h:858
bool IsToastRelation(Relation relation)
Definition catalog.c:206
bool IsCatalogRelation(Relation relation)
Definition catalog.c:104
int errcode(int sqlerrcode)
Definition elog.c:875
int errhint(const char *fmt,...) pg_attribute_printf(1
#define RelationGetRelationName(relation)
Definition rel.h:550
Oid GetRelationIdentityOrPK(Relation rel)
Definition relation.c:905
Form_pg_class rd_rel
Definition rel.h:111

References ereport, errcode(), errhint(), errmsg, ERROR, fb(), GetRelationIdentityOrPK(), IsCatalogRelation(), IsToastRelation(), OidIsValid, RelationData::rd_rel, and RelationGetRelationName.

Referenced by cluster_rel().

◆ check_index_is_clusterable()

void check_index_is_clusterable ( Relation  OldHeap,
Oid  indexOid,
LOCKMODE  lockmode 
)

Definition at line 767 of file repack.c.

768{
770
771 OldIndex = index_open(indexOid, lockmode);
772
773 /*
774 * Check that index is in fact an index on the given relation
775 */
776 if (OldIndex->rd_index == NULL ||
777 OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
780 errmsg("\"%s\" is not an index for table \"%s\"",
783
784 /* Index AM must allow clustering */
785 if (!OldIndex->rd_indam->amclusterable)
788 errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
790
791 /*
792 * Disallow clustering on incomplete indexes (those that might not index
793 * every row of the relation). We could relax this by making a separate
794 * seqscan pass over the table to copy the missing rows, but that seems
795 * expensive and tedious.
796 */
797 if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
800 errmsg("cannot cluster on partial index \"%s\"",
802
803 /*
804 * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
805 * it might well not contain entries for every heap row, or might not even
806 * be internally consistent. (But note that we don't check indcheckxmin;
807 * the worst consequence of following broken HOT chains would be that we
808 * might put recently-dead tuples out-of-order in the new table, and there
809 * is little harm in that.)
810 */
811 if (!OldIndex->rd_index->indisvalid)
814 errmsg("cannot cluster on invalid index \"%s\"",
816
817 /* Drop relcache refcnt on OldIndex, but keep lock */
819}
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456

References ereport, errcode(), errmsg, ERROR, fb(), heap_attisnull(), index_close(), index_open(), NoLock, RelationGetRelationName, and RelationGetRelid.

Referenced by ATExecClusterOn(), cluster_rel(), ExecRepack(), and process_single_relation().

◆ cluster_rel()

void cluster_rel ( RepackCommand  cmd,
Relation  OldHeap,
Oid  indexOid,
ClusterParams params,
bool  isTopLevel 
)

Definition at line 518 of file repack.c.

520{
521 Oid tableOid = RelationGetRelid(OldHeap);
524 Oid save_userid;
525 int save_sec_context;
526 int save_nestlevel;
527 bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
528 bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
529 bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0);
531
532 /* Determine the lock mode to use. */
533 lmode = RepackLockLevel(concurrent);
534
535 /*
536 * Check some preconditions in the concurrent case. This also obtains the
537 * replica index OID.
538 */
539 if (concurrent)
541
542 /* Check for user-requested abort. */
544
547
548 /*
549 * Switch to the table owner's userid, so that any index functions are run
550 * as that user. Also lock down security-restricted operations and
551 * arrange to make GUC variable changes local to this command.
552 */
553 GetUserIdAndSecContext(&save_userid, &save_sec_context);
554 SetUserIdAndSecContext(OldHeap->rd_rel->relowner,
555 save_sec_context | SECURITY_RESTRICTED_OPERATION);
556 save_nestlevel = NewGUCNestLevel();
558
559 /*
560 * Recheck that the relation is still what it was when we started.
561 *
562 * Note that it's critical to skip this in single-relation CLUSTER;
563 * otherwise, we would reject an attempt to cluster using a
564 * not-previously-clustered index.
565 */
566 if (recheck &&
567 !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid,
568 lmode, params->options))
569 goto out;
570
571 /*
572 * We allow repacking shared catalogs only when not using an index. It
573 * would work to use an index in most respects, but the index would only
574 * get marked as indisclustered in the current database, leading to
575 * unexpected behavior if CLUSTER were later invoked in another database.
576 */
577 if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
580 /*- translator: first %s is name of a SQL command, eg. REPACK */
581 errmsg("cannot execute %s on a shared catalog",
583
584 /*
585 * The CONCURRENTLY case should have been rejected earlier because it does
586 * not support system catalogs.
587 */
588 Assert(!(OldHeap->rd_rel->relisshared && concurrent));
589
590 /*
591 * Don't process temp tables of other backends ... their local buffer
592 * manager is not going to cope.
593 */
597 /*- translator: first %s is name of a SQL command, eg. REPACK */
598 errmsg("cannot execute %s on temporary tables of other sessions",
600
601 /*
602 * Also check for active uses of the relation in the current transaction,
603 * including open scans and pending AFTER trigger events.
604 */
606
607 /* Check heap and index are valid to cluster on */
608 if (OidIsValid(indexOid))
609 {
610 /* verify the index is good and lock it */
612 /* also open it */
613 index = index_open(indexOid, NoLock);
614 }
615 else
616 index = NULL;
617
618 /*
619 * When allow_system_table_mods is turned off, we disallow repacking a
620 * catalog on a particular index unless that's already the clustered index
621 * for that catalog.
622 *
623 * XXX We don't check for this in CLUSTER, because it's historically been
624 * allowed.
625 */
626 if (cmd != REPACK_COMMAND_CLUSTER &&
627 !allowSystemTableMods && OidIsValid(indexOid) &&
628 IsCatalogRelation(OldHeap) && !index->rd_index->indisclustered)
631 errmsg("permission denied: \"%s\" is a system catalog",
633 errdetail("System catalogs can only be clustered by the index they're already clustered on, if any, unless \"%s\" is enabled.",
634 "allow_system_table_mods"));
635
636 /*
637 * Quietly ignore the request if this is a materialized view which has not
638 * been populated from its query. No harm is done because there is no data
639 * to deal with, and we don't want to throw an error if this is part of a
640 * multi-relation request -- for example, CLUSTER was run on the entire
641 * database.
642 */
643 if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
645 {
646 if (index)
649 goto out;
650 }
651
652 Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
653 OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
654 OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
655
656 /*
657 * All predicate locks on the tuples or pages are about to be made
658 * invalid, because we move tuples around. Promote them to relation
659 * locks. Predicate locks on indexes will be promoted when they are
660 * reindexed.
661 *
662 * During concurrent processing, the heap as well as its indexes stay in
663 * operation, so we postpone this step until they are locked using
664 * AccessExclusiveLock near the end of the processing.
665 */
666 if (!concurrent)
668
669 /* rebuild_relation does all the dirty work */
670 PG_TRY();
671 {
673 }
674 PG_FINALLY();
675 {
676 if (concurrent)
677 {
678 /*
679 * Since during normal operation the worker was already asked to
680 * exit, stopping it explicitly is especially important on ERROR.
681 * However it still seems a good practice to make sure that the
682 * worker never survives the REPACK command.
683 */
685 }
686 }
687 PG_END_TRY();
688
689 /* rebuild_relation closes OldHeap, and index if valid */
690
691out:
692 /* Roll back any GUC changes executed by index functions */
693 AtEOXact_GUC(false, save_nestlevel);
694
695 /* Restore userid and security context */
696 SetUserIdAndSecContext(save_userid, save_sec_context);
697
699}
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_REPACK
#define Assert(condition)
Definition c.h:943
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define PG_FINALLY(...)
Definition elog.h:391
bool allowSystemTableMods
Definition globals.c:132
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
int LOCKMODE
Definition lockdefs.h:26
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:331
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
@ REPACK_COMMAND_CLUSTER
static int verbose
#define InvalidOid
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition predicate.c:3052
#define PROGRESS_REPACK_COMMAND
Definition progress.h:85
#define RelationIsPopulated(relation)
Definition rel.h:697
#define RELATION_IS_OTHER_TEMP(relation)
Definition rel.h:678
static bool cluster_rel_recheck(RepackCommand cmd, Relation OldHeap, Oid indexOid, Oid userid, LOCKMODE lmode, int options)
Definition repack.c:706
static void check_concurrent_repack_requirements(Relation rel, Oid *ident_idx_p)
Definition repack.c:893
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:767
static void stop_repack_decoding_worker(void)
Definition repack.c:3499
static LOCKMODE RepackLockLevel(bool concurrent)
Definition repack.c:485
static const char * RepackCommandAsString(RepackCommand cmd)
Definition repack.c:2481
static void rebuild_relation(Relation OldHeap, Relation index, bool verbose, Oid ident_idx)
Definition repack.c:978
#define CLUOPT_VERBOSE
Definition repack.h:25
#define CLUOPT_CONCURRENT
Definition repack.h:29
#define CLUOPT_RECHECK
Definition repack.h:26
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
uint32 options
Definition repack.h:34
Definition type.h:97
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition tablecmds.c:4473

References allowSystemTableMods, Assert, AtEOXact_GUC(), check_concurrent_repack_requirements(), CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), CLUOPT_CONCURRENT, CLUOPT_RECHECK, CLUOPT_VERBOSE, cluster_rel_recheck(), ereport, errcode(), errdetail(), errmsg, ERROR, fb(), GetUserIdAndSecContext(), index_close(), index_open(), InvalidOid, IsCatalogRelation(), NewGUCNestLevel(), NoLock, OidIsValid, ClusterParams::options, PG_END_TRY, PG_FINALLY, PG_TRY, pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), PROGRESS_COMMAND_REPACK, PROGRESS_REPACK_COMMAND, rebuild_relation(), relation_close(), RELATION_IS_OTHER_TEMP, RelationGetRelationName, RelationGetRelid, RelationIsPopulated, REPACK_COMMAND_CLUSTER, RepackCommandAsString(), RepackLockLevel(), RestrictSearchPath(), SECURITY_RESTRICTED_OPERATION, SetUserIdAndSecContext(), stop_repack_decoding_worker(), TransferPredicateLocksToHeapRelation(), and verbose.

Referenced by ExecRepack(), process_single_relation(), and vacuum_rel().

◆ cluster_rel_recheck()

static bool cluster_rel_recheck ( RepackCommand  cmd,
Relation  OldHeap,
Oid  indexOid,
Oid  userid,
LOCKMODE  lmode,
int  options 
)
static

Definition at line 706 of file repack.c.

708{
709 Oid tableOid = RelationGetRelid(OldHeap);
710
711 /* Check that the user still has privileges for the relation */
712 if (!repack_is_permitted_for_relation(cmd, tableOid, userid))
713 {
715 return false;
716 }
717
718 /*
719 * Silently skip a temp table for a remote session. Only doing this check
720 * in the "recheck" case is appropriate (which currently means somebody is
721 * executing a database-wide CLUSTER or on a partitioned table), because
722 * there is another check in cluster() which will stop any attempt to
723 * cluster remote temp tables by name. There is another check in
724 * cluster_rel which is redundant, but we leave it for extra safety.
725 */
727 {
729 return false;
730 }
731
732 if (OidIsValid(indexOid))
733 {
734 /*
735 * Check that the index still exists
736 */
738 {
740 return false;
741 }
742
743 /*
744 * Check that the index is still the one with indisclustered set, if
745 * needed.
746 */
747 if ((options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
748 !get_index_isclustered(indexOid))
749 {
751 return false;
752 }
753 }
754
755 return true;
756}
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3879
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static bool repack_is_permitted_for_relation(RepackCommand cmd, Oid relid, Oid userid)
Definition repack.c:2319
#define CLUOPT_RECHECK_ISCLUSTERED
Definition repack.h:27
#define SearchSysCacheExists1(cacheId, key1)
Definition syscache.h:100

References CLUOPT_RECHECK_ISCLUSTERED, fb(), get_index_isclustered(), ObjectIdGetDatum(), OidIsValid, relation_close(), RELATION_IS_OTHER_TEMP, RelationGetRelid, repack_is_permitted_for_relation(), and SearchSysCacheExists1.

Referenced by cluster_rel().

◆ copy_index_constraints()

static void copy_index_constraints ( Relation  old_index,
Oid  new_index_id,
Oid  new_heap_id 
)
static

Definition at line 3340 of file repack.c.

3341{
3343 Relation rel;
3344 TupleDesc desc;
3345 SysScanDesc scan;
3346 HeapTuple tup;
3348
3351
3352 /*
3353 * Retrieve the constraints supported by the old index and create an
3354 * identical one that points to the new index.
3355 */
3359 ObjectIdGetDatum(old_index->rd_index->indrelid));
3361 NULL, 1, &skey);
3362 desc = RelationGetDescr(rel);
3363 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3364 {
3366 Oid oid;
3368 bool nulls[Natts_pg_constraint] = {0};
3369 bool replaces[Natts_pg_constraint] = {0};
3372
3373 if (conform->conindid != RelationGetRelid(old_index))
3374 continue;
3375
3379 replaces[Anum_pg_constraint_oid - 1] = true;
3384
3385 new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
3386
3387 /* Insert it into the catalog. */
3389
3390 /* Create a dependency so it's removed when we drop the new heap. */
3393 }
3394 systable_endscan(scan);
3395
3397
3399}
static Datum values[MAXATTR]
Definition bootstrap.c:190
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:448
@ DEPENDENCY_AUTO
Definition dependency.h:34
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:612
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:523
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1118
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition indexing.c:233
#define RowExclusiveLock
Definition lockdefs.h:38
#define ObjectAddressSet(addr, class_id, object_id)
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:47
uint64_t Datum
Definition postgres.h:70
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define BTEqualStrategyNumber
Definition stratnum.h:31
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40

References BTEqualStrategyNumber, CatalogTupleInsert(), CommandCounterIncrement(), DEPENDENCY_AUTO, fb(), Form_pg_constraint, GetNewOidWithIndex(), GETSTRUCT(), heap_modify_tuple(), HeapTupleIsValid, ObjectAddressSet, ObjectIdGetDatum(), recordDependencyOn(), RelationGetDescr, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and values.

Referenced by build_new_indexes().

◆ copy_table_data()

static void copy_table_data ( Relation  NewHeap,
Relation  OldHeap,
Relation  OldIndex,
Snapshot  snapshot,
bool  verbose,
bool pSwapToastByContent,
TransactionId pFreezeXid,
MultiXactId pCutoffMulti 
)
static

Definition at line 1254 of file repack.c.

1257{
1263 VacuumParams params;
1264 struct VacuumCutoffs cutoffs;
1265 bool use_sort;
1266 double num_tuples = 0,
1267 tups_vacuumed = 0,
1269 BlockNumber num_pages;
1270 int elevel = verbose ? INFO : DEBUG2;
1271 PGRUsage ru0;
1272 char *nspname;
1273 bool concurrent = snapshot != NULL;
1275
1276 lmode = RepackLockLevel(concurrent);
1277
1279
1280 /* Store a copy of the namespace name for logging purposes */
1282
1283 /*
1284 * Their tuple descriptors should be exactly alike, but here we only need
1285 * assume that they have the same number of columns.
1286 */
1289 Assert(newTupDesc->natts == oldTupDesc->natts);
1290
1291 /*
1292 * If the OldHeap has a toast table, get lock on the toast table to keep
1293 * it from being vacuumed. This is needed because autovacuum processes
1294 * toast tables independently of their main tables, with no lock on the
1295 * latter. If an autovacuum were to start on the toast table after we
1296 * compute our OldestXmin below, it would use a later OldestXmin, and then
1297 * possibly remove as DEAD toast tuples belonging to main tuples we think
1298 * are only RECENTLY_DEAD. Then we'd fail while trying to copy those
1299 * tuples.
1300 *
1301 * We don't need to open the toast relation here, just lock it. The lock
1302 * will be held till end of transaction.
1303 */
1304 if (OldHeap->rd_rel->reltoastrelid)
1305 LockRelationOid(OldHeap->rd_rel->reltoastrelid, lmode);
1306
1307 /*
1308 * If both tables have TOAST tables, perform toast swap by content. It is
1309 * possible that the old table has a toast table but the new one doesn't,
1310 * if toastable columns have been dropped. In that case we have to do
1311 * swap by links. This is okay because swap by content is only essential
1312 * for system catalogs, and we don't support schema changes for them.
1313 */
1314 if (OldHeap->rd_rel->reltoastrelid && NewHeap->rd_rel->reltoastrelid &&
1315 !concurrent)
1316 {
1317 *pSwapToastByContent = true;
1318
1319 /*
1320 * When doing swap by content, any toast pointers written into NewHeap
1321 * must use the old toast table's OID, because that's where the toast
1322 * data will eventually be found. Set this up by setting rd_toastoid.
1323 * This also tells toast_save_datum() to preserve the toast value
1324 * OIDs, which we want so as not to invalidate toast pointers in
1325 * system catalog caches, and to avoid making multiple copies of a
1326 * single toast value.
1327 *
1328 * Note that we must hold NewHeap open until we are done writing data,
1329 * since the relcache will not guarantee to remember this setting once
1330 * the relation is closed. Also, this technique depends on the fact
1331 * that no one will try to read from the NewHeap until after we've
1332 * finished writing it and swapping the rels --- otherwise they could
1333 * follow the toast pointers to the wrong place. (It would actually
1334 * work for values copied over from the old toast table, but not for
1335 * any values that we toast which were previously not toasted.)
1336 *
1337 * This would not work with CONCURRENTLY because we may need to delete
1338 * TOASTed tuples from the new heap. With this hack, we'd delete them
1339 * from the old heap.
1340 */
1341 NewHeap->rd_toastoid = OldHeap->rd_rel->reltoastrelid;
1342 }
1343 else
1344 *pSwapToastByContent = false;
1345
1346 /*
1347 * Compute xids used to freeze and weed out dead tuples and multixacts.
1348 * Since we're going to rewrite the whole table anyway, there's no reason
1349 * not to be aggressive about this.
1350 */
1351 memset(&params, 0, sizeof(VacuumParams));
1352 vacuum_get_cutoffs(OldHeap, &params, &cutoffs);
1353
1354 /*
1355 * FreezeXid will become the table's new relfrozenxid, and that mustn't go
1356 * backwards, so take the max.
1357 */
1358 {
1359 TransactionId relfrozenxid = OldHeap->rd_rel->relfrozenxid;
1360
1362 TransactionIdPrecedes(cutoffs.FreezeLimit, relfrozenxid))
1363 cutoffs.FreezeLimit = relfrozenxid;
1364 }
1365
1366 /*
1367 * MultiXactCutoff, similarly, shouldn't go backwards either.
1368 */
1369 {
1370 MultiXactId relminmxid = OldHeap->rd_rel->relminmxid;
1371
1373 MultiXactIdPrecedes(cutoffs.MultiXactCutoff, relminmxid))
1374 cutoffs.MultiXactCutoff = relminmxid;
1375 }
1376
1377 /*
1378 * Decide whether to use an indexscan or seqscan-and-optional-sort to scan
1379 * the OldHeap. We know how to use a sort to duplicate the ordering of a
1380 * btree index, and will use seqscan-and-sort for that case if the planner
1381 * tells us it's cheaper. Otherwise, always indexscan if an index is
1382 * provided, else plain seqscan.
1383 */
1384 if (OldIndex != NULL && OldIndex->rd_rel->relam == BTREE_AM_OID)
1387 else
1388 use_sort = false;
1389
1390 /* Log what we're doing */
1391 if (OldIndex != NULL && !use_sort)
1392 ereport(elevel,
1393 errmsg("repacking \"%s.%s\" using index scan on \"%s\"",
1394 nspname,
1397 else if (use_sort)
1398 ereport(elevel,
1399 errmsg("repacking \"%s.%s\" using sequential scan and sort",
1400 nspname,
1402 else
1403 ereport(elevel,
1404 errmsg("repacking \"%s.%s\" in physical order",
1405 nspname,
1407
1408 /*
1409 * Hand off the actual copying to AM specific function, the generic code
1410 * cannot know how to deal with visibility across AMs. Note that this
1411 * routine is allowed to set FreezeXid / MultiXactCutoff to different
1412 * values (e.g. because the AM doesn't use freezing).
1413 */
1415 cutoffs.OldestXmin, snapshot,
1416 &cutoffs.FreezeLimit,
1417 &cutoffs.MultiXactCutoff,
1418 &num_tuples, &tups_vacuumed,
1420
1421 /* return selected values to caller, get set as relfrozenxid/minmxid */
1422 *pFreezeXid = cutoffs.FreezeLimit;
1423 *pCutoffMulti = cutoffs.MultiXactCutoff;
1424
1425 /*
1426 * Reset rd_toastoid just to be tidy --- it shouldn't be looked at again.
1427 * In the CONCURRENTLY case, we need to set it again before applying the
1428 * concurrent changes.
1429 */
1430 NewHeap->rd_toastoid = InvalidOid;
1431
1433
1434 /* Log what we did */
1435 ereport(elevel,
1436 (errmsg("\"%s.%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
1437 nspname,
1439 tups_vacuumed, num_tuples,
1441 errdetail("%.0f dead row versions cannot be removed yet.\n"
1442 "%s.",
1444 pg_rusage_show(&ru0))));
1445
1446 /* Update pg_class to reflect the correct values of pages and tuples. */
1448
1452 elog(ERROR, "cache lookup failed for relation %u",
1455
1456 relform->relpages = num_pages;
1457 relform->reltuples = num_tuples;
1458
1459 /* Don't update the stats for pg_class. See swap_relation_files. */
1462 else
1464
1465 /* Clean up. */
1468
1469 /* Make the update visible */
1471}
uint32 BlockNumber
Definition block.h:31
#define RelationGetNumberOfBlocks(reln)
Definition bufmgr.h:309
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
TransactionId MultiXactId
Definition c.h:746
uint32 TransactionId
Definition c.h:736
#define DEBUG2
Definition elog.h:30
#define INFO
Definition elog.h:35
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3599
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
Definition multixact.c:2865
#define MultiXactIdIsValid(multi)
Definition multixact.h:29
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
const char * pg_rusage_show(const PGRUsage *ru0)
Definition pg_rusage.c:40
void pg_rusage_init(PGRUsage *ru0)
Definition pg_rusage.c:27
bool plan_cluster_use_sort(Oid tableOid, Oid indexOid)
Definition planner.c:7161
#define RelationGetNamespace(relation)
Definition rel.h:557
TransactionId relfrozenxid
Definition vacuum.h:262
MultiXactId relminmxid
Definition vacuum.h:263
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
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
#define TransactionIdIsValid(xid)
Definition transam.h:41
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition transam.h:263
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
Definition vacuum.c:1106

References Assert, CacheInvalidateRelcacheByTuple(), CatalogTupleUpdate(), CommandCounterIncrement(), DEBUG2, elog, ereport, errdetail(), errmsg, ERROR, fb(), VacuumCutoffs::FreezeLimit, get_namespace_name(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, INFO, InvalidOid, LockRelationOid(), VacuumCutoffs::MultiXactCutoff, MultiXactIdIsValid, MultiXactIdPrecedes(), ObjectIdGetDatum(), VacuumCutoffs::OldestXmin, pg_rusage_init(), pg_rusage_show(), PG_USED_FOR_ASSERTS_ONLY, plan_cluster_use_sort(), RelationGetDescr, RelationGetNamespace, RelationGetNumberOfBlocks, RelationGetRelationName, RelationGetRelid, VacuumCutoffs::relfrozenxid, VacuumCutoffs::relminmxid, RepackLockLevel(), RowExclusiveLock, SearchSysCacheCopy1, table_close(), table_open(), table_relation_copy_for_cluster(), TransactionIdIsValid, TransactionIdPrecedes(), vacuum_get_cutoffs(), and verbose.

Referenced by rebuild_relation().

◆ DecodingWorkerFileName()

void DecodingWorkerFileName ( char fname,
Oid  relid,
uint32  seq 
)

Definition at line 3598 of file repack.c.

3599{
3600 /* The PID is already present in the fileset name, so we needn't add it */
3601 snprintf(fname, MAXPGPATH, "%u-%u", relid, seq);
3602}
#define MAXPGPATH
#define snprintf
Definition port.h:261

References fb(), MAXPGPATH, and snprintf.

Referenced by decode_concurrent_changes(), export_initial_snapshot(), get_initial_snapshot(), and process_concurrent_changes().

◆ determine_clustered_index()

static Oid determine_clustered_index ( Relation  rel,
bool  usingindex,
const char indexname 
)
static

Definition at line 2438 of file repack.c.

2439{
2440 Oid indexOid;
2441
2442 if (indexname == NULL && usingindex)
2443 {
2444 /*
2445 * If USING INDEX with no name is given, find a clustered index, or
2446 * error out if none.
2447 */
2448 indexOid = InvalidOid;
2450 {
2452 {
2453 indexOid = idxoid;
2454 break;
2455 }
2456 }
2457
2458 if (!OidIsValid(indexOid))
2459 ereport(ERROR,
2461 errmsg("there is no previously clustered index for table \"%s\"",
2463 }
2464 else if (indexname != NULL)
2465 {
2466 /* An index was specified; obtain its OID. */
2467 indexOid = get_relname_relid(indexname, rel->rd_rel->relnamespace);
2468 if (!OidIsValid(indexOid))
2469 ereport(ERROR,
2471 errmsg("index \"%s\" for table \"%s\" does not exist",
2472 indexname, RelationGetRelationName(rel)));
2473 }
2474 else
2475 indexOid = InvalidOid;
2476
2477 return indexOid;
2478}
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2116
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4837

References ereport, errcode(), errmsg, ERROR, fb(), foreach_oid, get_index_isclustered(), get_relname_relid(), InvalidOid, OidIsValid, RelationData::rd_rel, RelationGetIndexList(), and RelationGetRelationName.

Referenced by ExecRepack(), and process_single_relation().

◆ ExecRepack()

void ExecRepack ( ParseState pstate,
RepackStmt stmt,
bool  isTopLevel 
)

Definition at line 244 of file repack.c.

245{
246 ClusterParams params = {0};
247 Relation rel = NULL;
249 LOCKMODE lockmode;
250 List *rtcs;
251 bool verbose = false;
252 bool analyze = false;
253 bool concurrently = false;
254
255 /* Parse option list */
256 foreach_node(DefElem, opt, stmt->params)
257 {
258 if (strcmp(opt->defname, "verbose") == 0)
259 verbose = defGetBoolean(opt);
260 else if (strcmp(opt->defname, "analyze") == 0 ||
261 strcmp(opt->defname, "analyse") == 0)
262 analyze = defGetBoolean(opt);
263 else if (strcmp(opt->defname, "concurrently") == 0)
264 {
265 if (stmt->command != REPACK_COMMAND_REPACK)
268 errmsg("CONCURRENTLY option not supported for %s",
269 RepackCommandAsString(stmt->command)));
271 }
272 else
275 errmsg("unrecognized %s option \"%s\"",
276 RepackCommandAsString(stmt->command),
277 opt->defname),
278 parser_errposition(pstate, opt->location));
279 }
280
281 params.options |=
282 (verbose ? CLUOPT_VERBOSE : 0) |
283 (analyze ? CLUOPT_ANALYZE : 0) |
285
286 /* Determine the lock mode to use. */
287 lockmode = RepackLockLevel((params.options & CLUOPT_CONCURRENT) != 0);
288
289 if ((params.options & CLUOPT_CONCURRENT) != 0)
290 {
291 /*
292 * Make sure we're not in a transaction block.
293 *
294 * The reason is that repack_setup_logical_decoding() could wait
295 * indefinitely for our XID to complete. (The deadlock detector would
296 * not recognize it because we'd be waiting for ourselves, i.e. no
297 * real lock conflict.) It would be possible to run in a transaction
298 * block if we had no XID, but this restriction is simpler for users
299 * to understand and we don't lose any functionality.
300 */
301 PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)");
302 }
303
304 /*
305 * If a single relation is specified, process it and we're done ... unless
306 * the relation is a partitioned table, in which case we fall through.
307 */
308 if (stmt->relation != NULL)
309 {
310 rel = process_single_relation(stmt, lockmode, isTopLevel, &params);
311 if (rel == NULL)
312 return; /* all done */
313 }
314
315 /*
316 * Don't allow ANALYZE in the multiple-relation case for now. Maybe we
317 * can add support for this later.
318 */
319 if (params.options & CLUOPT_ANALYZE)
322 errmsg("cannot execute %s on multiple tables",
323 "REPACK (ANALYZE)"));
324
325 /*
326 * By here, we know we are in a multi-table situation.
327 *
328 * Concurrent processing is currently considered rather special (e.g. in
329 * terms of resources consumed) so it is not performed in bulk.
330 */
331 if (params.options & CLUOPT_CONCURRENT)
332 {
333 if (rel != NULL)
334 {
335 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
338 errmsg("%s is not supported for partitioned tables",
339 "REPACK (CONCURRENTLY)"),
340 errhint("Consider running the command on individual partitions."));
341 }
342 else
345 errmsg("%s requires an explicit table name",
346 "REPACK (CONCURRENTLY)"));
347 }
348
349 /*
350 * In order to avoid holding locks for too long, we want to process each
351 * table in its own transaction. This forces us to disallow running
352 * inside a user transaction block.
353 */
355
356 /* Also, we need a memory context to hold our list of relations */
358 "Repack",
360
361 /*
362 * Since we open a new transaction for each relation, we have to check
363 * that the relation still is what we think it is.
364 *
365 * In single-transaction CLUSTER, we don't need the overhead.
366 */
367 params.options |= CLUOPT_RECHECK;
368
369 /*
370 * If we don't have a relation yet, determine a relation list. If we do,
371 * then it must be a partitioned table, and we want to process its
372 * partitions.
373 */
374 if (rel == NULL)
375 {
376 Assert(stmt->indexname == NULL);
377 rtcs = get_tables_to_repack(stmt->command, stmt->usingindex,
380 }
381 else
382 {
383 Oid relid;
384 bool rel_is_index;
385
386 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
387
388 /*
389 * If USING INDEX was specified, resolve the index name now and pass
390 * it down.
391 */
392 if (stmt->usingindex)
393 {
394 /*
395 * If no index name was specified when repacking a partitioned
396 * table, punt for now. Maybe we can improve this later.
397 */
398 if (!stmt->indexname)
399 {
400 if (stmt->command == REPACK_COMMAND_CLUSTER)
403 errmsg("there is no previously clustered index for table \"%s\"",
405 else
408 /*- translator: first %s is name of a SQL command, eg. REPACK */
409 errmsg("cannot execute %s on partitioned table \"%s\" USING INDEX with no index name",
410 RepackCommandAsString(stmt->command),
412 }
413
414 relid = determine_clustered_index(rel, stmt->usingindex,
415 stmt->indexname);
416 if (!OidIsValid(relid))
417 elog(ERROR, "unable to determine index to cluster on");
419
420 rel_is_index = true;
421 }
422 else
423 {
424 relid = RelationGetRelid(rel);
425 rel_is_index = false;
426 }
427
429 relid, rel_is_index,
431
432 /* close parent relation, releasing lock on it */
434 rel = NULL;
435 }
436
437 /* Commit to get out of starting transaction */
440
441 /* Cluster the tables, each in a separate transaction */
442 Assert(rel == NULL);
444 {
445 /* Start a new transaction for each relation. */
447
448 /*
449 * Open the target table, coping with the case where it has been
450 * dropped.
451 */
452 rel = try_table_open(rtc->tableOid, lockmode);
453 if (rel == NULL)
454 {
456 continue;
457 }
458
459 /* functions in indexes may want a snapshot set */
461
462 /* Process this table */
463 cluster_rel(stmt->command, rel, rtc->indexOid, &params, isTopLevel);
464 /* cluster_rel closes the relation, but keeps lock */
465
468 }
469
470 /* Start a new transaction for the cleanup work. */
472
473 /* Clean up working storage */
475}
bool defGetBoolean(DefElem *def)
Definition define.c:93
#define stmt
#define AccessExclusiveLock
Definition lockdefs.h:43
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
int parser_errposition(ParseState *pstate, int location)
Definition parse_node.c:106
@ REPACK_COMMAND_REPACK
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define foreach_node(type, var, lst)
Definition pg_list.h:528
static long analyze(struct nfa *nfa)
Definition regc_nfa.c:3051
static List * get_tables_to_repack_partitioned(RepackCommand cmd, Oid relid, bool rel_is_index, MemoryContext permcxt)
Definition repack.c:2256
static Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, ClusterParams *params)
Definition repack.c:2347
void cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, ClusterParams *params, bool isTopLevel)
Definition repack.c:518
static List * get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
Definition repack.c:2103
static Oid determine_clustered_index(Relation rel, bool usingindex, const char *indexname)
Definition repack.c:2438
#define CLUOPT_ANALYZE
Definition repack.h:28
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
void PopActiveSnapshot(void)
Definition snapmgr.c:775
Relation try_table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:60
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition xact.c:3698
void StartTransactionCommand(void)
Definition xact.c:3109
void CommitTransactionCommand(void)
Definition xact.c:3207

References AccessExclusiveLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, analyze(), Assert, check_index_is_clusterable(), CLUOPT_ANALYZE, CLUOPT_CONCURRENT, CLUOPT_RECHECK, CLUOPT_RECHECK_ISCLUSTERED, CLUOPT_VERBOSE, cluster_rel(), CommitTransactionCommand(), defGetBoolean(), determine_clustered_index(), elog, ereport, errcode(), errhint(), errmsg, ERROR, fb(), foreach_node, foreach_ptr, get_tables_to_repack(), get_tables_to_repack_partitioned(), GetTransactionSnapshot(), MemoryContextDelete(), OidIsValid, ClusterParams::options, parser_errposition(), PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), process_single_relation(), PushActiveSnapshot(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, REPACK_COMMAND_CLUSTER, REPACK_COMMAND_REPACK, RepackCommandAsString(), RepackLockLevel(), StartTransactionCommand(), stmt, table_close(), try_table_open(), and verbose.

Referenced by standard_ProcessUtility().

◆ find_target_tuple()

static bool find_target_tuple ( Relation  rel,
ChangeContext chgcxt,
TupleTableSlot locator,
TupleTableSlot retrieved 
)
static

Definition at line 2823 of file repack.c.

2825{
2826 Form_pg_index idx = chgcxt->cc_ident_index->rd_index;
2827 IndexScanDesc scan;
2828 bool retval = false;
2829
2830 /*
2831 * Scan key is passed by caller, so it does not have to be constructed
2832 * multiple times. Key entries have all fields initialized, except for
2833 * sk_argument.
2834 *
2835 * Use the incoming tuple to finalize the scan key.
2836 */
2837 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
2838 {
2839 ScanKey entry = &chgcxt->cc_ident_key[i];
2840 AttrNumber attno = idx->indkey.values[i];
2841
2842 entry->sk_argument = locator->tts_values[attno - 1];
2843 Assert(!locator->tts_isnull[attno - 1]);
2844 }
2845
2846 /* XXX no instrumentation for now */
2847 scan = index_beginscan(rel, chgcxt->cc_ident_index, GetActiveSnapshot(),
2848 NULL, chgcxt->cc_ident_key_nentries, 0, 0);
2849 index_rescan(scan, chgcxt->cc_ident_key, chgcxt->cc_ident_key_nentries, NULL, 0);
2851 {
2852 /* Be wary of temporal constraints */
2853 if (scan->xs_recheck && !identity_key_equal(chgcxt, locator, retrieved))
2854 {
2856 continue;
2857 }
2858
2859 retval = true;
2860 break;
2861 }
2862 index_endscan(scan);
2863
2864 return retval;
2865}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
int16 AttrNumber
Definition attnum.h:21
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_endscan(IndexScanDesc scan)
Definition indexam.c:394
void index_rescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int norderbys)
Definition indexam.c:368
END_CATALOG_STRUCT typedef FormData_pg_index * Form_pg_index
Definition pg_index.h:74
static bool identity_key_equal(ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *candidate)
Definition repack.c:2876
@ ForwardScanDirection
Definition sdir.h:28
Snapshot GetActiveSnapshot(void)
Definition snapmgr.c:800
Datum sk_argument
Definition skey.h:72
bool * tts_isnull
Definition tuptable.h:133

References Assert, CHECK_FOR_INTERRUPTS, fb(), Form_pg_index, ForwardScanDirection, GetActiveSnapshot(), i, identity_key_equal(), idx(), index_beginscan(), index_endscan(), index_getnext_slot(), index_rescan(), ScanKeyData::sk_argument, TupleTableSlot::tts_isnull, TupleTableSlot::tts_values, and IndexScanDescData::xs_recheck.

Referenced by apply_concurrent_changes().

◆ finish_heap_swap()

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 at line 1882 of file repack.c.

1891{
1892 ObjectAddress object;
1893 Oid mapped_tables[4];
1894 int i;
1895
1896 /* Report that we are now swapping relation files */
1899
1900 /* Zero out possible results from swapped_relation_files */
1901 memset(mapped_tables, 0, sizeof(mapped_tables));
1902
1903 /*
1904 * Swap the contents of the heap relations (including any toast tables).
1905 * Also set old heap's relfrozenxid to frozenXid.
1906 */
1909 swap_toast_by_content, is_internal,
1911
1912 /*
1913 * If it's a system catalog, queue a sinval message to flush all catcaches
1914 * on the catalog when we reach CommandCounterIncrement.
1915 */
1918
1919 if (reindex)
1920 {
1921 int reindex_flags;
1923
1924 /*
1925 * Rebuild each index on the relation (but not the toast table, which
1926 * is all-new at this point). It is important to do this before the
1927 * DROP step because if we are processing a system catalog that will
1928 * be used during DROP, we want to have its indexes available. There
1929 * is no advantage to the other order anyway because this is all
1930 * transactional, so no chance to reclaim disk space before commit. We
1931 * do not need a final CommandCounterIncrement() because
1932 * reindex_relation does it.
1933 *
1934 * Note: because index_build is called via reindex_relation, it will
1935 * never set indcheckxmin true for the indexes. This is OK even
1936 * though in some sense we are building new indexes rather than
1937 * rebuilding existing ones, because the new heap won't contain any
1938 * HOT chains at all, let alone broken ones, so it can't be necessary
1939 * to set indcheckxmin.
1940 */
1944
1945 /*
1946 * Ensure that the indexes have the same persistence as the parent
1947 * relation.
1948 */
1949 if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1951 else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1953
1954 /* Report that we are now reindexing relations */
1957
1959 }
1960
1961 /* Report that we are now doing clean up */
1964
1965 /*
1966 * If the relation being rebuilt is pg_class, swap_relation_files()
1967 * couldn't update pg_class's own pg_class entry (check comments in
1968 * swap_relation_files()), thus relfrozenxid was not updated. That's
1969 * annoying because a potential reason for doing a VACUUM FULL is a
1970 * imminent or actual anti-wraparound shutdown. So, now that we can
1971 * access the new relation using its indices, update relfrozenxid.
1972 * pg_class doesn't have a toast relation, so we don't need to update the
1973 * corresponding toast relation. Not that there's little point moving all
1974 * relfrozenxid updates here since swap_relation_files() needs to write to
1975 * pg_class for non-mapped relations anyway.
1976 */
1978 {
1982
1984
1987 elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1989
1990 relform->relfrozenxid = frozenXid;
1991 relform->relminmxid = cutoffMulti;
1992
1994
1996 }
1997
1998 /* Destroy new heap with old filenumber */
1999 object.classId = RelationRelationId;
2000 object.objectId = OIDNewHeap;
2001 object.objectSubId = 0;
2002
2003 if (!reindex)
2004 {
2005 /*
2006 * Make sure the changes in pg_class are visible. This is especially
2007 * important if !swap_toast_by_content, so that the correct TOAST
2008 * relation is dropped. (reindex_relation() above did not help in this
2009 * case))
2010 */
2012 }
2013
2014 /*
2015 * The new relation is local to our transaction and we know nothing
2016 * depends on it, so DROP_RESTRICT should be OK.
2017 */
2019
2020 /* performDeletion does CommandCounterIncrement at end */
2021
2022 /*
2023 * Now we must remove any relation mapping entries that we set up for the
2024 * transient table, as well as its toast table and toast index if any. If
2025 * we fail to do this before commit, the relmapper will complain about new
2026 * permanent map entries being added post-bootstrap.
2027 */
2028 for (i = 0; OidIsValid(mapped_tables[i]); i++)
2030
2031 /*
2032 * At this point, everything is kosher except that, if we did toast swap
2033 * by links, the toast table's name corresponds to the transient table.
2034 * The name is irrelevant to the backend because it's referenced by OID,
2035 * but users looking at the catalogs could be confused. Rename it to
2036 * prevent this problem.
2037 *
2038 * Note no lock required on the relation, because we already hold an
2039 * exclusive lock on it.
2040 */
2042 {
2044
2046 if (OidIsValid(newrel->rd_rel->reltoastrelid))
2047 {
2048 Oid toastidx;
2050
2051 /* Get the associated valid index to be renamed */
2052 toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
2054
2055 /* rename the toast table ... */
2056 snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
2057 OIDOldHeap);
2058 RenameRelationInternal(newrel->rd_rel->reltoastrelid,
2059 NewToastName, true, false);
2060
2061 /* ... and its valid index too. */
2062 snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
2063 OIDOldHeap);
2064
2066 NewToastName, true, true);
2067
2068 /*
2069 * Reset the relrewrite for the toast. The command-counter
2070 * increment is required here as we are about to update the tuple
2071 * that is updated as part of RenameRelationInternal.
2072 */
2074 ResetRelRewrite(newrel->rd_rel->reltoastrelid);
2075 }
2077 }
2078
2079 /* if it's not a catalog table, clear any missing attribute settings */
2080 if (!is_system_catalog)
2081 {
2083
2087 }
2088}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:279
#define PERFORM_DELETION_INTERNAL
Definition dependency.h:92
void RelationClearMissing(Relation rel)
Definition heap.c:1981
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition index.c:3969
#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
void CacheInvalidateCatalog(Oid catalogId)
Definition inval.c:1609
@ DROP_RESTRICT
#define NAMEDATALEN
#define PROGRESS_REPACK_PHASE_SWAP_REL_FILES
Definition progress.h:104
#define PROGRESS_REPACK_PHASE_FINAL_CLEANUP
Definition progress.h:106
void RelationMapRemoveMapping(Oid relationId)
Definition relmapper.c:439
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:1500
void ResetRelRewrite(Oid myrelid)
Definition tablecmds.c:4420
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition tablecmds.c:4327
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)

References AccessExclusiveLock, CacheInvalidateCatalog(), CatalogTupleUpdate(), CommandCounterIncrement(), DROP_RESTRICT, elog, ERROR, fb(), GETSTRUCT(), HeapTupleIsValid, i, NAMEDATALEN, NoLock, ObjectIdGetDatum(), OidIsValid, PERFORM_DELETION_INTERNAL, performDeletion(), pgstat_progress_update_param(), PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PHASE_FINAL_CLEANUP, PROGRESS_REPACK_PHASE_REBUILD_INDEX, PROGRESS_REPACK_PHASE_SWAP_REL_FILES, REINDEX_REL_CHECK_CONSTRAINTS, REINDEX_REL_FORCE_INDEXES_PERMANENT, REINDEX_REL_FORCE_INDEXES_UNLOGGED, REINDEX_REL_SUPPRESS_INDEX_USE, reindex_relation(), relation_close(), RelationClearMissing(), RelationMapRemoveMapping(), RenameRelationInternal(), ResetRelRewrite(), RowExclusiveLock, SearchSysCacheCopy1, snprintf, swap_relation_files(), table_close(), table_open(), and toast_get_valid_index().

Referenced by ATRewriteTables(), rebuild_relation(), rebuild_relation_finish_concurrent(), and refresh_by_heap_swap().

◆ get_initial_snapshot()

static Snapshot get_initial_snapshot ( DecodingWorker worker)
static

Definition at line 3541 of file repack.c.

3542{
3543 DecodingWorkerShared *shared;
3544 char fname[MAXPGPATH];
3545 BufFile *file;
3547 char *snap_space;
3548 Snapshot snapshot;
3549
3550 shared = (DecodingWorkerShared *) dsm_segment_address(worker->seg);
3551
3552 /*
3553 * The worker needs to initialize the logical decoding, which usually
3554 * takes some time. Therefore it makes sense to prepare for the sleep
3555 * first.
3556 */
3558 for (;;)
3559 {
3560 int last_exported;
3561
3562 SpinLockAcquire(&shared->mutex);
3563 last_exported = shared->last_exported;
3564 SpinLockRelease(&shared->mutex);
3565
3566 /*
3567 * Has the worker exported the file we are waiting for?
3568 */
3569 if (last_exported == WORKER_FILE_SNAPSHOT)
3570 break;
3571
3573 }
3575
3576 /* Read the snapshot from a file. */
3578 file = BufFileOpenFileSet(&shared->sfs.fs, fname, O_RDONLY, false);
3579 BufFileReadExact(file, &snap_size, sizeof(snap_size));
3580 snap_space = (char *) palloc(snap_size);
3582 BufFileClose(file);
3583
3584 /* Restore it. */
3585 snapshot = RestoreSnapshot(snap_space);
3587
3588 return snapshot;
3589}
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:655
void BufFileClose(BufFile *file)
Definition buffile.c:413
size_t Size
Definition c.h:689
bool ConditionVariableCancelSleep(void)
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
void * dsm_segment_address(dsm_segment *seg)
Definition dsm.c:1103
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc(Size size)
Definition mcxt.c:1390
void DecodingWorkerFileName(char *fname, Oid relid, uint32 seq)
Definition repack.c:3598
#define WORKER_FILE_SNAPSHOT
Definition repack.c:98
Snapshot RestoreSnapshot(char *start_address)
Definition snapmgr.c:1793
static void SpinLockRelease(volatile slock_t *lock)
Definition spin.h:62
static void SpinLockAcquire(volatile slock_t *lock)
Definition spin.h:56
ConditionVariable cv
dsm_segment * seg
Definition repack.c:137

References BufFileClose(), BufFileOpenFileSet(), BufFileReadExact(), ConditionVariableCancelSleep(), ConditionVariablePrepareToSleep(), ConditionVariableSleep(), DecodingWorkerShared::cv, DecodingWorkerFileName(), dsm_segment_address(), fb(), SharedFileSet::fs, DecodingWorkerShared::last_exported, MAXPGPATH, DecodingWorkerShared::mutex, palloc(), pfree(), DecodingWorkerShared::relid, RestoreSnapshot(), DecodingWorker::seg, DecodingWorkerShared::sfs, SpinLockAcquire(), SpinLockRelease(), and WORKER_FILE_SNAPSHOT.

Referenced by rebuild_relation().

◆ get_tables_to_repack()

static List * get_tables_to_repack ( RepackCommand  cmd,
bool  usingindex,
MemoryContext  permcxt 
)
static

Definition at line 2103 of file repack.c.

2104{
2106 TableScanDesc scan;
2107 HeapTuple tuple;
2108 List *rtcs = NIL;
2109
2110 if (usingindex)
2111 {
2112 ScanKeyData entry;
2113
2114 /*
2115 * For USING INDEX, scan pg_index to find those with indisclustered.
2116 */
2118 ScanKeyInit(&entry,
2121 BoolGetDatum(true));
2122 scan = table_beginscan_catalog(catalog, 1, &entry);
2123 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2124 {
2130
2131 index = (Form_pg_index) GETSTRUCT(tuple);
2132
2133 /*
2134 * Try to obtain a light lock on the index's table, to ensure it
2135 * doesn't go away while we collect the list. If we cannot, just
2136 * disregard it. Be sure to release this if we ultimately decide
2137 * not to process the table!
2138 */
2140 continue;
2141
2142 /* Verify that the table still exists; skip if not */
2145 {
2147 continue;
2148 }
2150
2151 /* Skip temp relations belonging to other sessions */
2152 if (classForm->relpersistence == RELPERSISTENCE_TEMP &&
2153 !isTempOrTempToastNamespace(classForm->relnamespace))
2154 {
2157 continue;
2158 }
2159
2161
2162 /* noisily skip rels which the user can't process */
2163 if (!repack_is_permitted_for_relation(cmd, index->indrelid,
2164 GetUserId()))
2165 {
2167 continue;
2168 }
2169
2170 /* Use a permanent memory context for the result list */
2173 rtc->tableOid = index->indrelid;
2174 rtc->indexOid = index->indexrelid;
2175 rtcs = lappend(rtcs, rtc);
2177 }
2178 }
2179 else
2180 {
2183
2184 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2185 {
2187 Form_pg_class class;
2189
2190 class = (Form_pg_class) GETSTRUCT(tuple);
2191
2192 /*
2193 * Try to obtain a light lock on the table, to ensure it doesn't
2194 * go away while we collect the list. If we cannot, just
2195 * disregard the table. Be sure to release this if we ultimately
2196 * decide not to process the table!
2197 */
2199 continue;
2200
2201 /* Verify that the table still exists */
2203 {
2205 continue;
2206 }
2207
2208 /* Can only process plain tables and matviews */
2209 if (class->relkind != RELKIND_RELATION &&
2210 class->relkind != RELKIND_MATVIEW)
2211 {
2213 continue;
2214 }
2215
2216 /* Skip temp relations belonging to other sessions */
2217 if (class->relpersistence == RELPERSISTENCE_TEMP &&
2218 !isTempOrTempToastNamespace(class->relnamespace))
2219 {
2221 continue;
2222 }
2223
2224 /* noisily skip rels which the user can't process */
2226 GetUserId()))
2227 {
2229 continue;
2230 }
2231
2232 /* Use a permanent memory context for the result list */
2235 rtc->tableOid = class->oid;
2236 rtc->indexOid = InvalidOid;
2237 rtcs = lappend(rtcs, rtc);
2239 }
2240 }
2241
2242 table_endscan(scan);
2244
2245 return rtcs;
2246}
#define palloc_object(type)
Definition fe_memutils.h:89
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1435
List * lappend(List *list, void *datum)
Definition list.c:339
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:151
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:229
#define AccessShareLock
Definition lockdefs.h:36
Oid GetUserId(void)
Definition miscinit.c:470
bool isTempOrTempToastNamespace(Oid namespaceId)
Definition namespace.c:3745
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, ScanKeyData *key)
Definition tableam.c:113
static void table_endscan(TableScanDesc scan)
Definition tableam.h:1061

References AccessShareLock, BoolGetDatum(), BTEqualStrategyNumber, ConditionalLockRelationOid(), fb(), Form_pg_index, ForwardScanDirection, GETSTRUCT(), GetUserId(), heap_getnext(), HeapTupleIsValid, InvalidOid, isTempOrTempToastNamespace(), lappend(), MemoryContextSwitchTo(), NIL, ObjectIdGetDatum(), palloc_object, relation_close(), ReleaseSysCache(), repack_is_permitted_for_relation(), ScanKeyInit(), SearchSysCache1(), SearchSysCacheExists1, table_beginscan_catalog(), table_endscan(), table_open(), and UnlockRelationOid().

Referenced by ExecRepack().

◆ get_tables_to_repack_partitioned()

static List * get_tables_to_repack_partitioned ( RepackCommand  cmd,
Oid  relid,
bool  rel_is_index,
MemoryContext  permcxt 
)
static

Definition at line 2256 of file repack.c.

2258{
2259 List *inhoids;
2260 List *rtcs = NIL;
2261
2262 /*
2263 * Do not lock the children until they're processed. Note that we do hold
2264 * a lock on the parent partitioned table.
2265 */
2268 {
2269 Oid table_oid,
2270 index_oid;
2273
2274 if (rel_is_index)
2275 {
2276 /* consider only leaf indexes */
2278 continue;
2279
2282 }
2283 else
2284 {
2285 /* consider only leaf relations */
2287 continue;
2288
2291 }
2292
2293 /*
2294 * It's possible that the user does not have privileges to CLUSTER the
2295 * leaf partition despite having them on the partitioned table. Skip
2296 * if so.
2297 */
2299 continue;
2300
2301 /* Use a permanent memory context for the result list */
2304 rtc->tableOid = table_oid;
2305 rtc->indexOid = index_oid;
2306 rtcs = lappend(rtcs, rtc);
2308 }
2309
2310 return rtcs;
2311}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3604
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2234
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)

References fb(), find_all_inheritors(), foreach_oid, get_rel_relkind(), GetUserId(), IndexGetRelation(), InvalidOid, lappend(), MemoryContextSwitchTo(), NIL, NoLock, palloc_object, and repack_is_permitted_for_relation().

Referenced by ExecRepack().

◆ HandleRepackMessageInterrupt()

void HandleRepackMessageInterrupt ( void  )

Definition at line 3612 of file repack.c.

3613{
3614 InterruptPending = true;
3615 RepackMessagePending = true;
3617}
volatile sig_atomic_t InterruptPending
Definition globals.c:32
struct Latch * MyLatch
Definition globals.c:65
void SetLatch(Latch *latch)
Definition latch.c:290
volatile sig_atomic_t RepackMessagePending
Definition repack.c:150

References InterruptPending, MyLatch, RepackMessagePending, and SetLatch().

Referenced by procsignal_sigusr1_handler().

◆ identity_key_equal()

static bool identity_key_equal ( ChangeContext chgcxt,
TupleTableSlot locator,
TupleTableSlot candidate 
)
static

Definition at line 2876 of file repack.c.

2878{
2879 slot_getsomeattrs(locator, chgcxt->cc_last_key_attno);
2880 slot_getsomeattrs(candidate, chgcxt->cc_last_key_attno);
2881
2882 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
2883 {
2884 ScanKey entry = &chgcxt->cc_ident_key[i];
2885 AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
2886
2887 Assert(attno > 0);
2888
2889 if (locator->tts_isnull[attno - 1] != candidate->tts_isnull[attno - 1])
2890 return false;
2891
2892 if (locator->tts_isnull[attno - 1])
2893 continue;
2894
2896 entry->sk_collation,
2897 candidate->tts_values[attno - 1],
2898 entry->sk_argument)))
2899 return false;
2900 }
2901
2902 return true;
2903}
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition fmgr.c:1151
static bool DatumGetBool(Datum X)
Definition postgres.h:100
FmgrInfo sk_func
Definition skey.h:71
Oid sk_collation
Definition skey.h:70

References Assert, DatumGetBool(), fb(), FunctionCall2Coll(), i, ScanKeyData::sk_argument, ScanKeyData::sk_collation, ScanKeyData::sk_func, slot_getsomeattrs(), and TupleTableSlot::tts_isnull.

Referenced by find_target_tuple().

◆ initialize_change_context()

static void initialize_change_context ( ChangeContext chgcxt,
Relation  relation,
Oid  ident_index_id 
)
static

Definition at line 2969 of file repack.c.

2971{
2972 chgcxt->cc_rel = relation;
2973
2974 /* Only initialize fields needed by ExecInsertIndexTuples(). */
2975 chgcxt->cc_estate = CreateExecutorState();
2976
2977 chgcxt->cc_rri = (ResultRelInfo *) palloc(sizeof(ResultRelInfo));
2978 InitResultRelInfo(chgcxt->cc_rri, relation, 0, 0, 0);
2979 ExecOpenIndices(chgcxt->cc_rri, false);
2980
2981 /*
2982 * The table's relcache entry already has the relcache entry for the
2983 * identity index; find that.
2984 */
2985 chgcxt->cc_ident_index = NULL;
2986 for (int i = 0; i < chgcxt->cc_rri->ri_NumIndices; i++)
2987 {
2989
2990 ind_rel = chgcxt->cc_rri->ri_IndexRelationDescs[i];
2991 if (ind_rel->rd_id == ident_index_id)
2992 {
2993 chgcxt->cc_ident_index = ind_rel;
2994 break;
2995 }
2996 }
2997 if (chgcxt->cc_ident_index == NULL)
2998 elog(ERROR, "failed to find identity index");
2999
3000 /* Set up for scanning said identity index */
3001 {
3003
3004 indexForm = chgcxt->cc_ident_index->rd_index;
3005 chgcxt->cc_ident_key_nentries = indexForm->indnkeyatts;
3006 chgcxt->cc_ident_key = (ScanKey) palloc_array(ScanKeyData, indexForm->indnkeyatts);
3007 for (int i = 0; i < indexForm->indnkeyatts; i++)
3008 {
3009 ScanKey entry;
3010 Oid opfamily,
3011 opcintype,
3012 opno,
3013 opcode;
3015
3016 entry = &chgcxt->cc_ident_key[i];
3017
3018 opfamily = chgcxt->cc_ident_index->rd_opfamily[i];
3019 opcintype = chgcxt->cc_ident_index->rd_opcintype[i];
3021 chgcxt->cc_ident_index->rd_rel->relam,
3022 opfamily, false);
3024 elog(ERROR, "could not find equality strategy for index operator family %u for type %u",
3025 opfamily, opcintype);
3026 opno = get_opfamily_member(opfamily, opcintype, opcintype,
3027 eq_strategy);
3028 if (!OidIsValid(opno))
3029 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
3030 eq_strategy, opcintype, opcintype, opfamily);
3031 opcode = get_opcode(opno);
3032 if (!OidIsValid(opcode))
3033 elog(ERROR, "missing oprcode for operator %u", opno);
3034
3035 /* Initialize everything but argument. */
3036 ScanKeyInit(entry,
3037 i + 1,
3038 eq_strategy, opcode,
3039 (Datum) 0);
3040 entry->sk_collation = chgcxt->cc_ident_index->rd_indcollation[i];
3041 }
3042 }
3043
3044 /* Determine the last column we must deform to read the identity */
3045 chgcxt->cc_last_key_attno = InvalidAttrNumber;
3046 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
3047 {
3048 AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
3049
3050 Assert(attno > 0);
3051 chgcxt->cc_last_key_attno = Max(chgcxt->cc_last_key_attno, attno);
3052 }
3053
3054 chgcxt->cc_file_seq = WORKER_FILE_SNAPSHOT + 1;
3055}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition amapi.c:161
#define InvalidAttrNumber
Definition attnum.h:23
#define Max(x, y)
Definition c.h:1085
@ COMPARE_EQ
Definition cmptype.h:36
void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative)
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition execMain.c:1271
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define palloc_array(type, count)
Definition fe_memutils.h:91
RegProcedure get_opcode(Oid opno)
Definition lsyscache.c:1516
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:170
ScanKeyData * ScanKey
Definition skey.h:75
uint16 StrategyNumber
Definition stratnum.h:22
#define InvalidStrategy
Definition stratnum.h:24

References Assert, COMPARE_EQ, CreateExecutorState(), elog, ERROR, ExecOpenIndices(), fb(), Form_pg_index, get_opcode(), get_opfamily_member(), i, IndexAmTranslateCompareType(), InitResultRelInfo(), InvalidAttrNumber, InvalidStrategy, Max, OidIsValid, palloc(), palloc_array, ScanKeyInit(), ScanKeyData::sk_collation, and WORKER_FILE_SNAPSHOT.

Referenced by rebuild_relation_finish_concurrent().

◆ make_new_heap()

Oid make_new_heap ( Oid  OIDOldHeap,
Oid  NewTableSpace,
Oid  NewAccessMethod,
char  relpersistence,
LOCKMODE  lockmode 
)

Definition at line 1125 of file repack.c.

1127{
1131 Oid toastid;
1133 HeapTuple tuple;
1134 Datum reloptions;
1135 bool isNull;
1137
1138 OldHeap = table_open(OIDOldHeap, lockmode);
1140
1141 /*
1142 * Note that the NewHeap will not receive any of the defaults or
1143 * constraints associated with the OldHeap; we don't need 'em, and there's
1144 * no reason to spend cycles inserting them into the catalogs only to
1145 * delete them.
1146 */
1147
1148 /*
1149 * But we do want to use reloptions of the old heap for new heap.
1150 */
1152 if (!HeapTupleIsValid(tuple))
1153 elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1154 reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
1155 &isNull);
1156 if (isNull)
1157 reloptions = (Datum) 0;
1158
1159 if (relpersistence == RELPERSISTENCE_TEMP)
1161 else
1163
1164 /*
1165 * Create the new heap, using a temporary name in the same namespace as
1166 * the existing table. NOTE: there is some risk of collision with user
1167 * relnames. Working around this seems more trouble than it's worth; in
1168 * particular, we can't create the new heap in a different namespace from
1169 * the old, or we will have problems with the TEMP status of temp tables.
1170 *
1171 * Note: the new heap is not a shared relation, even if we are rebuilding
1172 * a shared rel. However, we do make the new heap mapped if the source is
1173 * mapped. This simplifies swap_relation_files, and is absolutely
1174 * necessary for rebuilding pg_class, for reasons explained there.
1175 */
1176 snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
1177
1181 InvalidOid,
1182 InvalidOid,
1183 InvalidOid,
1184 OldHeap->rd_rel->relowner,
1187 NIL,
1189 relpersistence,
1190 false,
1193 reloptions,
1194 false,
1195 true,
1196 true,
1197 OIDOldHeap,
1198 NULL);
1200
1201 ReleaseSysCache(tuple);
1202
1203 /*
1204 * Advance command counter so that the newly-created relation's catalog
1205 * tuples will be visible to table_open.
1206 */
1208
1209 /*
1210 * If necessary, create a TOAST table for the new relation.
1211 *
1212 * If the relation doesn't have a TOAST table already, we can't need one
1213 * for the new relation. The other way around is possible though: if some
1214 * wide columns have been dropped, NewHeapCreateToastTable can decide that
1215 * no TOAST table is needed for the new table.
1216 *
1217 * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
1218 * that the TOAST table will be visible for insertion.
1219 */
1220 toastid = OldHeap->rd_rel->reltoastrelid;
1221 if (OidIsValid(toastid))
1222 {
1223 /* keep the existing toast table's reloptions, if any */
1225 if (!HeapTupleIsValid(tuple))
1226 elog(ERROR, "cache lookup failed for relation %u", toastid);
1227 reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
1228 &isNull);
1229 if (isNull)
1230 reloptions = (Datum) 0;
1231
1232 NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid);
1233
1234 ReleaseSysCache(tuple);
1235 }
1236
1238
1239 return OIDNewHeap;
1240}
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:1137
Oid LookupCreationNamespace(const char *nspname)
Definition namespace.c:3500
@ ONCOMMIT_NOOP
Definition primnodes.h:59
#define RelationIsMapped(relation)
Definition rel.h:565
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, Oid OIDOldToast)
Definition toasting.c:65

References Assert, CommandCounterIncrement(), elog, ERROR, fb(), heap_create_with_catalog(), HeapTupleIsValid, InvalidOid, LookupCreationNamespace(), NAMEDATALEN, NewHeapCreateToastTable(), NIL, NoLock, ObjectIdGetDatum(), OidIsValid, ONCOMMIT_NOOP, RelationGetDescr, RelationGetNamespace, RelationIsMapped, ReleaseSysCache(), SearchSysCache1(), snprintf, SysCacheGetAttr(), table_close(), and table_open().

Referenced by ATRewriteTables(), rebuild_relation(), and RefreshMatViewByOid().

◆ mark_index_clustered()

void mark_index_clustered ( Relation  rel,
Oid  indexOid,
bool  is_internal 
)

Definition at line 827 of file repack.c.

828{
833
834 Assert(rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE);
835
836 /*
837 * If the index is already marked clustered, no need to do anything.
838 */
839 if (OidIsValid(indexOid))
840 {
841 if (get_index_isclustered(indexOid))
842 return;
843 }
844
845 /*
846 * Check each index of the relation and set/clear the bit as needed.
847 */
849
850 foreach(index, RelationGetIndexList(rel))
851 {
853
857 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
859
860 /*
861 * Unset the bit if set. We know it's wrong because we checked this
862 * earlier.
863 */
864 if (indexForm->indisclustered)
865 {
866 indexForm->indisclustered = false;
868 }
869 else if (thisIndexOid == indexOid)
870 {
871 /* this was checked earlier, but let's be real sure */
872 if (!indexForm->indisvalid)
873 elog(ERROR, "cannot cluster on invalid index %u", indexOid);
874 indexForm->indisclustered = true;
876 }
877
879 InvalidOid, is_internal);
880
882 }
883
885}
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
#define lfirst_oid(lc)
Definition pg_list.h:174

References Assert, CatalogTupleUpdate(), elog, ERROR, fb(), Form_pg_index, get_index_isclustered(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHookArg, lfirst_oid, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, RelationGetIndexList(), RowExclusiveLock, SearchSysCacheCopy1, table_close(), and table_open().

Referenced by ATExecClusterOn(), ATExecDropCluster(), and rebuild_relation().

◆ process_concurrent_changes()

static void process_concurrent_changes ( XLogRecPtr  end_of_wal,
ChangeContext chgcxt,
bool  done 
)
static

Definition at line 2913 of file repack.c.

2914{
2915 DecodingWorkerShared *shared;
2916 char fname[MAXPGPATH];
2917 BufFile *file;
2918
2921
2922 /* Ask the worker for the file. */
2924 SpinLockAcquire(&shared->mutex);
2925 shared->lsn_upto = end_of_wal;
2926 shared->done = done;
2927 SpinLockRelease(&shared->mutex);
2928
2929 /*
2930 * The worker needs to finish processing of the current WAL record. Even
2931 * if it's idle, it'll need to close the output file. Thus we're likely to
2932 * wait, so prepare for sleep.
2933 */
2935 for (;;)
2936 {
2937 int last_exported;
2938
2939 SpinLockAcquire(&shared->mutex);
2940 last_exported = shared->last_exported;
2941 SpinLockRelease(&shared->mutex);
2942
2943 /*
2944 * Has the worker exported the file we are waiting for?
2945 */
2946 if (last_exported == chgcxt->cc_file_seq)
2947 break;
2948
2950 }
2952
2953 /* Open the file. */
2954 DecodingWorkerFileName(fname, shared->relid, chgcxt->cc_file_seq);
2955 file = BufFileOpenFileSet(&shared->sfs.fs, fname, O_RDONLY, false);
2957
2958 BufFileClose(file);
2959
2960 /* Get ready for the next file. */
2961 chgcxt->cc_file_seq++;
2962}
#define PROGRESS_REPACK_PHASE_CATCH_UP
Definition progress.h:103
static void apply_concurrent_changes(BufFile *file, ChangeContext *chgcxt)
Definition repack.c:2499
static DecodingWorker * decoding_worker
Definition repack.c:144

References apply_concurrent_changes(), BufFileClose(), BufFileOpenFileSet(), ConditionVariableCancelSleep(), ConditionVariablePrepareToSleep(), ConditionVariableSleep(), DecodingWorkerShared::cv, decoding_worker, DecodingWorkerFileName(), DecodingWorkerShared::done, dsm_segment_address(), fb(), SharedFileSet::fs, DecodingWorkerShared::last_exported, DecodingWorkerShared::lsn_upto, MAXPGPATH, DecodingWorkerShared::mutex, pgstat_progress_update_param(), PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PHASE_CATCH_UP, DecodingWorkerShared::relid, DecodingWorker::seg, DecodingWorkerShared::sfs, SpinLockAcquire(), and SpinLockRelease().

Referenced by rebuild_relation_finish_concurrent().

◆ process_single_relation()

static Relation process_single_relation ( RepackStmt stmt,
LOCKMODE  lockmode,
bool  isTopLevel,
ClusterParams params 
)
static

Definition at line 2347 of file repack.c.

2349{
2350 Relation rel;
2351 Oid tableOid;
2352
2353 Assert(stmt->relation != NULL);
2354 Assert(stmt->command == REPACK_COMMAND_CLUSTER ||
2355 stmt->command == REPACK_COMMAND_REPACK);
2356
2357 /*
2358 * Make sure ANALYZE is specified if a column list is present.
2359 */
2360 if ((params->options & CLUOPT_ANALYZE) == 0 && stmt->relation->va_cols != NIL)
2361 ereport(ERROR,
2363 errmsg("ANALYZE option must be specified when a column list is provided"));
2364
2365 /* Find, lock, and check permissions on the table. */
2366 tableOid = RangeVarGetRelidExtended(stmt->relation->relation,
2367 lockmode,
2368 0,
2370 NULL);
2371 rel = table_open(tableOid, NoLock);
2372
2373 /*
2374 * Reject clustering a remote temp table ... their local buffer manager is
2375 * not going to cope.
2376 */
2377 if (RELATION_IS_OTHER_TEMP(rel))
2378 ereport(ERROR,
2380 /*- translator: first %s is name of a SQL command, eg. REPACK */
2381 errmsg("cannot execute %s on temporary tables of other sessions",
2382 RepackCommandAsString(stmt->command)));
2383
2384 /*
2385 * For partitioned tables, let caller handle this. Otherwise, process it
2386 * here and we're done.
2387 */
2388 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2389 return rel;
2390 else
2391 {
2392 Oid indexOid = InvalidOid;
2393
2394 indexOid = determine_clustered_index(rel, stmt->usingindex,
2395 stmt->indexname);
2396 if (OidIsValid(indexOid))
2397 check_index_is_clusterable(rel, indexOid, lockmode);
2398
2399 cluster_rel(stmt->command, rel, indexOid, params, isTopLevel);
2400
2401 /*
2402 * Do an analyze, if requested. We close the transaction and start a
2403 * new one, so that we don't hold the stronger lock for longer than
2404 * needed.
2405 */
2406 if (params->options & CLUOPT_ANALYZE)
2407 {
2409
2412
2415
2416 vac_params.options |= VACOPT_ANALYZE;
2417 if (params->options & CLUOPT_VERBOSE)
2418 vac_params.options |= VACOPT_VERBOSE;
2419 analyze_rel(tableOid, NULL, &vac_params,
2420 stmt->relation->va_cols, true, NULL);
2423 }
2424
2425 return NULL;
2426 }
2427}
void analyze_rel(Oid relid, RangeVar *relation, const VacuumParams *params, List *va_cols, bool in_outer_xact, BufferAccessStrategy bstrategy)
Definition analyze.c:110
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition namespace.c:442
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
#define VACOPT_VERBOSE
Definition vacuum.h:181
#define VACOPT_ANALYZE
Definition vacuum.h:180

References analyze_rel(), Assert, check_index_is_clusterable(), CLUOPT_ANALYZE, CLUOPT_VERBOSE, cluster_rel(), CommandCounterIncrement(), CommitTransactionCommand(), determine_clustered_index(), ereport, errcode(), errmsg, ERROR, fb(), GetTransactionSnapshot(), InvalidOid, NIL, NoLock, OidIsValid, ClusterParams::options, PopActiveSnapshot(), PushActiveSnapshot(), RangeVarCallbackMaintainsTable(), RangeVarGetRelidExtended(), RelationData::rd_rel, RELATION_IS_OTHER_TEMP, REPACK_COMMAND_CLUSTER, REPACK_COMMAND_REPACK, RepackCommandAsString(), StartTransactionCommand(), stmt, table_open(), VACOPT_ANALYZE, and VACOPT_VERBOSE.

Referenced by ExecRepack().

◆ ProcessRepackMessage()

static void ProcessRepackMessage ( StringInfo  msg)
static

Definition at line 3708 of file repack.c.

3709{
3710 char msgtype;
3711
3712 msgtype = pq_getmsgbyte(msg);
3713
3714 switch (msgtype)
3715 {
3718 {
3720
3721 /* Parse ErrorResponse or NoticeResponse. */
3723
3724 /* Death of a worker isn't enough justification for suicide. */
3725 edata.elevel = Min(edata.elevel, ERROR);
3726
3727 /*
3728 * Add a context line to show that this is a message
3729 * propagated from the worker. Otherwise, it can sometimes be
3730 * confusing to understand what actually happened.
3731 */
3732 if (edata.context)
3733 edata.context = psprintf("%s\n%s", edata.context,
3734 _("REPACK decoding worker"));
3735 else
3736 edata.context = pstrdup(_("REPACK decoding worker"));
3737
3738 /* Rethrow error or print notice. */
3740
3741 break;
3742 }
3743
3744 default:
3745 {
3746 elog(ERROR, "unrecognized message type received from decoding worker: %c (message length %d bytes)",
3747 msgtype, msg->len);
3748 }
3749 }
3750}
#define Min(x, y)
Definition c.h:1091
void ThrowErrorData(ErrorData *edata)
Definition elog.c:2091
#define _(x)
Definition elog.c:96
char * pstrdup(const char *in)
Definition mcxt.c:1910
int pq_getmsgbyte(StringInfo msg)
Definition pqformat.c:398
void pq_parse_errornotice(StringInfo msg, ErrorData *edata)
Definition pqmq.c:229
#define PqMsg_ErrorResponse
Definition protocol.h:44
#define PqMsg_NoticeResponse
Definition protocol.h:49
char * psprintf(const char *fmt,...)
Definition psprintf.c:43

References _, elog, ERROR, fb(), StringInfoData::len, Min, pq_getmsgbyte(), pq_parse_errornotice(), PqMsg_ErrorResponse, PqMsg_NoticeResponse, psprintf(), pstrdup(), and ThrowErrorData().

Referenced by ProcessRepackMessages().

◆ ProcessRepackMessages()

void ProcessRepackMessages ( void  )

Definition at line 3623 of file repack.c.

3624{
3625 MemoryContext oldcontext;
3627
3628 /*
3629 * Nothing to do if we haven't launched the worker yet or have already
3630 * terminated it.
3631 */
3632 if (decoding_worker == NULL)
3633 return;
3634
3635 /*
3636 * This is invoked from ProcessInterrupts(), and since some of the
3637 * functions it calls contain CHECK_FOR_INTERRUPTS(), there is a potential
3638 * for recursive calls if more signals are received while this runs. It's
3639 * unclear that recursive entry would be safe, and it doesn't seem useful
3640 * even if it is safe, so let's block interrupts until done.
3641 */
3643
3644 /*
3645 * Moreover, CurrentMemoryContext might be pointing almost anywhere. We
3646 * don't want to risk leaking data into long-lived contexts, so let's do
3647 * our work here in a private context that we can reset on each use.
3648 */
3649 if (hpm_context == NULL) /* first time through? */
3651 "ProcessRepackMessages",
3653 else
3655
3656 oldcontext = MemoryContextSwitchTo(hpm_context);
3657
3658 /* OK to process messages. Reset the flag saying there are more to do. */
3659 RepackMessagePending = false;
3660
3661 /*
3662 * Read as many messages as we can from the worker, but stop when no more
3663 * messages can be read from the worker without blocking.
3664 */
3665 while (true)
3666 {
3667 shm_mq_result res;
3668 Size nbytes;
3669 void *data;
3670
3672 &data, true);
3673 if (res == SHM_MQ_WOULD_BLOCK)
3674 break;
3675 else if (res == SHM_MQ_SUCCESS)
3676 {
3677 StringInfoData msg;
3678
3679 initStringInfo(&msg);
3680 appendBinaryStringInfo(&msg, data, nbytes);
3682 pfree(msg.data);
3683 }
3684 else
3685 {
3686 /*
3687 * The decoding worker is special in that it exits as soon as it
3688 * has its work done. Thus the DETACHED result code is fine.
3689 */
3690 Assert(res == SHM_MQ_DETACHED);
3691
3692 break;
3693 }
3694 }
3695
3696 MemoryContextSwitchTo(oldcontext);
3697
3698 /* Might as well clear the context on our way out */
3700
3702}
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
MemoryContext TopMemoryContext
Definition mcxt.c:167
#define RESUME_INTERRUPTS()
Definition miscadmin.h:138
#define HOLD_INTERRUPTS()
Definition miscadmin.h:136
const void * data
static void ProcessRepackMessage(StringInfo msg)
Definition repack.c:3708
shm_mq_result shm_mq_receive(shm_mq_handle *mqh, Size *nbytesp, void **datap, bool nowait)
Definition shm_mq.c:574
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
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition stringinfo.c:281
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
shm_mq_handle * error_mqh
Definition repack.c:140

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, appendBinaryStringInfo(), Assert, StringInfoData::data, data, decoding_worker, DecodingWorker::error_mqh, fb(), HOLD_INTERRUPTS, initStringInfo(), MemoryContextReset(), MemoryContextSwitchTo(), pfree(), ProcessRepackMessage(), RepackMessagePending, RESUME_INTERRUPTS, SHM_MQ_DETACHED, shm_mq_receive(), SHM_MQ_SUCCESS, SHM_MQ_WOULD_BLOCK, and TopMemoryContext.

Referenced by ProcessInterrupts().

◆ rebuild_relation()

static void rebuild_relation ( Relation  OldHeap,
Relation  index,
bool  verbose,
Oid  ident_idx 
)
static

Definition at line 978 of file repack.c.

980{
981 Oid tableOid = RelationGetRelid(OldHeap);
982 Oid accessMethod = OldHeap->rd_rel->relam;
983 Oid tableSpace = OldHeap->rd_rel->reltablespace;
986 char relpersistence;
990 bool concurrent = OidIsValid(ident_idx);
991 Snapshot snapshot = NULL;
992#if USE_ASSERT_CHECKING
994
995 lmode = RepackLockLevel(concurrent);
996
999#endif
1000
1001 if (concurrent)
1002 {
1003 /*
1004 * The worker needs to be member of the locking group we're the leader
1005 * of. We ought to become the leader before the worker starts. The
1006 * worker will join the group as soon as it starts.
1007 *
1008 * This is to make sure that the deadlock described below is
1009 * detectable by deadlock.c: if the worker waits for a transaction to
1010 * complete and we are waiting for the worker output, then effectively
1011 * we (i.e. this backend) are waiting for that transaction.
1012 */
1014
1015 /*
1016 * Start the worker that decodes data changes applied while we're
1017 * copying the table contents.
1018 *
1019 * Note that the worker has to wait for all transactions with XID
1020 * already assigned to finish. If some of those transactions is
1021 * waiting for a lock conflicting with ShareUpdateExclusiveLock on our
1022 * table (e.g. it runs CREATE INDEX), we can end up in a deadlock.
1023 * Not sure this risk is worth unlocking/locking the table (and its
1024 * clustering index) and checking again if it's still eligible for
1025 * REPACK CONCURRENTLY.
1026 */
1028
1029 /*
1030 * Wait until the worker has the initial snapshot and retrieve it.
1031 */
1033
1034 PushActiveSnapshot(snapshot);
1035 }
1036
1037 /* for CLUSTER or REPACK USING INDEX, mark the index as the one to use */
1038 if (index != NULL)
1040
1041 /* Remember info about rel before closing OldHeap */
1042 relpersistence = OldHeap->rd_rel->relpersistence;
1043
1044 /*
1045 * Create the transient table that will receive the re-ordered data.
1046 *
1047 * OldHeap is already locked, so no need to lock it again. make_new_heap
1048 * obtains AccessExclusiveLock on the new heap and its toast table.
1049 */
1050 OIDNewHeap = make_new_heap(tableOid, tableSpace,
1051 accessMethod,
1052 relpersistence,
1053 NoLock);
1056
1057 /* Copy the heap data into the new table in the desired order */
1060
1061 /* The historic snapshot won't be needed anymore. */
1062 if (snapshot)
1063 {
1066 }
1067
1068 if (concurrent)
1069 {
1071
1072 /*
1073 * Close the index, but keep the lock. Both heaps will be closed by
1074 * the following call.
1075 */
1076 if (index)
1078
1081
1084 }
1085 else
1086 {
1088
1089 /* Close relcache entries, but keep lock until transaction commit */
1091 if (index)
1093
1094 /*
1095 * Close the new relation so it can be dropped as soon as the storage
1096 * is swapped. The relation is not visible to others, so no need to
1097 * unlock it explicitly.
1098 */
1100
1101 /*
1102 * Swap the physical files of the target and transient tables, then
1103 * rebuild the target's indexes and throw away the transient table.
1104 */
1106 swap_toast_by_content, false, true,
1107 true, /* reindex */
1109 relpersistence);
1110 }
1111}
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:334
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:351
static void start_repack_decoding_worker(Oid relid)
Definition repack.c:3407
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:1882
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition repack.c:1125
static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, Snapshot snapshot, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
Definition repack.c:1254
static void rebuild_relation_finish_concurrent(Relation NewHeap, Relation OldHeap, Oid identIdx, TransactionId frozenXid, MultiXactId cutoffMulti)
Definition repack.c:3079
static Snapshot get_initial_snapshot(DecodingWorker *worker)
Definition repack.c:3541
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition repack.c:827
void BecomeLockGroupLeader(void)
Definition proc.c:2042

References AccessExclusiveLock, Assert, BecomeLockGroupLeader(), CheckRelationLockedByMe(), CheckRelationOidLockedByMe(), copy_table_data(), decoding_worker, fb(), finish_heap_swap(), get_initial_snapshot(), index_close(), IsSystemRelation(), make_new_heap(), mark_index_clustered(), NoLock, OidIsValid, pgstat_progress_update_param(), PopActiveSnapshot(), PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PHASE_FINAL_CLEANUP, PushActiveSnapshot(), rebuild_relation_finish_concurrent(), RelationGetRelid, RepackLockLevel(), start_repack_decoding_worker(), table_close(), table_open(), UpdateActiveSnapshotCommandId(), and verbose.

Referenced by cluster_rel().

◆ rebuild_relation_finish_concurrent()

static void rebuild_relation_finish_concurrent ( Relation  NewHeap,
Relation  OldHeap,
Oid  identIdx,
TransactionId  frozenXid,
MultiXactId  cutoffMulti 
)
static

Definition at line 3079 of file repack.c.

3082{
3087 ListCell *lc,
3088 *lc2;
3089 char relpersistence;
3090 bool is_system_catalog;
3092 XLogRecPtr end_of_wal;
3093 List *indexrels;
3095
3098
3099 /*
3100 * Unlike the exclusive case, we build new indexes for the new relation
3101 * rather than swapping the storage and reindexing the old relation. The
3102 * point is that the index build can take some time, so we do it before we
3103 * get AccessExclusiveLock on the old heap and therefore we cannot swap
3104 * the heap storage yet.
3105 *
3106 * index_create() will lock the new indexes using AccessExclusiveLock - no
3107 * need to change that. At the same time, we use ShareUpdateExclusiveLock
3108 * to lock the existing indexes - that should be enough to prevent others
3109 * from changing them while we're repacking the relation. The lock on
3110 * table should prevent others from changing the index column list, but
3111 * might not be enough for commands like ALTER INDEX ... SET ... (Those
3112 * are not necessarily dangerous, but can make user confused if the
3113 * changes they do get lost due to REPACK.)
3114 */
3116
3117 /*
3118 * The identity index in the new relation appears in the same relative
3119 * position as the corresponding index in the old relation. Find it.
3120 */
3123 {
3124 if (identIdx == ind_old)
3125 {
3126 int pos = foreach_current_index(ind_old);
3127
3128 if (list_length(ind_oids_new) <= pos)
3129 elog(ERROR, "list of new indexes too short");
3131 break;
3132 }
3133 }
3135 elog(ERROR, "could not find index matching \"%s\" at the new relation",
3137
3138 /* Gather information to apply concurrent changes. */
3140
3141 /*
3142 * During testing, wait for another backend to perform concurrent data
3143 * changes which we will process below.
3144 */
3145 INJECTION_POINT("repack-concurrently-before-lock", NULL);
3146
3147 /*
3148 * Flush all WAL records inserted so far (possibly except for the last
3149 * incomplete page; see GetInsertRecPtr), to minimize the amount of data
3150 * we need to flush while holding exclusive lock on the source table.
3151 */
3153 end_of_wal = GetFlushRecPtr(NULL);
3154
3155 /*
3156 * Apply concurrent changes first time, to minimize the time we need to
3157 * hold AccessExclusiveLock. (Quite some amount of WAL could have been
3158 * written during the data copying and index creation.)
3159 */
3160 process_concurrent_changes(end_of_wal, &chgcxt, false);
3161
3162 /*
3163 * Acquire AccessExclusiveLock on the table, its TOAST relation (if there
3164 * is one), all its indexes, so that we can swap the files.
3165 */
3167
3168 /*
3169 * Lock all indexes now, not only the clustering one: all indexes need to
3170 * have their files swapped. While doing that, store their relation
3171 * references in a zero-terminated array, to handle predicate locks below.
3172 */
3173 indexrels = NIL;
3175 {
3177
3179
3180 /*
3181 * Some things about the index may have changed before we locked the
3182 * index, such as ALTER INDEX RENAME. We don't need to do anything
3183 * here to absorb those changes in the new index.
3184 */
3186 }
3187
3188 /*
3189 * Lock the OldHeap's TOAST relation exclusively - again, the lock is
3190 * needed to swap the files.
3191 */
3192 if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
3193 LockRelationOid(OldHeap->rd_rel->reltoastrelid, AccessExclusiveLock);
3194
3195 /*
3196 * Tuples and pages of the old heap will be gone, but the heap will stay.
3197 */
3200 {
3203 }
3205
3206 /*
3207 * Flush WAL again, to make sure that all changes committed while we were
3208 * waiting for the exclusive lock are available for decoding.
3209 */
3211 end_of_wal = GetFlushRecPtr(NULL);
3212
3213 /*
3214 * Apply the concurrent changes again. Indicate that the decoding worker
3215 * won't be needed anymore.
3216 */
3217 process_concurrent_changes(end_of_wal, &chgcxt, true);
3218
3219 /* Remember info about rel before closing OldHeap */
3220 relpersistence = OldHeap->rd_rel->relpersistence;
3222
3225
3226 /*
3227 * Even ShareUpdateExclusiveLock should have prevented others from
3228 * creating / dropping indexes (even using the CONCURRENTLY option), so we
3229 * do not need to check whether the lists match.
3230 */
3232 {
3235 Oid mapped_tables[4] = {0};
3236
3239 false, /* swap_toast_by_content */
3240 true,
3244
3245#ifdef USE_ASSERT_CHECKING
3246
3247 /*
3248 * Concurrent processing is not supported for system relations, so
3249 * there should be no mapped tables.
3250 */
3251 for (int i = 0; i < 4; i++)
3253#endif
3254 }
3255
3256 /* The new indexes must be visible for deletion. */
3258
3259 /* Close the old heap but keep lock until transaction commit. */
3261 /* Close the new heap. (We didn't have to open its indexes). */
3263
3264 /* Cleanup what we don't need anymore. (And close the identity index.) */
3266
3267 /*
3268 * Swap the relations and their TOAST relations and TOAST indexes. This
3269 * also drops the new relation and its indexes.
3270 *
3271 * (System catalogs are currently not supported.)
3272 */
3276 false, /* swap_toast_by_content */
3277 false,
3278 true,
3279 false, /* reindex */
3281 relpersistence);
3282}
#define INJECTION_POINT(name, arg)
void list_free(List *list)
Definition list.c:1546
#define InvalidMultiXactId
Definition multixact.h:25
static int list_length(const List *l)
Definition pg_list.h:152
#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
static void release_change_context(ChangeContext *chgcxt)
Definition repack.c:3061
static void initialize_change_context(ChangeContext *chgcxt, Relation relation, Oid ident_index_id)
Definition repack.c:2969
static void process_concurrent_changes(XLogRecPtr end_of_wal, ChangeContext *chgcxt, bool done)
Definition repack.c:2913
static List * build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
Definition repack.c:3295
#define InvalidTransactionId
Definition transam.h:31
XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI)
Definition xlog.c:6997
XLogRecPtr GetXLogInsertEndRecPtr(void)
Definition xlog.c:10108
void XLogFlush(XLogRecPtr record)
Definition xlog.c:2801
uint64 XLogRecPtr
Definition xlogdefs.h:21

References AccessExclusiveLock, Assert, build_new_indexes(), CheckRelationLockedByMe(), CommandCounterIncrement(), elog, ERROR, fb(), finish_heap_swap(), forboth, foreach_current_index, foreach_oid, foreach_ptr, get_rel_name(), GetFlushRecPtr(), GetXLogInsertEndRecPtr(), i, index_close(), index_open(), initialize_change_context(), INJECTION_POINT, InvalidMultiXactId, InvalidOid, InvalidTransactionId, IsSystemRelation(), lappend(), lfirst_oid, list_free(), list_length(), list_nth_oid(), LockRelationOid(), NIL, NoLock, OidIsValid, pgstat_progress_update_param(), process_concurrent_changes(), PROGRESS_REPACK_PHASE, PROGRESS_REPACK_PHASE_SWAP_REL_FILES, RelationGetIndexList(), RelationGetRelid, release_change_context(), ShareUpdateExclusiveLock, swap_relation_files(), table_close(), TransferPredicateLocksToHeapRelation(), and XLogFlush().

Referenced by rebuild_relation().

◆ release_change_context()

static void release_change_context ( ChangeContext chgcxt)
static

Definition at line 3061 of file repack.c.

3062{
3063 ExecCloseIndices(chgcxt->cc_rri);
3064 FreeExecutorState(chgcxt->cc_estate);
3065 /* XXX are these pfrees necessary? */
3066 pfree(chgcxt->cc_rri);
3067 pfree(chgcxt->cc_ident_key);
3068}
void ExecCloseIndices(ResultRelInfo *resultRelInfo)
void FreeExecutorState(EState *estate)
Definition execUtils.c:197

References ExecCloseIndices(), fb(), FreeExecutorState(), and pfree().

Referenced by rebuild_relation_finish_concurrent().

◆ repack_is_permitted_for_relation()

static bool repack_is_permitted_for_relation ( RepackCommand  cmd,
Oid  relid,
Oid  userid 
)
static

Definition at line 2319 of file repack.c.

2320{
2322
2323 if (pg_class_aclcheck(relid, userid, ACL_MAINTAIN) == ACLCHECK_OK)
2324 return true;
2325
2327 errmsg("permission denied to execute %s on \"%s\", skipping it",
2329 get_rel_name(relid)));
2330
2331 return false;
2332}
@ ACLCHECK_OK
Definition acl.h:184
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4083
#define WARNING
Definition elog.h:37
#define ACL_MAINTAIN
Definition parsenodes.h:90

References ACL_MAINTAIN, ACLCHECK_OK, Assert, ereport, errmsg, get_rel_name(), pg_class_aclcheck(), REPACK_COMMAND_CLUSTER, REPACK_COMMAND_REPACK, RepackCommandAsString(), and WARNING.

Referenced by cluster_rel_recheck(), get_tables_to_repack(), and get_tables_to_repack_partitioned().

◆ RepackCommandAsString()

static const char * RepackCommandAsString ( RepackCommand  cmd)
static

Definition at line 2481 of file repack.c.

2482{
2483 switch (cmd)
2484 {
2486 return "REPACK";
2488 return "VACUUM";
2490 return "CLUSTER";
2491 }
2492 return "???"; /* keep compiler quiet */
2493}
@ REPACK_COMMAND_VACUUMFULL

References REPACK_COMMAND_CLUSTER, REPACK_COMMAND_REPACK, and REPACK_COMMAND_VACUUMFULL.

Referenced by cluster_rel(), ExecRepack(), process_single_relation(), and repack_is_permitted_for_relation().

◆ RepackLockLevel()

static LOCKMODE RepackLockLevel ( bool  concurrent)
static

Definition at line 485 of file repack.c.

486{
487 if (concurrent)
489 else
490 return AccessExclusiveLock;
491}

References AccessExclusiveLock, and ShareUpdateExclusiveLock.

Referenced by cluster_rel(), copy_table_data(), ExecRepack(), and rebuild_relation().

◆ restore_tuple()

static void restore_tuple ( BufFile file,
Relation  relation,
TupleTableSlot slot 
)
static

Definition at line 2718 of file repack.c.

2719{
2720 uint32 t_len;
2721 HeapTuple tup;
2722 int natt_ext;
2723
2724 /* Read the tuple. */
2725 BufFileReadExact(file, &t_len, sizeof(t_len));
2726 tup = (HeapTuple) palloc(HEAPTUPLESIZE + t_len);
2727 tup->t_data = (HeapTupleHeader) ((char *) tup + HEAPTUPLESIZE);
2728 BufFileReadExact(file, tup->t_data, t_len);
2729 tup->t_len = t_len;
2730 ItemPointerSetInvalid(&tup->t_self);
2731 tup->t_tableOid = RelationGetRelid(relation);
2732
2733 /*
2734 * Put the tuple we read in a slot. This deforms it, so that we can hack
2735 * the external attributes in place.
2736 */
2737 ExecForceStoreHeapTuple(tup, slot, false);
2738
2739 /*
2740 * Next, read any attributes we stored separately into the tts_values
2741 * array elements expecting them, if any. This matches
2742 * repack_store_change.
2743 */
2744 BufFileReadExact(file, &natt_ext, sizeof(natt_ext));
2745 if (natt_ext > 0)
2746 {
2747 TupleDesc desc = slot->tts_tupleDescriptor;
2748
2749 for (int i = 0; i < desc->natts; i++)
2750 {
2752 varlena *varlen;
2754 void *value;
2755 Size varlensz;
2756
2757 if (attr->attisdropped || attr->attlen != -1)
2758 continue;
2759 if (slot_attisnull(slot, i + 1))
2760 continue;
2763 continue;
2764 slot_getsomeattrs(slot, i + 1);
2765
2768
2771 BufFileReadExact(file, (char *) value + VARHDRSZ, varlensz - VARHDRSZ);
2772
2774 natt_ext--;
2775 if (natt_ext < 0)
2776 ereport(ERROR,
2778 errmsg("insufficient number of attributes stored separately"));
2779 }
2780 }
2781}
#define VARHDRSZ
Definition c.h:781
uint64_t uint64
Definition c.h:625
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
void ExecForceStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
#define HEAPTUPLESIZE
Definition htup.h:73
HeapTupleData * HeapTuple
Definition htup.h:71
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
static struct @177 value
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
#define ERRCODE_DATA_CORRUPTED
#define PointerGetDatum(X)
Definition postgres.h:354
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
static Size VARSIZE_ANY(const void *PTR)
Definition varatt.h:460
static bool VARATT_IS_EXTERNAL_INDIRECT(const void *PTR)
Definition varatt.h:368

References CompactAttribute::attisdropped, CompactAttribute::attlen, BufFileReadExact(), DatumGetPointer(), ereport, errcode(), ERRCODE_DATA_CORRUPTED, errmsg, ERROR, ExecForceStoreHeapTuple(), fb(), HEAPTUPLESIZE, i, ItemPointerSetInvalid(), memcpy(), TupleDescData::natts, palloc(), PointerGetDatum, RelationGetRelid, slot_attisnull(), slot_getsomeattrs(), TupleTableSlot::tts_tupleDescriptor, TupleTableSlot::tts_values, TupleDescCompactAttr(), value, VARATT_IS_EXTERNAL_INDIRECT(), VARHDRSZ, and VARSIZE_ANY().

Referenced by apply_concurrent_changes().

◆ start_repack_decoding_worker()

static void start_repack_decoding_worker ( Oid  relid)
static

Definition at line 3407 of file repack.c.

3408{
3409 Size size;
3410 dsm_segment *seg;
3411 DecodingWorkerShared *shared;
3412 shm_mq *mq;
3415
3416 /* Setup shared memory. */
3417 size = BUFFERALIGN(offsetof(DecodingWorkerShared, error_queue)) +
3419 seg = dsm_create(size, 0);
3420 shared = (DecodingWorkerShared *) dsm_segment_address(seg);
3421 shared->initialized = false;
3422 shared->lsn_upto = InvalidXLogRecPtr;
3423 shared->done = false;
3424 SharedFileSetInit(&shared->sfs, seg);
3425 shared->last_exported = -1;
3426 SpinLockInit(&shared->mutex);
3427 shared->dbid = MyDatabaseId;
3428
3429 /*
3430 * This is the UserId set in cluster_rel(). Security context shouldn't be
3431 * needed for decoding worker.
3432 */
3433 shared->roleid = GetUserId();
3434 shared->relid = relid;
3435 ConditionVariableInit(&shared->cv);
3436 shared->backend_proc = MyProc;
3437 shared->backend_pid = MyProcPid;
3439
3440 mq = shm_mq_create((char *) BUFFERALIGN(shared->error_queue),
3443 mqh = shm_mq_attach(mq, seg, NULL);
3444
3445 memset(&bgw, 0, sizeof(bgw));
3446 snprintf(bgw.bgw_name, BGW_MAXLEN,
3447 "REPACK decoding worker for relation \"%s\"",
3448 get_rel_name(relid));
3449 snprintf(bgw.bgw_type, BGW_MAXLEN, "REPACK decoding worker");
3450 bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
3452 bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
3453 bgw.bgw_restart_time = BGW_NEVER_RESTART;
3454 snprintf(bgw.bgw_library_name, MAXPGPATH, "postgres");
3455 snprintf(bgw.bgw_function_name, BGW_MAXLEN, "RepackWorkerMain");
3456 bgw.bgw_main_arg = UInt32GetDatum(dsm_segment_handle(seg));
3457 bgw.bgw_notify_pid = MyProcPid;
3458
3461 ereport(ERROR,
3463 errmsg("out of background worker slots"),
3464 errhint("You might need to increase \"%s\".", "max_worker_processes"));
3465
3466 decoding_worker->seg = seg;
3468
3469 /*
3470 * The decoding setup must be done before the caller can have XID assigned
3471 * for any reason, otherwise the worker might end up in a deadlock,
3472 * waiting for the caller's transaction to end. Therefore wait here until
3473 * the worker indicates that it has the logical decoding initialized.
3474 */
3476 for (;;)
3477 {
3478 bool initialized;
3479
3480 SpinLockAcquire(&shared->mutex);
3481 initialized = shared->initialized;
3482 SpinLockRelease(&shared->mutex);
3483
3484 if (initialized)
3485 break;
3486
3488 }
3490}
bool RegisterDynamicBackgroundWorker(BackgroundWorker *worker, BackgroundWorkerHandle **handle)
Definition bgworker.c:1068
#define BGW_NEVER_RESTART
Definition bgworker.h:92
@ 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
#define BUFFERALIGN(LEN)
Definition c.h:898
void ConditionVariableInit(ConditionVariable *cv)
dsm_handle dsm_segment_handle(dsm_segment *seg)
Definition dsm.c:1131
dsm_segment * dsm_create(Size size, int flags)
Definition dsm.c:524
#define palloc0_object(type)
Definition fe_memutils.h:90
int MyProcPid
Definition globals.c:49
ProcNumber MyProcNumber
Definition globals.c:92
Oid MyDatabaseId
Definition globals.c:96
static Datum UInt32GetDatum(uint32 X)
Definition postgres.h:232
#define REPACK_ERROR_QUEUE_SIZE
void SharedFileSetInit(SharedFileSet *fileset, dsm_segment *seg)
shm_mq * shm_mq_create(void *address, Size size)
Definition shm_mq.c:179
void shm_mq_set_receiver(shm_mq *mq, PGPROC *proc)
Definition shm_mq.c:208
shm_mq_handle * shm_mq_attach(shm_mq *mq, dsm_segment *seg, BackgroundWorkerHandle *handle)
Definition shm_mq.c:292
static void SpinLockInit(volatile slock_t *lock)
Definition spin.h:50
PGPROC * MyProc
Definition proc.c:71
char error_queue[FLEXIBLE_ARRAY_MEMBER]
BackgroundWorkerHandle * handle
Definition repack.c:134
static bool initialized
Definition win32ntdll.c:36
#define InvalidXLogRecPtr
Definition xlogdefs.h:28

References DecodingWorkerShared::backend_pid, DecodingWorkerShared::backend_proc, DecodingWorkerShared::backend_proc_number, BGW_MAXLEN, BGW_NEVER_RESTART, BGWORKER_BACKEND_DATABASE_CONNECTION, BGWORKER_SHMEM_ACCESS, BgWorkerStart_RecoveryFinished, BUFFERALIGN, ConditionVariableCancelSleep(), ConditionVariableInit(), ConditionVariablePrepareToSleep(), ConditionVariableSleep(), DecodingWorkerShared::cv, DecodingWorkerShared::dbid, decoding_worker, DecodingWorkerShared::done, dsm_create(), dsm_segment_address(), dsm_segment_handle(), ereport, errcode(), errhint(), errmsg, ERROR, DecodingWorker::error_mqh, DecodingWorkerShared::error_queue, fb(), get_rel_name(), GetUserId(), DecodingWorker::handle, DecodingWorkerShared::initialized, initialized, InvalidXLogRecPtr, DecodingWorkerShared::last_exported, DecodingWorkerShared::lsn_upto, MAXPGPATH, DecodingWorkerShared::mutex, MyDatabaseId, MyProc, MyProcNumber, MyProcPid, palloc0_object, RegisterDynamicBackgroundWorker(), DecodingWorkerShared::relid, REPACK_ERROR_QUEUE_SIZE, DecodingWorkerShared::roleid, DecodingWorker::seg, DecodingWorkerShared::sfs, SharedFileSetInit(), shm_mq_attach(), shm_mq_create(), shm_mq_set_receiver(), snprintf, SpinLockAcquire(), SpinLockInit(), SpinLockRelease(), and UInt32GetDatum().

Referenced by rebuild_relation().

◆ stop_repack_decoding_worker()

static void stop_repack_decoding_worker ( void  )
static

Definition at line 3499 of file repack.c.

3500{
3501 BgwHandleStatus status;
3502
3503 /* Haven't reached the worker startup? */
3504 if (decoding_worker == NULL)
3505 return;
3506
3507 /* Could not register the worker? */
3508 if (decoding_worker->handle == NULL)
3509 return;
3510
3512 /* The worker should really exit before the REPACK command does. */
3516
3517 if (status == BGWH_POSTMASTER_DIED)
3518 ereport(FATAL,
3520 errmsg("postmaster exited during REPACK command"));
3521
3523
3524 /*
3525 * If we could not cancel the current sleep due to ERROR, do that before
3526 * we detach from the shared memory the condition variable is located in.
3527 * If we did not, the bgworker ERROR handling code would try and fail
3528 * badly.
3529 */
3531
3535}
void TerminateBackgroundWorker(BackgroundWorkerHandle *handle)
Definition bgworker.c:1319
BgwHandleStatus WaitForBackgroundWorkerShutdown(BackgroundWorkerHandle *handle)
Definition bgworker.c:1280
BgwHandleStatus
Definition bgworker.h:111
@ BGWH_POSTMASTER_DIED
Definition bgworker.h:115
void dsm_detach(dsm_segment *seg)
Definition dsm.c:811
#define FATAL
Definition elog.h:42
void shm_mq_detach(shm_mq_handle *mqh)
Definition shm_mq.c:845

References BGWH_POSTMASTER_DIED, ConditionVariableCancelSleep(), decoding_worker, dsm_detach(), ereport, errcode(), errmsg, DecodingWorker::error_mqh, FATAL, fb(), DecodingWorker::handle, HOLD_INTERRUPTS, pfree(), RESUME_INTERRUPTS, DecodingWorker::seg, shm_mq_detach(), TerminateBackgroundWorker(), and WaitForBackgroundWorkerShutdown().

Referenced by cluster_rel().

◆ swap_relation_files()

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 
)
static

Definition at line 1500 of file repack.c.

1506{
1509 reltup2;
1511 relform2;
1515 char swptmpchr;
1516 Oid relam1,
1517 relam2;
1518
1519 /* We need writable copies of both pg_class tuples. */
1521
1524 elog(ERROR, "cache lookup failed for relation %u", r1);
1526
1529 elog(ERROR, "cache lookup failed for relation %u", r2);
1531
1532 relfilenumber1 = relform1->relfilenode;
1533 relfilenumber2 = relform2->relfilenode;
1534 relam1 = relform1->relam;
1535 relam2 = relform2->relam;
1536
1539 {
1540 /*
1541 * Normal non-mapped relations: swap relfilenumbers, reltablespaces,
1542 * relpersistence
1543 */
1545
1546 swaptemp = relform1->relfilenode;
1547 relform1->relfilenode = relform2->relfilenode;
1548 relform2->relfilenode = swaptemp;
1549
1550 swaptemp = relform1->reltablespace;
1551 relform1->reltablespace = relform2->reltablespace;
1552 relform2->reltablespace = swaptemp;
1553
1554 swaptemp = relform1->relam;
1555 relform1->relam = relform2->relam;
1556 relform2->relam = swaptemp;
1557
1558 swptmpchr = relform1->relpersistence;
1559 relform1->relpersistence = relform2->relpersistence;
1560 relform2->relpersistence = swptmpchr;
1561
1562 /* Also swap toast links, if we're swapping by links */
1564 {
1565 swaptemp = relform1->reltoastrelid;
1566 relform1->reltoastrelid = relform2->reltoastrelid;
1567 relform2->reltoastrelid = swaptemp;
1568 }
1569 }
1570 else
1571 {
1572 /*
1573 * Mapped-relation case. Here we have to swap the relation mappings
1574 * instead of modifying the pg_class columns. Both must be mapped.
1575 */
1578 elog(ERROR, "cannot swap mapped relation \"%s\" with non-mapped relation",
1579 NameStr(relform1->relname));
1580
1581 /*
1582 * We can't change the tablespace nor persistence of a mapped rel, and
1583 * we can't handle toast link swapping for one either, because we must
1584 * not apply any critical changes to its pg_class row. These cases
1585 * should be prevented by upstream permissions tests, so these checks
1586 * are non-user-facing emergency backstop.
1587 */
1588 if (relform1->reltablespace != relform2->reltablespace)
1589 elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
1590 NameStr(relform1->relname));
1591 if (relform1->relpersistence != relform2->relpersistence)
1592 elog(ERROR, "cannot change persistence of mapped relation \"%s\"",
1593 NameStr(relform1->relname));
1594 if (relform1->relam != relform2->relam)
1595 elog(ERROR, "cannot change access method of mapped relation \"%s\"",
1596 NameStr(relform1->relname));
1597 if (!swap_toast_by_content &&
1598 (relform1->reltoastrelid || relform2->reltoastrelid))
1599 elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
1600 NameStr(relform1->relname));
1601
1602 /*
1603 * Fetch the mappings --- shouldn't fail, but be paranoid
1604 */
1607 elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1608 NameStr(relform1->relname), r1);
1611 elog(ERROR, "could not find relation mapping for relation \"%s\", OID %u",
1612 NameStr(relform2->relname), r2);
1613
1614 /*
1615 * Send replacement mappings to relmapper. Note these won't actually
1616 * take effect until CommandCounterIncrement.
1617 */
1618 RelationMapUpdateMap(r1, relfilenumber2, relform1->relisshared, false);
1619 RelationMapUpdateMap(r2, relfilenumber1, relform2->relisshared, false);
1620
1621 /* Pass OIDs of mapped r2 tables back to caller */
1622 *mapped_tables++ = r2;
1623 }
1624
1625 /*
1626 * Recognize that rel1's relfilenumber (swapped from rel2) is new in this
1627 * subtransaction. The rel2 storage (swapped from rel1) may or may not be
1628 * new.
1629 */
1630 {
1631 Relation rel1,
1632 rel2;
1633
1636 rel2->rd_createSubid = rel1->rd_createSubid;
1637 rel2->rd_newRelfilelocatorSubid = rel1->rd_newRelfilelocatorSubid;
1638 rel2->rd_firstRelfilelocatorSubid = rel1->rd_firstRelfilelocatorSubid;
1642 }
1643
1644 /*
1645 * In the case of a shared catalog, these next few steps will only affect
1646 * our own database's pg_class row; but that's okay, because they are all
1647 * noncritical updates. That's also an important fact for the case of a
1648 * mapped catalog, because it's possible that we'll commit the map change
1649 * and then fail to commit the pg_class update.
1650 */
1651
1652 /* set rel1's frozen Xid and minimum MultiXid */
1653 if (relform1->relkind != RELKIND_INDEX)
1654 {
1657 relform1->relfrozenxid = frozenXid;
1658 relform1->relminmxid = cutoffMulti;
1659 }
1660
1661 /* swap size statistics too, since new rel has freshly-updated stats */
1662 {
1667
1668 swap_pages = relform1->relpages;
1669 relform1->relpages = relform2->relpages;
1670 relform2->relpages = swap_pages;
1671
1672 swap_tuples = relform1->reltuples;
1673 relform1->reltuples = relform2->reltuples;
1674 relform2->reltuples = swap_tuples;
1675
1676 swap_allvisible = relform1->relallvisible;
1677 relform1->relallvisible = relform2->relallvisible;
1678 relform2->relallvisible = swap_allvisible;
1679
1680 swap_allfrozen = relform1->relallfrozen;
1681 relform1->relallfrozen = relform2->relallfrozen;
1682 relform2->relallfrozen = swap_allfrozen;
1683 }
1684
1685 /*
1686 * Update the tuples in pg_class --- unless the target relation of the
1687 * swap is pg_class itself. In that case, there is zero point in making
1688 * changes because we'd be updating the old data that we're about to throw
1689 * away. Because the real work being done here for a mapped relation is
1690 * just to change the relation map settings, it's all right to not update
1691 * the pg_class rows in this case. The most important changes will instead
1692 * performed later, in finish_heap_swap() itself.
1693 */
1694 if (!target_is_pg_class)
1695 {
1697
1700 indstate);
1702 indstate);
1704 }
1705 else
1706 {
1707 /* no update ... but we do still need relcache inval */
1710 }
1711
1712 /*
1713 * Now that pg_class has been updated with its relevant information for
1714 * the swap, update the dependency of the relations to point to their new
1715 * table AM, if it has changed.
1716 */
1717 if (relam1 != relam2)
1718 {
1720 r1,
1722 relam1,
1723 relam2) != 1)
1724 elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
1726 get_rel_name(r1));
1728 r2,
1730 relam2,
1731 relam1) != 1)
1732 elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
1734 get_rel_name(r2));
1735 }
1736
1737 /*
1738 * Post alter hook for modified relations. The change to r2 is always
1739 * internal, but r1 depends on the invocation context.
1740 */
1742 InvalidOid, is_internal);
1744 InvalidOid, true);
1745
1746 /*
1747 * If we have toast tables associated with the relations being swapped,
1748 * deal with them too.
1749 */
1750 if (relform1->reltoastrelid || relform2->reltoastrelid)
1751 {
1753 {
1754 if (relform1->reltoastrelid && relform2->reltoastrelid)
1755 {
1756 /* Recursively swap the contents of the toast tables */
1757 swap_relation_files(relform1->reltoastrelid,
1758 relform2->reltoastrelid,
1761 is_internal,
1762 frozenXid,
1765 }
1766 else
1767 {
1768 /* caller messed up */
1769 elog(ERROR, "cannot swap toast files by content when there's only one");
1770 }
1771 }
1772 else
1773 {
1774 /*
1775 * We swapped the ownership links, so we need to change dependency
1776 * data to match.
1777 *
1778 * NOTE: it is possible that only one table has a toast table.
1779 *
1780 * NOTE: at present, a TOAST table's only dependency is the one on
1781 * its owning table. If more are ever created, we'd need to use
1782 * something more selective than deleteDependencyRecordsFor() to
1783 * get rid of just the link we want.
1784 */
1787 long count;
1788
1789 /*
1790 * We disallow this case for system catalogs, to avoid the
1791 * possibility that the catalog we're rebuilding is one of the
1792 * ones the dependency changes would change. It's too late to be
1793 * making any data changes to the target catalog.
1794 */
1796 elog(ERROR, "cannot swap toast files by links for system catalogs");
1797
1798 /* Delete old dependencies */
1799 if (relform1->reltoastrelid)
1800 {
1802 relform1->reltoastrelid,
1803 false);
1804 if (count != 1)
1805 elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1806 count);
1807 }
1808 if (relform2->reltoastrelid)
1809 {
1811 relform2->reltoastrelid,
1812 false);
1813 if (count != 1)
1814 elog(ERROR, "expected one dependency record for TOAST table, found %ld",
1815 count);
1816 }
1817
1818 /* Register new dependencies */
1820 baseobject.objectSubId = 0;
1822 toastobject.objectSubId = 0;
1823
1824 if (relform1->reltoastrelid)
1825 {
1826 baseobject.objectId = r1;
1827 toastobject.objectId = relform1->reltoastrelid;
1830 }
1831
1832 if (relform2->reltoastrelid)
1833 {
1834 baseobject.objectId = r2;
1835 toastobject.objectId = relform2->reltoastrelid;
1838 }
1839 }
1840 }
1841
1842 /*
1843 * If we're swapping two toast tables by content, do the same for their
1844 * valid index. The swap can actually be safely done only if the relations
1845 * have indexes.
1846 */
1848 relform1->relkind == RELKIND_TOASTVALUE &&
1849 relform2->relkind == RELKIND_TOASTVALUE)
1850 {
1853
1854 /* Get valid index for each relation */
1859
1864 is_internal,
1868 }
1869
1870 /* Clean up. */
1873
1875}
#define NameStr(name)
Definition c.h:835
int32_t int32
Definition c.h:620
float float4
Definition c.h:713
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition catalog.c:86
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
void CatalogTupleUpdateWithInfo(Relation heapRel, const ItemPointerData *otid, HeapTuple tup, CatalogIndexState indstate)
Definition indexing.c:337
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition indexing.c:61
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition indexing.c:43
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:459
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:303
void RelationAssumeNewRelfilelocator(Relation relation)
Definition relcache.c:3978
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
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
#define TransactionIdIsNormal(xid)
Definition transam.h:42

References AccessExclusiveLock, Assert, CacheInvalidateRelcacheByTuple(), CatalogCloseIndexes(), CatalogOpenIndexes(), CatalogTupleUpdateWithInfo(), changeDependencyFor(), deleteDependencyRecordsFor(), DEPENDENCY_INTERNAL, elog, ERROR, fb(), get_namespace_name(), get_rel_name(), get_rel_namespace(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidMultiXactId, InvalidOid, InvalidTransactionId, InvokeObjectPostAlterHookArg, IsSystemClass(), NameStr, NoLock, ObjectIdGetDatum(), recordDependencyOn(), relation_close(), relation_open(), RelationAssumeNewRelfilelocator(), RelationMapOidToFilenumber(), RelationMapUpdateMap(), RelFileNumberIsValid, RowExclusiveLock, SearchSysCacheCopy1, swap_relation_files(), table_close(), table_open(), toast_get_valid_index(), TransactionIdIsNormal, and TransactionIdIsValid.

Referenced by finish_heap_swap(), rebuild_relation_finish_concurrent(), and swap_relation_files().

Variable Documentation

◆ decoding_worker

◆ RepackMessagePending

volatile sig_atomic_t RepackMessagePending = false