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 "access/xlog.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_attrdef.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 "parser/parse_relation.h"
#include "pgstat.h"
#include "replication/logicalrelation.h"
#include "storage/bufmgr.h"
#include "storage/ipc.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 (RepackStmt *stmt, Relation rel, 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 void copy_attribute_defaults (Oid old_heap_oid, Oid new_heap_oid)
 
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 void stop_repack_decoding_worker_cb (int code, Datum arg)
 
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 102 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 2838 of file repack.c.

2839{
2840 TupleDesc desc = dest->tts_tupleDescriptor;
2841
2842 for (int i = 0; i < desc->natts; i++)
2843 {
2846
2847 if (attr->attisdropped)
2848 continue;
2849 if (attr->attlen != -1)
2850 continue;
2851 if (slot_attisnull(dest, i + 1))
2852 continue;
2853
2854 slot_getsomeattrs(dest, i + 1);
2855
2856 varlena_dst = (varlena *) DatumGetPointer(dest->tts_values[i]);
2858 continue;
2859 slot_getsomeattrs(src, i + 1);
2860
2861 dest->tts_values[i] = src->tts_values[i];
2862 }
2863}
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:835
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 2542 of file repack.c.

2543{
2544 ConcurrentChangeKind kind = '\0';
2545 Relation rel = chgcxt->cc_rel;
2549 bool have_old_tuple = false;
2551
2553 &TTSOpsVirtual);
2557 &TTSOpsVirtual);
2558
2560
2561 while (true)
2562 {
2563 size_t nread;
2565
2567
2568 nread = BufFileReadMaybeEOF(file, &kind, 1, true);
2569 if (nread == 0) /* done with the file? */
2570 break;
2571
2572 /*
2573 * If this is the old tuple for an update, read it into the tuple slot
2574 * and go to the next one. The update itself will be executed on the
2575 * next iteration, when we receive the NEW tuple.
2576 */
2577 if (kind == CHANGE_UPDATE_OLD)
2578 {
2579 restore_tuple(file, rel, old_update_tuple);
2580 have_old_tuple = true;
2581 continue;
2582 }
2583
2584 /*
2585 * Just before an UPDATE or DELETE, we must update the command
2586 * counter, because the change could refer to a tuple that we have
2587 * just inserted; and before an INSERT, we have to do this also if the
2588 * previous command was either update or delete.
2589 *
2590 * With this approach we don't spend so many CCIs for long strings of
2591 * only INSERTs, which can't affect one another.
2592 */
2593 if (kind == CHANGE_UPDATE_NEW || kind == CHANGE_DELETE ||
2594 (kind == CHANGE_INSERT && (prevkind == CHANGE_UPDATE_NEW ||
2596 {
2599 }
2600
2601 /*
2602 * Now restore the tuple into the slot and execute the change.
2603 */
2604 restore_tuple(file, rel, spilled_tuple);
2605
2606 if (kind == CHANGE_INSERT)
2607 {
2609 }
2610 else if (kind == CHANGE_DELETE)
2611 {
2612 bool found;
2613
2614 /* Find the tuple to be deleted */
2616 if (!found)
2617 elog(ERROR, "could not find target tuple");
2619 }
2620 else if (kind == CHANGE_UPDATE_NEW)
2621 {
2623 bool found;
2624
2625 if (have_old_tuple)
2627 else
2629
2630 /* Find the tuple to be updated or deleted. */
2631 found = find_target_tuple(rel, chgcxt, key, ondisk_tuple);
2632 if (!found)
2633 elog(ERROR, "could not find target tuple");
2634
2635 /*
2636 * If 'tup' contains TOAST pointers, they point to the old
2637 * relation's toast. Copy the corresponding TOAST pointers for the
2638 * new relation from the existing tuple. (The fact that we
2639 * received a TOAST pointer here implies that the attribute hasn't
2640 * changed.)
2641 */
2643
2645
2647 have_old_tuple = false;
2648 }
2649 else
2650 elog(ERROR, "unrecognized kind of change: %d", kind);
2651
2652 ResetPerTupleExprContext(chgcxt->cc_estate);
2653 }
2654
2655 /* Cleanup. */
2659
2661}
size_t BufFileReadMaybeEOF(BufFile *file, void *ptr, size_t size, bool eofOK)
Definition buffile.c:669
#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:674
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
#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:2765
static bool find_target_tuple(Relation rel, ChangeContext *chgcxt, TupleTableSlot *locator, TupleTableSlot *retrieved)
Definition repack.c:2873
static void apply_concurrent_delete(Relation rel, TupleTableSlot *slot)
Definition repack.c:2731
static void apply_concurrent_insert(Relation rel, TupleTableSlot *slot, ChangeContext *chgcxt)
Definition repack.c:2668
static void adjust_toast_pointers(Relation relation, TupleTableSlot *dest, TupleTableSlot *src)
Definition repack.c:2838
static void apply_concurrent_update(Relation rel, TupleTableSlot *spilled_tuple, TupleTableSlot *ondisk_tuple, ChangeContext *chgcxt)
Definition repack.c:2689
#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 2731 of file repack.c.

2732{
2733 TM_Result res;
2734 TM_FailureData tmfd;
2735
2736 /*
2737 * Delete tuple from the new heap, skipping logical decoding for it.
2738 */
2739 res = table_tuple_delete(rel, &(slot->tts_tid),
2740 GetCurrentCommandId(true),
2743 false,
2744 &tmfd);
2745
2746 if (res != TM_Ok)
2747 ereport(ERROR,
2749 errmsg("could not apply concurrent %s on relation \"%s\"",
2750 "DELETE", RelationGetRelationName(rel)));
2751
2753}
void pgstat_progress_incr_param(int index, int64 incr)
int errcode(int sqlerrcode)
Definition elog.c:875
#define ereport(elevel,...)
Definition elog.h:152
static char * errmsg
#define ERRCODE_T_R_SERIALIZATION_FAILURE
Definition pgbench.c:77
#define PROGRESS_REPACK_HEAP_TUPLES_DELETED
Definition progress.h:91
#define RelationGetRelationName(relation)
Definition rel.h:550
#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, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errmsg, ERROR, GetCurrentCommandId(), InvalidSnapshot, pgstat_progress_incr_param(), PROGRESS_REPACK_HEAP_TUPLES_DELETED, RelationGetRelationName, 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 2668 of file repack.c.

2670{
2671 /* Put the tuple in the table, but make sure it won't be decoded */
2672 table_tuple_insert(rel, slot, GetCurrentCommandId(true),
2674
2675 /* Update indexes with this new tuple. */
2677 chgcxt->cc_estate,
2678 0,
2679 slot,
2680 NIL, NULL);
2682}
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 2689 of file repack.c.

2692{
2693 LockTupleMode lockmode;
2694 TM_FailureData tmfd;
2696 TM_Result res;
2697
2698 /*
2699 * Carry out the update, skipping logical decoding for it.
2700 */
2701 res = table_tuple_update(rel, &(ondisk_tuple->tts_tid), spilled_tuple,
2702 GetCurrentCommandId(true),
2706 false,
2707 &tmfd, &lockmode, &update_indexes);
2708 if (res != TM_Ok)
2709 ereport(ERROR,
2711 errmsg("could not apply concurrent %s on relation \"%s\"",
2712 "UPDATE", RelationGetRelationName(rel)));
2713
2714 if (update_indexes != TU_None)
2715 {
2716 uint32 flags = EIIT_IS_UPDATE;
2717
2719 flags |= EIIT_ONLY_SUMMARIZING;
2721 chgcxt->cc_estate,
2722 flags,
2724 NIL, NULL);
2725 }
2726
2728}
uint32_t uint32
Definition c.h:683
#define EIIT_IS_UPDATE
Definition executor.h:755
#define EIIT_ONLY_SUMMARIZING
Definition executor.h:757
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, errcode(), ERRCODE_T_R_SERIALIZATION_FAILURE, errmsg, ERROR, ExecInsertIndexTuples(), fb(), GetCurrentCommandId(), InvalidSnapshot, NIL, pgstat_progress_incr_param(), PROGRESS_REPACK_HEAP_TUPLES_UPDATED, RelationGetRelationName, 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 3397 of file repack.c.

3398{
3399 List *result = NIL;
3400
3403
3405 {
3406 Oid newindex;
3407 char *newName;
3408 Relation ind;
3409
3411
3413 NULL,
3414 "repacknew",
3415 get_rel_namespace(ind->rd_index->indrelid),
3416 false);
3418 oldindex, ind->rd_rel->reltablespace,
3419 newName);
3422
3424 }
3425
3426 return result;
3427}
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:2644
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:2242
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2266
#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:3442
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 858 of file repack.c.

859{
860 char relpersistence,
861 replident;
863
867 errmsg("cannot execute %s in this configuration",
868 "REPACK (CONCURRENTLY)"),
869 errdetail("%s requires \"wal_level\" to be set to \"replica\" or higher.",
870 "REPACK (CONCURRENTLY)"));
871
872 /* Data changes in system relations are not logically decoded. */
873 if (IsCatalogRelation(rel))
876 errmsg("cannot execute %s on relation \"%s\"",
877 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
878 errhint("%s is not supported for catalog relations.",
879 "REPACK (CONCURRENTLY)"));
880
881 /*
882 * reorderbuffer.c does not seem to handle processing of TOAST relation
883 * alone.
884 */
885 if (IsToastRelation(rel))
888 errmsg("cannot execute %s on relation \"%s\"",
889 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
890 errhint("%s is not supported for TOAST relations.",
891 "REPACK (CONCURRENTLY)"));
892
893 relpersistence = rel->rd_rel->relpersistence;
894 if (relpersistence != RELPERSISTENCE_PERMANENT)
897 errmsg("cannot execute %s on relation \"%s\"",
898 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
899 errhint("%s is only allowed for permanent relations.",
900 "REPACK (CONCURRENTLY)"));
901
902 /*
903 * With NOTHING, WAL does not contain the old tuple; FULL is not yet
904 * supported.
905 */
906 replident = rel->rd_rel->relreplident;
907 if (replident == REPLICA_IDENTITY_NOTHING ||
908 replident == REPLICA_IDENTITY_FULL)
911 errmsg("cannot execute %s on relation \"%s\"",
912 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
913 errdetail("%s does not support tables with %s.",
914 "REPACK (CONCURRENTLY)",
915 replident == REPLICA_IDENTITY_NOTHING ?
916 "REPLICA IDENTITY NOTHING" : "REPLICA IDENTITY FULL"));
917
918 /*
919 * Obtain the replica identity index -- either one that has been set
920 * explicitly, or a non-deferrable primary key. If none of these cases
921 * apply, the table cannot be repacked concurrently. It might be possible
922 * to have repack work with a FULL replica identity; however that requires
923 * more work and is not implemented yet.
924 */
926 if (!OidIsValid(ident_idx))
927 {
928 /* This special case warrants its own error message */
929 if (OidIsValid(rel->rd_pkindex) && rel->rd_ispkdeferrable)
932 errmsg("cannot execute %s on relation \"%s\"",
933 "REPACK (CONCURRENTLY)",
935 errdetail("%s does not support deferrable primary keys.",
936 "REPACK (CONCURRENTLY)"),
937 errhint("Use ALTER TABLE ... REPLICA IDENTITY USING INDEX to designate another index as replica identity."));
938
941 errmsg("cannot execute %s on relation \"%s\"",
942 "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
943 errhint("Relation \"%s\" has no identity index.",
945 }
946
948}
#define OidIsValid(objectId)
Definition c.h:917
bool IsToastRelation(Relation relation)
Definition catalog.c:208
bool IsCatalogRelation(Relation relation)
Definition catalog.c:106
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
Oid GetRelationIdentityOrPK(Relation rel)
Definition relation.c:904
bool rd_ispkdeferrable
Definition rel.h:154
Oid rd_pkindex
Definition rel.h:153
Form_pg_class rd_rel
Definition rel.h:111
int wal_level
Definition xlog.c:138
@ WAL_LEVEL_REPLICA
Definition xlog.h:77

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

Referenced by cluster_rel().

◆ check_index_is_clusterable()

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

Definition at line 732 of file repack.c.

733{
735
736 OldIndex = index_open(indexOid, lockmode);
737
738 /*
739 * Check that index is in fact an index on the given relation
740 */
741 if (OldIndex->rd_index == NULL ||
742 OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
745 errmsg("\"%s\" is not an index for table \"%s\"",
748
749 /* Index AM must allow clustering */
750 if (!OldIndex->rd_indam->amclusterable)
753 errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
755
756 /*
757 * Disallow clustering on incomplete indexes (those that might not index
758 * every row of the relation). We could relax this by making a separate
759 * seqscan pass over the table to copy the missing rows, but that seems
760 * expensive and tedious.
761 */
762 if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
765 errmsg("cannot cluster on partial index \"%s\"",
767
768 /*
769 * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
770 * it might well not contain entries for every heap row, or might not even
771 * be internally consistent. (But note that we don't check indcheckxmin;
772 * the worst consequence of following broken HOT chains would be that we
773 * might put recently-dead tuples out-of-order in the new table, and there
774 * is little harm in that.)
775 */
776 if (!OldIndex->rd_index->indisvalid)
779 errmsg("cannot cluster on invalid index \"%s\"",
781
782 /* Drop relcache refcnt on OldIndex, but keep lock */
784}
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(), get_tables_to_repack_partitioned(), and process_single_relation().

◆ cluster_rel()

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

Definition at line 482 of file repack.c.

484{
485 Oid tableOid = RelationGetRelid(OldHeap);
488 Oid save_userid;
489 int save_sec_context;
490 int save_nestlevel;
491 bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
492 bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
493 bool concurrent = ((params->options & CLUOPT_CONCURRENT) != 0);
495
496 /* Determine the lock mode to use. */
497 lmode = RepackLockLevel(concurrent);
498
499 /*
500 * Check some preconditions in the concurrent case. This also obtains the
501 * replica index OID.
502 */
503 if (concurrent)
505
506 /* Check for user-requested abort. */
508
511
512 /*
513 * Switch to the table owner's userid, so that any index functions are run
514 * as that user. Also lock down security-restricted operations and
515 * arrange to make GUC variable changes local to this command.
516 */
517 GetUserIdAndSecContext(&save_userid, &save_sec_context);
518 SetUserIdAndSecContext(OldHeap->rd_rel->relowner,
519 save_sec_context | SECURITY_RESTRICTED_OPERATION);
520 save_nestlevel = NewGUCNestLevel();
522
523 /*
524 * Recheck that the relation is still what it was when we started.
525 *
526 * Note that it's critical to skip this in single-relation CLUSTER;
527 * otherwise, we would reject an attempt to cluster using a
528 * not-previously-clustered index.
529 */
530 if (recheck &&
531 !cluster_rel_recheck(cmd, OldHeap, indexOid, save_userid,
532 lmode, params->options))
533 goto out;
534
535 /*
536 * We allow repacking shared catalogs only when not using an index. It
537 * would work to use an index in most respects, but the index would only
538 * get marked as indisclustered in the current database, leading to
539 * unexpected behavior if CLUSTER were later invoked in another database.
540 */
541 if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
544 /*- translator: first %s is name of a SQL command, eg. REPACK */
545 errmsg("cannot execute %s on a shared catalog",
547
548 /*
549 * The CONCURRENTLY case should have been rejected earlier because it does
550 * not support system catalogs.
551 */
552 Assert(!(OldHeap->rd_rel->relisshared && concurrent));
553
554 /*
555 * Don't process temp tables of other backends ... their local buffer
556 * manager is not going to cope.
557 */
561 /*- translator: first %s is name of a SQL command, eg. REPACK */
562 errmsg("cannot execute %s on temporary tables of other sessions",
564
565 /*
566 * Also check for active uses of the relation in the current transaction,
567 * including open scans and pending AFTER trigger events.
568 */
570
571 /* Check heap and index are valid to cluster on */
572 if (OidIsValid(indexOid))
573 {
574 /* verify the index is good and lock it */
576 /* also open it */
577 index = index_open(indexOid, NoLock);
578 }
579 else
580 index = NULL;
581
582 /*
583 * When allow_system_table_mods is turned off, we disallow repacking a
584 * catalog on a particular index unless that's already the clustered index
585 * for that catalog.
586 *
587 * XXX We don't check for this in CLUSTER, because it's historically been
588 * allowed.
589 */
590 if (cmd != REPACK_COMMAND_CLUSTER &&
591 !allowSystemTableMods && OidIsValid(indexOid) &&
592 IsCatalogRelation(OldHeap) && !index->rd_index->indisclustered)
595 errmsg("permission denied: \"%s\" is a system catalog",
597 errdetail("System catalogs can only be clustered by the index they're already clustered on, if any, unless \"%s\" is enabled.",
598 "allow_system_table_mods"));
599
600 /*
601 * Quietly ignore the request if this is a materialized view which has not
602 * been populated from its query. No harm is done because there is no data
603 * to deal with, and we don't want to throw an error if this is part of a
604 * multi-relation request -- for example, CLUSTER was run on the entire
605 * database.
606 */
607 if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
609 {
610 if (index)
613 goto out;
614 }
615
616 Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
617 OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
618 OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
619
620 /*
621 * All predicate locks on the tuples or pages are about to be made
622 * invalid, because we move tuples around. Promote them to relation
623 * locks. Predicate locks on indexes will be promoted when they are
624 * reindexed.
625 *
626 * During concurrent processing, the heap as well as its indexes stay in
627 * operation, so we postpone this step until they are locked using
628 * AccessExclusiveLock near the end of the processing.
629 */
630 if (!concurrent)
632
633 /*
634 * rebuild_relation does all the dirty work, and closes OldHeap and index,
635 * if valid.
636 *
637 * In concurrent mode, make sure the worker terminates; normally it does
638 * so by itself, but a PG_ENSURE_ERROR_CLEANUP callback ensures that this
639 * happens even in case this backend dies early on a FATAL exit. Normal
640 * mode doesn't need that overhead.
641 */
642 if (concurrent)
643 {
645 {
647 }
650 }
651 else
653
654out:
655 /* Roll back any GUC changes executed by index functions */
656 AtEOXact_GUC(false, save_nestlevel);
657
658 /* Restore userid and security context */
659 SetUserIdAndSecContext(save_userid, save_sec_context);
660
662}
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_REPACK
#define Assert(condition)
Definition c.h:1002
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
#define PG_ENSURE_ERROR_CLEANUP(cleanup_function, arg)
Definition ipc.h:47
#define PG_END_ENSURE_ERROR_CLEANUP(cleanup_function, arg)
Definition ipc.h:52
int LOCKMODE
Definition lockdefs.h:26
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:322
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:669
static void check_concurrent_repack_requirements(Relation rel, Oid *ident_idx_p)
Definition repack.c:858
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:732
static void stop_repack_decoding_worker(void)
Definition repack.c:3691
static LOCKMODE RepackLockLevel(bool concurrent)
Definition repack.c:449
static const char * RepackCommandAsString(RepackCommand cmd)
Definition repack.c:2524
static void stop_repack_decoding_worker_cb(int code, Datum arg)
Definition repack.c:3736
static void rebuild_relation(Relation OldHeap, Relation index, bool verbose, Oid ident_idx)
Definition repack.c:970
#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:4508

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_ENSURE_ERROR_CLEANUP, PG_ENSURE_ERROR_CLEANUP, 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(), stop_repack_decoding_worker_cb(), 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 669 of file repack.c.

671{
672 Oid tableOid = RelationGetRelid(OldHeap);
673
675
676 /* Check that the user still has privileges for the relation */
677 if (!repack_is_permitted_for_relation(cmd, tableOid, userid))
678 {
680 return false;
681 }
682
683 /*
684 * Silently skip a temp table for a remote session. Only doing this check
685 * in the "recheck" case is appropriate (which currently means somebody is
686 * executing a database-wide CLUSTER or on a partitioned table), because
687 * there is another check in cluster() which will stop any attempt to
688 * cluster remote temp tables by name. There is another check in
689 * cluster_rel which is redundant, but we leave it for extra safety.
690 */
692 {
694 return false;
695 }
696
697 if (OidIsValid(indexOid))
698 {
699 /*
700 * Check that the index still exists
701 */
703 {
705 return false;
706 }
707
708 /*
709 * Check that the index is still the one with indisclustered set, if
710 * needed.
711 */
712 if ((options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
713 !get_index_isclustered(indexOid))
714 {
716 return false;
717 }
718 }
719
720 return true;
721}
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:334
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3962
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:2346
#define CLUOPT_RECHECK_ISCLUSTERED
Definition repack.h:27
#define SearchSysCacheExists1(cacheId, key1)
Definition syscache.h:100

References Assert, CheckRelationLockedByMe(), 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_attribute_defaults()

static void copy_attribute_defaults ( Oid  old_heap_oid,
Oid  new_heap_oid 
)
static

Definition at line 3514 of file repack.c.

3515{
3517 Relation rel;
3519 SysScanDesc scan;
3522
3525
3527
3532 scan = systable_beginscan(rel, AttrDefaultIndexId, true,
3533 NULL, 1, &skey);
3535 {
3537 Oid oid;
3540 bool def_replaces[Natts_pg_attrdef] = {0};
3543 bool att_replaces[Natts_pg_attribute] = {0};
3545 att_tup,
3548
3550 Assert(adform->adrelid == old_heap_oid);
3551
3552 /*
3553 * Insert a new tuple that's identical to the existing one, other than
3554 * its OID and the relation it refers to.
3555 */
3559 def_nulls[Anum_pg_attrdef_oid - 1] = false;
3567
3568 /* Set atthasdef for this attribute in the transient table */
3571 ObjectIdGetDatum(adform->adnum));
3573 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
3574 adform->adnum, new_heap_oid);
3582
3583 /* Add a pg_depend record so it's removed with the transient table */
3586 }
3587 systable_endscan(scan);
3588
3591
3593}
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition catalog.c:475
@ DEPENDENCY_AUTO
Definition dependency.h:34
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
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 CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
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_attrdef * Form_pg_attrdef
Definition pg_attrdef.h:53
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:51
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
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 ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:231
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40

References Assert, BoolGetDatum(), BTEqualStrategyNumber, CatalogTupleInsert(), CatalogTupleUpdate(), CommandCounterIncrement(), DEPENDENCY_AUTO, elog, ERROR, fb(), Form_pg_attrdef, GetNewOidWithIndex(), GETSTRUCT(), heap_modify_tuple(), HeapTupleIsValid, ObjectAddressSet, ObjectIdGetDatum(), recordDependencyOn(), RelationGetDescr, ReleaseSysCache(), RowExclusiveLock, ScanKeyInit(), SearchSysCache2(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by rebuild_relation().

◆ copy_index_constraints()

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

Definition at line 3442 of file repack.c.

3443{
3445 Relation rel;
3446 TupleDesc desc;
3447 SysScanDesc scan;
3448 HeapTuple tup;
3450
3453
3454 /*
3455 * Retrieve the constraints supported by the old index and create an
3456 * identical one that points to the new index.
3457 */
3461 ObjectIdGetDatum(old_index->rd_index->indrelid));
3463 NULL, 1, &skey);
3464 desc = RelationGetDescr(rel);
3465 while (HeapTupleIsValid(tup = systable_getnext(scan)))
3466 {
3468 Oid oid;
3470 bool nulls[Natts_pg_constraint] = {0};
3471 bool replaces[Natts_pg_constraint] = {0};
3474
3475 if (conform->conindid != RelationGetRelid(old_index))
3476 continue;
3477
3481 replaces[Anum_pg_constraint_oid - 1] = true;
3486
3487 new_tup = heap_modify_tuple(tup, desc, values, nulls, replaces);
3488
3489 /* Insert it into the catalog. */
3491
3492 /* Create a dependency so it's removed when we drop the new heap. */
3495 }
3496 systable_endscan(scan);
3497
3499
3501}
static Datum values[MAXATTR]
Definition bootstrap.c:190
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint

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 1253 of file repack.c.

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

3803{
3804 /* The PID is already present in the fileset name, so we needn't add it */
3805 snprintf(fname, MAXPGPATH, "%u-%u", relid, seq);
3806}
#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 2481 of file repack.c.

2482{
2483 Oid indexOid;
2484
2485 if (indexname == NULL && usingindex)
2486 {
2487 /*
2488 * If USING INDEX with no name is given, find a clustered index, or
2489 * error out if none.
2490 */
2491 indexOid = InvalidOid;
2493 {
2495 {
2496 indexOid = idxoid;
2497 break;
2498 }
2499 }
2500
2501 if (!OidIsValid(indexOid))
2502 ereport(ERROR,
2504 errmsg("there is no previously clustered index for table \"%s\"",
2506 }
2507 else if (indexname != NULL)
2508 {
2509 /* An index was specified; obtain its OID. */
2510 indexOid = get_relname_relid(indexname, rel->rd_rel->relnamespace);
2511 if (!OidIsValid(indexOid))
2512 ereport(ERROR,
2514 errmsg("index \"%s\" for table \"%s\" does not exist",
2515 indexname, RelationGetRelationName(rel)));
2516 }
2517 else
2518 indexOid = InvalidOid;
2519
2520 return indexOid;
2521}
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2199
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4848

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

Referenced by get_tables_to_repack_partitioned(), and process_single_relation().

◆ ExecRepack()

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

Definition at line 250 of file repack.c.

251{
252 ClusterParams params = {0};
253 Relation rel = NULL;
255 LOCKMODE lockmode;
256 List *rtcs;
257 bool verbose = false;
258 bool analyze = false;
259 bool concurrently = false;
260
261 /* Parse option list */
262 foreach_node(DefElem, opt, stmt->params)
263 {
264 if (strcmp(opt->defname, "verbose") == 0)
265 verbose = defGetBoolean(opt);
266 else if (strcmp(opt->defname, "analyze") == 0 ||
267 strcmp(opt->defname, "analyse") == 0)
268 analyze = defGetBoolean(opt);
269 else if (strcmp(opt->defname, "concurrently") == 0)
270 {
271 if (stmt->command != REPACK_COMMAND_REPACK)
274 errmsg("CONCURRENTLY option not supported for %s",
275 RepackCommandAsString(stmt->command)));
277 }
278 else
281 errmsg("unrecognized %s option \"%s\"",
282 RepackCommandAsString(stmt->command),
283 opt->defname),
284 parser_errposition(pstate, opt->location));
285 }
286
287 params.options |=
288 (verbose ? CLUOPT_VERBOSE : 0) |
289 (analyze ? CLUOPT_ANALYZE : 0) |
291
292 /* Determine the lock mode to use. */
293 lockmode = RepackLockLevel((params.options & CLUOPT_CONCURRENT) != 0);
294
295 if ((params.options & CLUOPT_CONCURRENT) != 0)
296 {
297 /*
298 * Make sure we're not in a transaction block.
299 *
300 * The reason is that repack_setup_logical_decoding() could wait
301 * indefinitely for our XID to complete. (The deadlock detector would
302 * not recognize it because we'd be waiting for ourselves, i.e. no
303 * real lock conflict.) It would be possible to run in a transaction
304 * block if we had no XID, but this restriction is simpler for users
305 * to understand and we don't lose any functionality.
306 */
307 PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)");
308 }
309
310 /*
311 * If a single relation is specified, process it and we're done ... unless
312 * the relation is a partitioned table, in which case we fall through.
313 */
314 if (stmt->relation != NULL)
315 {
316 rel = process_single_relation(stmt, lockmode, isTopLevel, &params);
317 if (rel == NULL)
318 return; /* all done */
319 }
320
321 /*
322 * Don't allow ANALYZE in the multiple-relation case for now. Maybe we
323 * can add support for this later.
324 */
325 if (params.options & CLUOPT_ANALYZE)
328 errmsg("cannot execute %s on multiple tables",
329 "REPACK (ANALYZE)"));
330
331 /*
332 * By here, we know we are in a multi-table situation.
333 *
334 * Concurrent processing is currently considered rather special (e.g. in
335 * terms of resources consumed) so it is not performed in bulk.
336 */
337 if (params.options & CLUOPT_CONCURRENT)
338 {
339 if (rel != NULL)
340 {
341 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
344 errmsg("%s is not supported for partitioned tables",
345 "REPACK (CONCURRENTLY)"),
346 errhint("Consider running the command on individual partitions."));
347 }
348 else
351 errmsg("%s requires an explicit table name",
352 "REPACK (CONCURRENTLY)"));
353 }
354
355 /*
356 * In order to avoid holding locks for too long, we want to process each
357 * table in its own transaction. This forces us to disallow running
358 * inside a user transaction block.
359 */
361
362 /* Also, we need a memory context to hold our list of relations */
364 "Repack",
366
367 /*
368 * Since we open a new transaction for each relation, we have to check
369 * that the relation still is what we think it is.
370 *
371 * In single-transaction CLUSTER, we don't need the overhead.
372 */
373 params.options |= CLUOPT_RECHECK;
374
375 /*
376 * If we don't have a relation yet, determine a relation list. If we do,
377 * then it must be a partitioned table, and we want to process its
378 * partitions. Note that we don't acquire any locks on these tables, so
379 * the returned list must be treated with suspicion.
380 */
381 if (rel == NULL)
382 {
383 Assert(stmt->indexname == NULL);
384 rtcs = get_tables_to_repack(stmt->command, stmt->usingindex,
387 }
388 else
389 {
391 rel = NULL; /* clobber no longer valid pointer */
392 }
393
394 /* Commit to get out of starting transaction */
397
398 /* Cluster the tables, each in a separate transaction */
399 Assert(rel == NULL);
401 {
402 /* Start a new transaction for each relation. */
404
405 /*
406 * Open the target table. It may have been dropped or replaced with
407 * something different, in which case silently skip it.
408 */
409 rel = try_relation_open(rtc->tableOid, lockmode);
410 if (rel == NULL)
411 {
413 continue;
414 }
415 if (rel->rd_rel->relkind != RELKIND_RELATION &&
416 rel->rd_rel->relkind != RELKIND_MATVIEW)
417 {
418 relation_close(rel, lockmode);
420 continue;
421 }
422
423 /* functions in indexes may want a snapshot set */
425
426 /* Process this table */
427 cluster_rel(stmt->command, rel, rtc->indexOid, &params, isTopLevel);
428 /* cluster_rel closes the relation, but keeps lock */
429
432 }
433
434 /* Start a new transaction for the cleanup work. */
436
437 /* Clean up working storage */
439}
bool defGetBoolean(DefElem *def)
Definition define.c:93
#define stmt
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 Relation process_single_relation(RepackStmt *stmt, LOCKMODE lockmode, bool isTopLevel, ClusterParams *params)
Definition repack.c:2390
void cluster_rel(RepackCommand cmd, Relation OldHeap, Oid indexOid, ClusterParams *params, bool isTopLevel)
Definition repack.c:482
static List * get_tables_to_repack(RepackCommand cmd, bool usingindex, MemoryContext permcxt)
Definition repack.c:2102
static List * get_tables_to_repack_partitioned(RepackStmt *stmt, Relation rel, MemoryContext permcxt)
Definition repack.c:2219
#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_relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:89
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition xact.c:3701
void StartTransactionCommand(void)
Definition xact.c:3112
void CommitTransactionCommand(void)
Definition xact.c:3210

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, analyze(), Assert, CLUOPT_ANALYZE, CLUOPT_CONCURRENT, CLUOPT_RECHECK, CLUOPT_RECHECK_ISCLUSTERED, CLUOPT_VERBOSE, cluster_rel(), CommitTransactionCommand(), defGetBoolean(), ereport, errcode(), errhint(), errmsg, ERROR, fb(), foreach_node, foreach_ptr, get_tables_to_repack(), get_tables_to_repack_partitioned(), GetTransactionSnapshot(), MemoryContextDelete(), ClusterParams::options, parser_errposition(), PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), process_single_relation(), PushActiveSnapshot(), RelationData::rd_rel, relation_close(), REPACK_COMMAND_REPACK, RepackCommandAsString(), RepackLockLevel(), StartTransactionCommand(), stmt, try_relation_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 2873 of file repack.c.

2875{
2876 Form_pg_index idx = chgcxt->cc_ident_index->rd_index;
2877 IndexScanDesc scan;
2878 bool retval = false;
2879
2880 /*
2881 * Scan key is passed by caller, so it does not have to be constructed
2882 * multiple times. Key entries have all fields initialized, except for
2883 * sk_argument.
2884 *
2885 * Use the incoming tuple to finalize the scan key.
2886 */
2887 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
2888 {
2889 ScanKey entry = &chgcxt->cc_ident_key[i];
2890 AttrNumber attno = idx->indkey.values[i];
2891
2892 entry->sk_argument = locator->tts_values[attno - 1];
2893 Assert(!locator->tts_isnull[attno - 1]);
2894 }
2895
2896 /* XXX no instrumentation for now */
2897 scan = index_beginscan(rel, chgcxt->cc_ident_index, GetActiveSnapshot(),
2898 NULL, chgcxt->cc_ident_key_nentries, 0, 0);
2899 index_rescan(scan, chgcxt->cc_ident_key, chgcxt->cc_ident_key_nentries, NULL, 0);
2901 {
2902 /* Be wary of temporal constraints */
2903 if (scan->xs_recheck && !identity_key_equal(chgcxt, locator, retrieved))
2904 {
2906 continue;
2907 }
2908
2909 retval = true;
2910 break;
2911 }
2912 index_endscan(scan);
2913
2914 return retval;
2915}
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:2926
@ 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 1881 of file repack.c.

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

3746{
3747 DecodingWorkerShared *shared;
3748 char fname[MAXPGPATH];
3749 BufFile *file;
3751 char *snap_space;
3752 Snapshot snapshot;
3753
3754 shared = (DecodingWorkerShared *) dsm_segment_address(worker->seg);
3755
3756 /*
3757 * The worker needs to initialize the logical decoding, which usually
3758 * takes some time. Therefore it makes sense to prepare for the sleep
3759 * first.
3760 */
3762 for (;;)
3763 {
3764 int last_exported;
3765
3766 SpinLockAcquire(&shared->mutex);
3767 last_exported = shared->last_exported;
3768 SpinLockRelease(&shared->mutex);
3769
3770 /*
3771 * Has the worker exported the file we are waiting for?
3772 */
3773 if (last_exported == WORKER_FILE_SNAPSHOT)
3774 break;
3775
3777 }
3779
3780 /* Read the snapshot from a file. */
3782 file = BufFileOpenFileSet(&shared->sfs.fs, fname, O_RDONLY, false);
3783 BufFileReadExact(file, &snap_size, sizeof(snap_size));
3784 snap_space = (char *) palloc(snap_size);
3786 BufFileClose(file);
3787
3788 /* Restore it. */
3789 snapshot = RestoreSnapshot(snap_space);
3791
3792 return snapshot;
3793}
BufFile * BufFileOpenFileSet(FileSet *fileset, const char *name, int mode, bool missing_ok)
Definition buffile.c:292
void BufFileReadExact(BufFile *file, void *ptr, size_t size)
Definition buffile.c:659
void BufFileClose(BufFile *file)
Definition buffile.c:413
size_t Size
Definition c.h:748
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:3802
#define WORKER_FILE_SNAPSHOT
Definition repack.c:102
Snapshot RestoreSnapshot(char *start_address)
Definition snapmgr.c:1792
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:141

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 2102 of file repack.c.

2103{
2105 TableScanDesc scan;
2106 HeapTuple tuple;
2107 List *rtcs = NIL;
2108
2109 if (usingindex)
2110 {
2111 ScanKeyData entry;
2112
2113 /*
2114 * For USING INDEX, scan pg_index to find those with indisclustered.
2115 *
2116 * Note we don't obtain lock of any kind on the index, which means the
2117 * index or its owning table could be gone or change at any point. We
2118 * have to be extra careful when examining catalog state for them.
2119 */
2121 ScanKeyInit(&entry,
2124 BoolGetDatum(true));
2125 scan = table_beginscan_catalog(catalog, 1, &entry);
2126 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2127 {
2131 Oid relnamespace;
2132 char relpersistence;
2134
2135 index = (Form_pg_index) GETSTRUCT(tuple);
2136
2139 continue;
2140 relnamespace = ((Form_pg_class) GETSTRUCT(classtup))->relnamespace;
2141 relpersistence = ((Form_pg_class) GETSTRUCT(classtup))->relpersistence;
2143
2144 /* Skip temp relations belonging to other sessions */
2145 if (relpersistence == RELPERSISTENCE_TEMP &&
2146 !isTempOrTempToastNamespace(relnamespace))
2147 continue;
2148
2149 /* noisily skip rels which the user can't process */
2150 if (!repack_is_permitted_for_relation(cmd, index->indrelid,
2151 GetUserId()))
2152 continue;
2153
2154 /* Use a permanent memory context for the result list */
2157 rtc->tableOid = index->indrelid;
2158 rtc->indexOid = index->indexrelid;
2159 rtcs = lappend(rtcs, rtc);
2161 }
2162 }
2163 else
2164 {
2167
2168 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
2169 {
2171 Form_pg_class class;
2173
2174 class = (Form_pg_class) GETSTRUCT(tuple);
2175
2176 /* Can only process plain tables and matviews */
2177 if (class->relkind != RELKIND_RELATION &&
2178 class->relkind != RELKIND_MATVIEW)
2179 continue;
2180
2181 /* Skip temp relations belonging to other sessions */
2182 if (class->relpersistence == RELPERSISTENCE_TEMP &&
2183 !isTempOrTempToastNamespace(class->relnamespace))
2184 continue;
2185
2186 /* noisily skip rels which the user can't process */
2188 GetUserId()))
2189 continue;
2190
2191 /* Use a permanent memory context for the result list */
2194 rtc->tableOid = class->oid;
2195 rtc->indexOid = InvalidOid;
2196 rtcs = lappend(rtcs, rtc);
2198 }
2199 }
2200
2201 table_endscan(scan);
2203
2204 return rtcs;
2205}
#define palloc_object(type)
Definition fe_memutils.h:89
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1436
List * lappend(List *list, void *datum)
Definition list.c:339
#define AccessShareLock
Definition lockdefs.h:36
Oid GetUserId(void)
Definition miscinit.c:470
bool isTempOrTempToastNamespace(Oid namespaceId)
Definition namespace.c:3745
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, fb(), Form_pg_index, ForwardScanDirection, GETSTRUCT(), GetUserId(), heap_getnext(), HeapTupleIsValid, InvalidOid, isTempOrTempToastNamespace(), lappend(), MemoryContextSwitchTo(), NIL, ObjectIdGetDatum(), palloc_object, ReleaseSysCache(), repack_is_permitted_for_relation(), ScanKeyInit(), SearchSysCache1(), table_beginscan_catalog(), table_close(), table_endscan(), and table_open().

Referenced by ExecRepack().

◆ get_tables_to_repack_partitioned()

static List * get_tables_to_repack_partitioned ( RepackStmt stmt,
Relation  rel,
MemoryContext  permcxt 
)
static

Definition at line 2219 of file repack.c.

2221{
2222 Oid relid;
2223 bool rel_is_index;
2224 List *inhoids;
2225 List *rtcs = NIL;
2226
2227 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
2229
2230 /*
2231 * We find the list of tables by looking for inheritors. If USING INDEX
2232 * was given, look for inheritors of that index, whose name we resolve
2233 * now.
2234 *
2235 * Otherwise we look for inheritors of the table itself.
2236 */
2237 if (stmt->usingindex)
2238 {
2239 /*
2240 * If no index name was specified when repacking a partitioned table,
2241 * punt for now. Maybe we can improve this later.
2242 */
2243 if (!stmt->indexname)
2244 {
2245 if (stmt->command == REPACK_COMMAND_CLUSTER)
2246 ereport(ERROR,
2248 errmsg("there is no previously clustered index for table \"%s\"",
2250 else
2251 ereport(ERROR,
2253 /*- translator: first %s is name of a SQL command, eg. REPACK */
2254 errmsg("cannot execute %s on partitioned table \"%s\" USING INDEX with no index name",
2255 RepackCommandAsString(stmt->command),
2257 }
2258
2259 relid = determine_clustered_index(rel, stmt->usingindex,
2260 stmt->indexname);
2261 if (!OidIsValid(relid))
2262 elog(ERROR, "unable to determine index to cluster on");
2264
2265 rel_is_index = true;
2266 }
2267 else
2268 {
2269 relid = RelationGetRelid(rel);
2270 rel_is_index = false;
2271 }
2272
2273 /*
2274 * Do not lock the children until they're processed. Note that we do hold
2275 * a lock on the parent partitioned table.
2276 */
2279 {
2280 Oid table_oid,
2281 index_oid;
2284
2285 if (rel_is_index)
2286 {
2287 /* consider only leaf indexes */
2289 continue;
2290
2291 /*
2292 * Although we do have a lock on some ancestor partitioned index,
2293 * we may not have one on the immediate parent, so this lookup may
2294 * still return invalid.
2295 */
2297 if (!OidIsValid(table_oid))
2298 continue;
2300 }
2301 else
2302 {
2303 /* consider only leaf relations */
2305 continue;
2306
2309 }
2310
2311 /*
2312 * It's possible that the user does not have privileges to CLUSTER the
2313 * leaf partition despite having them on the partitioned table. Skip
2314 * if so.
2315 */
2317 GetUserId()))
2318 continue;
2319
2320 /* Use a permanent memory context for the result list */
2323 rtc->tableOid = table_oid;
2324 rtc->indexOid = index_oid;
2325 rtcs = lappend(rtcs, rtc);
2327 }
2328
2329 /* close parent relation, releasing lock on it */
2331
2332 return rtcs;
2333}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3616
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2317
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
static Oid determine_clustered_index(Relation rel, bool usingindex, const char *indexname)
Definition repack.c:2481

References AccessExclusiveLock, Assert, check_index_is_clusterable(), CheckRelationLockedByMe(), determine_clustered_index(), elog, ereport, errcode(), errmsg, ERROR, fb(), find_all_inheritors(), foreach_oid, get_rel_relkind(), GetUserId(), IndexGetRelation(), InvalidOid, lappend(), MemoryContextSwitchTo(), NIL, NoLock, OidIsValid, palloc_object, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, REPACK_COMMAND_CLUSTER, repack_is_permitted_for_relation(), RepackCommandAsString(), stmt, and table_close().

Referenced by ExecRepack().

◆ HandleRepackMessageInterrupt()

void HandleRepackMessageInterrupt ( void  )

Definition at line 3816 of file repack.c.

3817{
3818 InterruptPending = true;
3819 RepackMessagePending = true;
3821}
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:154

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 2926 of file repack.c.

2928{
2929 slot_getsomeattrs(locator, chgcxt->cc_last_key_attno);
2930 slot_getsomeattrs(candidate, chgcxt->cc_last_key_attno);
2931
2932 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
2933 {
2934 ScanKey entry = &chgcxt->cc_ident_key[i];
2935 AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
2936
2937 Assert(attno > 0);
2938
2939 if (locator->tts_isnull[attno - 1] != candidate->tts_isnull[attno - 1])
2940 return false;
2941
2942 if (locator->tts_isnull[attno - 1])
2943 continue;
2944
2946 entry->sk_collation,
2947 candidate->tts_values[attno - 1],
2948 entry->sk_argument)))
2949 return false;
2950 }
2951
2952 return true;
2953}
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 3019 of file repack.c.

3021{
3022 chgcxt->cc_rel = relation;
3023
3024 /* Only initialize fields needed by ExecInsertIndexTuples(). */
3025 chgcxt->cc_estate = CreateExecutorState();
3026
3027 /*
3028 * Set up a range table for the executor, containing our repacked table as
3029 * its only member.
3030 */
3031 {
3033 TupleDesc desc = RelationGetDescr(relation);
3034 List *perminfos = NIL;
3035 Bitmapset *updatedCols = NULL;
3037
3038 /*
3039 * For our use, the RTE only needs to have perminfoindex initialized,
3040 * but there's no reason to not set the fields whose values we have at
3041 * hand.
3042 */
3044 rte->rtekind = RTE_RELATION;
3045 rte->relid = RelationGetRelid(relation);
3046 rte->relkind = RelationGetForm(relation)->relkind;
3047 /* Create the RTEPermissionInfo instance (and set ->perminfoindex). */
3049
3050 /*
3051 * Initialize updatedCols to show that all columns are updated. This
3052 * is of course not necessarily true, and we cannot know this early;
3053 * but this is only used by ExecInsertIndexTuples to flag index
3054 * updates with no logical value changes, so if it's wrong, nothing
3055 * terribly bad happens. We may want to improve this someday though.
3056 *
3057 * Don't claim that dropped columns are changed though.
3058 */
3059 for (int i = 0; i < desc->natts; i++)
3060 {
3062
3063 if (attr->attisdropped)
3064 continue;
3065 updatedCols = bms_add_member(updatedCols,
3067 }
3068
3069 /* install updatedCols in the right place */
3071 perminfo->updatedCols = updatedCols;
3072
3073 /* finally we can initialize the range table proper */
3076 }
3077
3078 /* Set up our ResultRelInfo to use for index updates */
3079 chgcxt->cc_rri = makeNode(ResultRelInfo);
3080 InitResultRelInfo(chgcxt->cc_rri, relation, 1, NULL, 0);
3081 ExecOpenIndices(chgcxt->cc_rri, false);
3082
3083 /*
3084 * The table's relcache entry already has the relcache entry for the
3085 * identity index; find that.
3086 */
3087 chgcxt->cc_ident_index = NULL;
3088 for (int i = 0; i < chgcxt->cc_rri->ri_NumIndices; i++)
3089 {
3091
3092 ind_rel = chgcxt->cc_rri->ri_IndexRelationDescs[i];
3093 if (ind_rel->rd_id == ident_index_id)
3094 {
3095 chgcxt->cc_ident_index = ind_rel;
3096 break;
3097 }
3098 }
3099 if (chgcxt->cc_ident_index == NULL)
3100 elog(ERROR, "could not find identity index");
3101
3102 /* Set up for scanning said identity index */
3103 {
3105
3106 indexForm = chgcxt->cc_ident_index->rd_index;
3107 chgcxt->cc_ident_key_nentries = indexForm->indnkeyatts;
3108 chgcxt->cc_ident_key = (ScanKey) palloc_array(ScanKeyData, indexForm->indnkeyatts);
3109 for (int i = 0; i < indexForm->indnkeyatts; i++)
3110 {
3111 ScanKey entry;
3112 Oid opfamily,
3113 opcintype,
3114 opno,
3115 opcode;
3117
3118 entry = &chgcxt->cc_ident_key[i];
3119
3120 opfamily = chgcxt->cc_ident_index->rd_opfamily[i];
3121 opcintype = chgcxt->cc_ident_index->rd_opcintype[i];
3123 chgcxt->cc_ident_index->rd_rel->relam,
3124 opfamily, false);
3126 elog(ERROR, "could not find equality strategy for index operator family %u for type %u",
3127 opfamily, opcintype);
3128 opno = get_opfamily_member(opfamily, opcintype, opcintype,
3129 eq_strategy);
3130 if (!OidIsValid(opno))
3131 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
3132 eq_strategy, opcintype, opcintype, opfamily);
3133 opcode = get_opcode(opno);
3134 if (!OidIsValid(opcode))
3135 elog(ERROR, "missing oprcode for operator %u", opno);
3136
3137 /* Initialize everything but argument. */
3138 ScanKeyInit(entry,
3139 i + 1,
3140 eq_strategy, opcode,
3141 (Datum) 0);
3142 entry->sk_collation = chgcxt->cc_ident_index->rd_indcollation[i];
3143 }
3144 }
3145
3146 /* Determine the last column we must deform to read the identity */
3147 chgcxt->cc_last_key_attno = InvalidAttrNumber;
3148 for (int i = 0; i < chgcxt->cc_ident_key_nentries; i++)
3149 {
3150 AttrNumber attno = chgcxt->cc_ident_index->rd_index->indkey.values[i];
3151
3152 Assert(attno > 0);
3153 chgcxt->cc_last_key_attno = Max(chgcxt->cc_last_key_attno, attno);
3154 }
3155
3156 chgcxt->cc_file_seq = WORKER_FILE_SNAPSHOT + 1;
3157}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition amapi.c:161
#define InvalidAttrNumber
Definition attnum.h:23
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:217
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
#define Max(x, y)
Definition c.h:1125
@ 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:1308
void ExecInitRangeTable(EState *estate, List *rangeTable, List *permInfos, Bitmapset *unpruned_relids)
Definition execUtils.c:799
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:1585
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:170
#define makeNode(_type_)
Definition nodes.h:159
RTEPermissionInfo * getRTEPermissionInfo(List *rteperminfos, RangeTblEntry *rte)
RTEPermissionInfo * addRTEPermissionInfo(List **rteperminfos, RangeTblEntry *rte)
@ RTE_RELATION
#define list_make1(x1)
Definition pg_list.h:244
#define RelationGetForm(relation)
Definition rel.h:510
ScanKeyData * ScanKey
Definition skey.h:75
uint16 StrategyNumber
Definition stratnum.h:22
#define InvalidStrategy
Definition stratnum.h:24
#define FirstLowInvalidHeapAttributeNumber
Definition sysattr.h:27

References addRTEPermissionInfo(), Assert, CompactAttribute::attisdropped, bms_add_member(), bms_make_singleton(), COMPARE_EQ, CreateExecutorState(), elog, ERROR, ExecInitRangeTable(), ExecOpenIndices(), fb(), FirstLowInvalidHeapAttributeNumber, Form_pg_index, get_opcode(), get_opfamily_member(), getRTEPermissionInfo(), i, IndexAmTranslateCompareType(), InitResultRelInfo(), InvalidAttrNumber, InvalidStrategy, list_make1, makeNode, Max, TupleDescData::natts, NIL, OidIsValid, palloc_array, RelationGetDescr, RelationGetForm, RelationGetRelid, RTE_RELATION, ScanKeyInit(), ScanKeyData::sk_collation, TupleDescCompactAttr(), 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 1124 of file repack.c.

1126{
1130 Oid toastid;
1132 HeapTuple tuple;
1133 Datum reloptions;
1134 bool isNull;
1136
1137 OldHeap = table_open(OIDOldHeap, lockmode);
1139
1140 /*
1141 * Note that the NewHeap will not receive any of the defaults or
1142 * constraints associated with the OldHeap; we don't need 'em, and there's
1143 * no reason to spend cycles inserting them into the catalogs only to
1144 * delete them.
1145 */
1146
1147 /*
1148 * But we do want to use reloptions of the old heap for new heap.
1149 */
1151 if (!HeapTupleIsValid(tuple))
1152 elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1153 reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
1154 &isNull);
1155 if (isNull)
1156 reloptions = (Datum) 0;
1157
1158 if (relpersistence == RELPERSISTENCE_TEMP)
1160 else
1162
1163 /*
1164 * Create the new heap, using a temporary name in the same namespace as
1165 * the existing table. NOTE: there is some risk of collision with user
1166 * relnames. Working around this seems more trouble than it's worth; in
1167 * particular, we can't create the new heap in a different namespace from
1168 * the old, or we will have problems with the TEMP status of temp tables.
1169 *
1170 * Note: the new heap is not a shared relation, even if we are rebuilding
1171 * a shared rel. However, we do make the new heap mapped if the source is
1172 * mapped. This simplifies swap_relation_files, and is absolutely
1173 * necessary for rebuilding pg_class, for reasons explained there.
1174 */
1175 snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
1176
1180 InvalidOid,
1181 InvalidOid,
1182 InvalidOid,
1183 OldHeap->rd_rel->relowner,
1186 NIL,
1188 relpersistence,
1189 false,
1192 reloptions,
1193 false,
1194 true,
1195 true,
1196 OIDOldHeap,
1197 NULL);
1199
1200 ReleaseSysCache(tuple);
1201
1202 /*
1203 * Advance command counter so that the newly-created relation's catalog
1204 * tuples will be visible to table_open.
1205 */
1207
1208 /*
1209 * If necessary, create a TOAST table for the new relation.
1210 *
1211 * If the relation doesn't have a TOAST table already, we can't need one
1212 * for the new relation. The other way around is possible though: if some
1213 * wide columns have been dropped, NewHeapCreateToastTable can decide that
1214 * no TOAST table is needed for the new table.
1215 *
1216 * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
1217 * that the TOAST table will be visible for insertion.
1218 */
1219 toastid = OldHeap->rd_rel->reltoastrelid;
1220 if (OidIsValid(toastid))
1221 {
1222 /* keep the existing toast table's reloptions, if any */
1224 if (!HeapTupleIsValid(tuple))
1225 elog(ERROR, "cache lookup failed for relation %u", toastid);
1226 reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
1227 &isNull);
1228 if (isNull)
1229 reloptions = (Datum) 0;
1230
1231 NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid);
1232
1233 ReleaseSysCache(tuple);
1234 }
1235
1237
1238 return OIDNewHeap;
1239}
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition heap.c:1140
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 792 of file repack.c.

793{
798
799 Assert(rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE);
800
801 /*
802 * If the index is already marked clustered, no need to do anything.
803 */
804 if (OidIsValid(indexOid))
805 {
806 if (get_index_isclustered(indexOid))
807 return;
808 }
809
810 /*
811 * Check each index of the relation and set/clear the bit as needed.
812 */
814
815 foreach(index, RelationGetIndexList(rel))
816 {
818
822 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
824
825 /*
826 * Unset the bit if set. We know it's wrong because we checked this
827 * earlier.
828 */
829 if (indexForm->indisclustered)
830 {
831 indexForm->indisclustered = false;
833 }
834 else if (thisIndexOid == indexOid)
835 {
836 /* this was checked earlier, but let's be real sure */
837 if (!indexForm->indisvalid)
838 elog(ERROR, "cannot cluster on invalid index %u", indexOid);
839 indexForm->indisclustered = true;
841 }
842
844 InvalidOid, is_internal);
845
847 }
848
850}
#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 2963 of file repack.c.

2964{
2965 DecodingWorkerShared *shared;
2966 char fname[MAXPGPATH];
2967 BufFile *file;
2968
2971
2972 /* Ask the worker for the file. */
2974 SpinLockAcquire(&shared->mutex);
2975 shared->lsn_upto = end_of_wal;
2976 shared->done = done;
2977 SpinLockRelease(&shared->mutex);
2978
2979 /*
2980 * The worker needs to finish processing of the current WAL record. Even
2981 * if it's idle, it'll need to close the output file. Thus we're likely to
2982 * wait, so prepare for sleep.
2983 */
2985 for (;;)
2986 {
2987 int last_exported;
2988
2989 SpinLockAcquire(&shared->mutex);
2990 last_exported = shared->last_exported;
2991 SpinLockRelease(&shared->mutex);
2992
2993 /*
2994 * Has the worker exported the file we are waiting for?
2995 */
2996 if (last_exported == chgcxt->cc_file_seq)
2997 break;
2998
3000 }
3002
3003 /* Open the file. */
3004 DecodingWorkerFileName(fname, shared->relid, chgcxt->cc_file_seq);
3005 file = BufFileOpenFileSet(&shared->sfs.fs, fname, O_RDONLY, false);
3007
3008 BufFileClose(file);
3009
3010 /* Get ready for the next file. */
3011 chgcxt->cc_file_seq++;
3012}
#define PROGRESS_REPACK_PHASE_CATCH_UP
Definition progress.h:103
static void apply_concurrent_changes(BufFile *file, ChangeContext *chgcxt)
Definition repack.c:2542
static DecodingWorker * decoding_worker
Definition repack.c:148

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 2390 of file repack.c.

2392{
2393 Relation rel;
2394 Oid tableOid;
2395
2396 Assert(stmt->relation != NULL);
2397 Assert(stmt->command == REPACK_COMMAND_CLUSTER ||
2398 stmt->command == REPACK_COMMAND_REPACK);
2399
2400 /*
2401 * Make sure ANALYZE is specified if a column list is present.
2402 */
2403 if ((params->options & CLUOPT_ANALYZE) == 0 && stmt->relation->va_cols != NIL)
2404 ereport(ERROR,
2406 errmsg("ANALYZE option must be specified when a column list is provided"));
2407
2408 /* Find, lock, and check permissions on the table. */
2409 tableOid = RangeVarGetRelidExtended(stmt->relation->relation,
2410 lockmode,
2411 0,
2413 NULL);
2414 rel = table_open(tableOid, NoLock);
2415
2416 /*
2417 * Reject clustering a remote temp table ... their local buffer manager is
2418 * not going to cope.
2419 */
2420 if (RELATION_IS_OTHER_TEMP(rel))
2421 ereport(ERROR,
2423 /*- translator: first %s is name of a SQL command, eg. REPACK */
2424 errmsg("cannot execute %s on temporary tables of other sessions",
2425 RepackCommandAsString(stmt->command)));
2426
2427 /*
2428 * For partitioned tables, let caller handle this. Otherwise, process it
2429 * here and we're done.
2430 */
2431 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2432 return rel;
2433 else
2434 {
2435 Oid indexOid = InvalidOid;
2436
2437 indexOid = determine_clustered_index(rel, stmt->usingindex,
2438 stmt->indexname);
2439 if (OidIsValid(indexOid))
2440 check_index_is_clusterable(rel, indexOid, lockmode);
2441
2442 cluster_rel(stmt->command, rel, indexOid, params, isTopLevel);
2443
2444 /*
2445 * Do an analyze, if requested. We close the transaction and start a
2446 * new one, so that we don't hold the stronger lock for longer than
2447 * needed.
2448 */
2449 if (params->options & CLUOPT_ANALYZE)
2450 {
2452
2455
2458
2459 vac_params.options |= VACOPT_ANALYZE;
2460 if (params->options & CLUOPT_VERBOSE)
2461 vac_params.options |= VACOPT_VERBOSE;
2462 analyze_rel(tableOid, NULL, &vac_params,
2463 stmt->relation->va_cols, true, NULL);
2466 }
2467
2468 return NULL;
2469 }
2470}
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 3912 of file repack.c.

3913{
3914 char msgtype;
3915
3916 msgtype = pq_getmsgbyte(msg);
3917
3918 switch (msgtype)
3919 {
3922 {
3924
3925 /* Parse ErrorResponse or NoticeResponse. */
3927
3928 /* Death of a worker isn't enough justification for suicide. */
3929 edata.elevel = Min(edata.elevel, ERROR);
3930
3931 /*
3932 * Add a context line to show that this is a message
3933 * propagated from the worker. Otherwise, it can sometimes be
3934 * confusing to understand what actually happened.
3935 */
3936 if (edata.context)
3937 edata.context = psprintf("%s\n%s", edata.context,
3938 _("REPACK decoding worker"));
3939 else
3940 edata.context = pstrdup(_("REPACK decoding worker"));
3941
3942 /* Rethrow error or print notice. */
3944
3945 break;
3946 }
3947
3948 default:
3949 {
3950 elog(ERROR, "unrecognized message type received from decoding worker: %c (message length %d bytes)",
3951 msgtype, msg->len);
3952 }
3953 }
3954}
#define Min(x, y)
Definition c.h:1131
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:228
#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 3827 of file repack.c.

3828{
3829 MemoryContext oldcontext;
3831
3832 /*
3833 * Nothing to do if we haven't launched the worker yet or have already
3834 * terminated it.
3835 */
3836 if (decoding_worker == NULL)
3837 return;
3838
3839 /*
3840 * This is invoked from ProcessInterrupts(), and since some of the
3841 * functions it calls contain CHECK_FOR_INTERRUPTS(), there is a potential
3842 * for recursive calls if more signals are received while this runs. It's
3843 * unclear that recursive entry would be safe, and it doesn't seem useful
3844 * even if it is safe, so let's block interrupts until done.
3845 */
3847
3848 /*
3849 * Moreover, CurrentMemoryContext might be pointing almost anywhere. We
3850 * don't want to risk leaking data into long-lived contexts, so let's do
3851 * our work here in a private context that we can reset on each use.
3852 */
3853 if (hpm_context == NULL) /* first time through? */
3855 "ProcessRepackMessages",
3857 else
3859
3860 oldcontext = MemoryContextSwitchTo(hpm_context);
3861
3862 /* OK to process messages. Reset the flag saying there are more to do. */
3863 RepackMessagePending = false;
3864
3865 /*
3866 * Read as many messages as we can from the worker, but stop when no more
3867 * messages can be read from the worker without blocking.
3868 */
3869 while (true)
3870 {
3871 shm_mq_result res;
3872 Size nbytes;
3873 void *data;
3874
3876 &data, true);
3877 if (res == SHM_MQ_WOULD_BLOCK)
3878 break;
3879 else if (res == SHM_MQ_SUCCESS)
3880 {
3881 StringInfoData msg;
3882
3883 initStringInfo(&msg);
3884 appendBinaryStringInfo(&msg, data, nbytes);
3886 pfree(msg.data);
3887 }
3888 else
3889 {
3890 /*
3891 * The decoding worker is special in that it exits as soon as it
3892 * has its work done. Thus the DETACHED result code is fine.
3893 */
3894 Assert(res == SHM_MQ_DETACHED);
3895
3896 break;
3897 }
3898 }
3899
3900 MemoryContextSwitchTo(oldcontext);
3901
3902 /* Might as well clear the context on our way out */
3904
3906}
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:3912
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:144

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 970 of file repack.c.

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

References AccessExclusiveLock, Assert, BecomeLockGroupLeader(), CheckRelationLockedByMe(), CheckRelationOidLockedByMe(), copy_attribute_defaults(), 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 3181 of file repack.c.

3184{
3189 ListCell *lc,
3190 *lc2;
3191 char relpersistence;
3192 bool is_system_catalog;
3194 XLogRecPtr end_of_wal;
3195 List *indexrels;
3197
3200
3201 /*
3202 * Unlike the exclusive case, we build new indexes for the new relation
3203 * rather than swapping the storage and reindexing the old relation. The
3204 * point is that the index build can take some time, so we do it before we
3205 * get AccessExclusiveLock on the old heap and therefore we cannot swap
3206 * the heap storage yet.
3207 *
3208 * index_create() will lock the new indexes using AccessExclusiveLock - no
3209 * need to change that. At the same time, we use ShareUpdateExclusiveLock
3210 * to lock the existing indexes - that should be enough to prevent others
3211 * from changing them while we're repacking the relation. The lock on
3212 * table should prevent others from changing the index column list, but
3213 * might not be enough for commands like ALTER INDEX ... SET ... (Those
3214 * are not necessarily dangerous, but can make user confused if the
3215 * changes they do get lost due to REPACK.)
3216 */
3218
3219 /*
3220 * The identity index in the new relation appears in the same relative
3221 * position as the corresponding index in the old relation. Find it.
3222 */
3225 {
3226 if (identIdx == ind_old)
3227 {
3228 int pos = foreach_current_index(ind_old);
3229
3230 if (list_length(ind_oids_new) <= pos)
3231 elog(ERROR, "list of new indexes too short");
3233 break;
3234 }
3235 }
3237 elog(ERROR, "could not find index matching \"%s\" at the new relation",
3239
3240 /* Gather information to apply concurrent changes. */
3242
3243 /*
3244 * During testing, wait for another backend to perform concurrent data
3245 * changes which we will process below.
3246 */
3247 INJECTION_POINT("repack-concurrently-before-lock", NULL);
3248
3249 /*
3250 * Flush all WAL records inserted so far (possibly except for the last
3251 * incomplete page; see GetInsertRecPtr), to minimize the amount of data
3252 * we need to flush while holding exclusive lock on the source table.
3253 */
3255 end_of_wal = GetFlushRecPtr(NULL);
3256
3257 /*
3258 * Apply concurrent changes first time, to minimize the time we need to
3259 * hold AccessExclusiveLock. (Quite some amount of WAL could have been
3260 * written during the data copying and index creation.)
3261 */
3262 process_concurrent_changes(end_of_wal, &chgcxt, false);
3263
3264 /*
3265 * Acquire AccessExclusiveLock on the table, its TOAST relation (if there
3266 * is one), all its indexes, so that we can swap the files.
3267 */
3269
3270 /*
3271 * Lock all indexes now, not only the clustering one: all indexes need to
3272 * have their files swapped. While doing that, store their relation
3273 * references in a zero-terminated array, to handle predicate locks below.
3274 */
3275 indexrels = NIL;
3277 {
3279
3281
3282 /*
3283 * Some things about the index may have changed before we locked the
3284 * index, such as ALTER INDEX RENAME. We don't need to do anything
3285 * here to absorb those changes in the new index.
3286 */
3288 }
3289
3290 /*
3291 * Lock the OldHeap's TOAST relation exclusively - again, the lock is
3292 * needed to swap the files.
3293 */
3294 if (OidIsValid(OldHeap->rd_rel->reltoastrelid))
3295 LockRelationOid(OldHeap->rd_rel->reltoastrelid, AccessExclusiveLock);
3296
3297 /*
3298 * Tuples and pages of the old heap will be gone, but the heap will stay.
3299 */
3302 {
3305 }
3307
3308 /*
3309 * Flush WAL again, to make sure that all changes committed while we were
3310 * waiting for the exclusive lock are available for decoding.
3311 */
3313 end_of_wal = GetFlushRecPtr(NULL);
3314
3315 /*
3316 * Apply the concurrent changes again. Indicate that the decoding worker
3317 * won't be needed anymore.
3318 */
3319 process_concurrent_changes(end_of_wal, &chgcxt, true);
3320
3321 /* Remember info about rel before closing OldHeap */
3322 relpersistence = OldHeap->rd_rel->relpersistence;
3324
3327
3328 /*
3329 * Even ShareUpdateExclusiveLock should have prevented others from
3330 * creating / dropping indexes (even using the CONCURRENTLY option), so we
3331 * do not need to check whether the lists match.
3332 */
3334 {
3337 Oid mapped_tables[4] = {0};
3338
3341 false, /* swap_toast_by_content */
3342 true,
3346
3347#ifdef USE_ASSERT_CHECKING
3348
3349 /*
3350 * Concurrent processing is not supported for system relations, so
3351 * there should be no mapped tables.
3352 */
3353 for (int i = 0; i < 4; i++)
3355#endif
3356 }
3357
3358 /* The new indexes must be visible for deletion. */
3360
3361 /* Close the old heap but keep lock until transaction commit. */
3363 /* Close the new heap. (We didn't have to open its indexes). */
3365
3366 /* Cleanup what we don't need anymore. (And close the identity index.) */
3368
3369 /*
3370 * Swap the relations and their TOAST relations and TOAST indexes. This
3371 * also drops the new relation and its indexes.
3372 *
3373 * (System catalogs are currently not supported.)
3374 */
3378 false, /* swap_toast_by_content */
3379 false,
3380 true,
3381 false, /* reindex */
3383 relpersistence);
3384}
#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:3163
static void initialize_change_context(ChangeContext *chgcxt, Relation relation, Oid ident_index_id)
Definition repack.c:3019
static void process_concurrent_changes(XLogRecPtr end_of_wal, ChangeContext *chgcxt, bool done)
Definition repack.c:2963
static List * build_new_indexes(Relation NewHeap, Relation OldHeap, List *OldIndexes)
Definition repack.c:3397
#define InvalidTransactionId
Definition transam.h:31
XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI)
Definition xlog.c:7000
XLogRecPtr GetXLogInsertEndRecPtr(void)
Definition xlog.c:10111
void XLogFlush(XLogRecPtr record)
Definition xlog.c:2800
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 3163 of file repack.c.

3164{
3165 ExecCloseIndices(chgcxt->cc_rri);
3166 FreeExecutorState(chgcxt->cc_estate);
3167 /* XXX are these pfrees necessary? */
3168 pfree(chgcxt->cc_rri);
3169 pfree(chgcxt->cc_ident_key);
3170}
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 2346 of file repack.c.

2347{
2348 bool is_missing = false;
2350 char *relname;
2351
2353
2355 if (is_missing)
2356 return false;
2357
2358 if (result == ACLCHECK_OK)
2359 return true;
2360
2361 /*
2362 * The relation can also be dropped after we tested its ACL and before we
2363 * read its relname, so be careful here.
2364 */
2365 relname = get_rel_name(relid);
2366 if (relname != NULL)
2367 {
2369 errmsg("permission denied to execute %s on \"%s\", skipping it",
2371 pfree(relname);
2372 }
2373
2374 return false;
2375}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
AclResult pg_class_aclcheck_ext(Oid table_oid, Oid roleid, AclMode mode, bool *is_missing)
Definition aclchk.c:4115
#define WARNING
Definition elog.h:37
#define ACL_MAINTAIN
Definition parsenodes.h:90
NameData relname
Definition pg_class.h:40

References ACL_MAINTAIN, ACLCHECK_OK, Assert, ereport, errmsg, fb(), get_rel_name(), pfree(), pg_class_aclcheck_ext(), relname, REPACK_COMMAND_CLUSTER, REPACK_COMMAND_REPACK, RepackCommandAsString(), result, 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 2524 of file repack.c.

2525{
2526 switch (cmd)
2527 {
2529 return "REPACK";
2531 return "VACUUM";
2533 return "CLUSTER";
2534 }
2535 return "???"; /* keep compiler quiet */
2536}
@ REPACK_COMMAND_VACUUMFULL

References REPACK_COMMAND_CLUSTER, REPACK_COMMAND_REPACK, and REPACK_COMMAND_VACUUMFULL.

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

◆ RepackLockLevel()

static LOCKMODE RepackLockLevel ( bool  concurrent)
static

Definition at line 449 of file repack.c.

450{
451 if (concurrent)
453 else
454 return AccessExclusiveLock;
455}

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 2765 of file repack.c.

2766{
2767 uint32 t_len;
2768 HeapTuple tup;
2769 int natt_ext;
2770
2771 /* Read the tuple. */
2772 BufFileReadExact(file, &t_len, sizeof(t_len));
2773 tup = (HeapTuple) palloc(HEAPTUPLESIZE + t_len);
2774 tup->t_data = (HeapTupleHeader) ((char *) tup + HEAPTUPLESIZE);
2775 BufFileReadExact(file, tup->t_data, t_len);
2776 tup->t_len = t_len;
2777 ItemPointerSetInvalid(&tup->t_self);
2778 tup->t_tableOid = RelationGetRelid(relation);
2779
2780 /*
2781 * Put the tuple we read in a slot. This deforms it, so that we can hack
2782 * the external attributes in place.
2783 */
2784 ExecForceStoreHeapTuple(tup, slot, false);
2785
2786 /*
2787 * Next, read any attributes we stored separately into the tts_values
2788 * array elements expecting them, if any. This matches
2789 * repack_store_change.
2790 */
2791 BufFileReadExact(file, &natt_ext, sizeof(natt_ext));
2792 if (natt_ext > 0)
2793 {
2794 TupleDesc desc = slot->tts_tupleDescriptor;
2795
2796 for (int i = 0; i < desc->natts; i++)
2797 {
2799 varlena *varlen;
2801 void *value;
2802 Size varlensz;
2803
2804 if (attr->attisdropped || attr->attlen != -1)
2805 continue;
2806 if (slot_attisnull(slot, i + 1))
2807 continue;
2810 continue;
2811 slot_getsomeattrs(slot, i + 1);
2812
2815
2818 BufFileReadExact(file, (char *) value + VARHDRSZ, varlensz - VARHDRSZ);
2819
2821 natt_ext--;
2822 if (natt_ext < 0)
2823 elog(ERROR, "insufficient number of attributes stored separately");
2824 }
2825
2826 if (natt_ext != 0)
2827 elog(ERROR,
2828 "unexpected number of attributes stored separately (%d remaining)",
2829 natt_ext);
2830 }
2831}
#define VARHDRSZ
Definition c.h:840
uint64_t uint64
Definition c.h:684
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 @175 value
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition itemptr.h:184
#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(), elog, 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 3601 of file repack.c.

3602{
3603 Size size;
3604 DecodingWorkerShared *shared;
3605 shm_mq *mq;
3607
3609
3610 /* Setup shared memory. */
3611 size = BUFFERALIGN(offsetof(DecodingWorkerShared, error_queue)) +
3613 decoding_worker->seg = dsm_create(size, 0);
3614
3616 shared->initialized = false;
3617 shared->lsn_upto = InvalidXLogRecPtr;
3618 shared->done = false;
3620 shared->last_exported = -1;
3621 SpinLockInit(&shared->mutex);
3622 shared->dbid = MyDatabaseId;
3623
3624 /*
3625 * This is the UserId set in cluster_rel(). Security context shouldn't be
3626 * needed for decoding worker.
3627 */
3628 shared->roleid = GetUserId();
3629 shared->relid = relid;
3630 ConditionVariableInit(&shared->cv);
3631 shared->backend_proc = MyProc;
3632 shared->backend_pid = MyProcPid;
3634
3635 mq = shm_mq_create((char *) BUFFERALIGN(shared->error_queue),
3638
3640
3641 memset(&bgw, 0, sizeof(bgw));
3642 snprintf(bgw.bgw_name, BGW_MAXLEN,
3643 "REPACK decoding worker for relation \"%s\"",
3644 get_rel_name(relid));
3645 snprintf(bgw.bgw_type, BGW_MAXLEN, "REPACK decoding worker");
3646 bgw.bgw_flags = BGWORKER_SHMEM_ACCESS |
3648 bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
3649 bgw.bgw_restart_time = BGW_NEVER_RESTART;
3650 snprintf(bgw.bgw_library_name, MAXPGPATH, "postgres");
3651 snprintf(bgw.bgw_function_name, BGW_MAXLEN, "RepackWorkerMain");
3653 bgw.bgw_notify_pid = MyProcPid;
3654
3656 ereport(ERROR,
3658 errmsg("out of background worker slots"),
3659 errhint("You might need to increase \"%s\".", "max_worker_processes"));
3660
3661 /*
3662 * The decoding setup must be done before the caller can have XID assigned
3663 * for any reason, otherwise the worker might end up in a deadlock,
3664 * waiting for the caller's transaction to end. Therefore wait here until
3665 * the worker indicates that it has the logical decoding initialized.
3666 */
3668 for (;;)
3669 {
3670 bool initialized;
3671
3672 SpinLockAcquire(&shared->mutex);
3673 initialized = shared->initialized;
3674 SpinLockRelease(&shared->mutex);
3675
3676 if (initialized)
3677 break;
3678
3680 }
3682}
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:957
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:138
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 3691 of file repack.c.

3692{
3693 /* Nothing to do if no worker was set up. */
3694 if (decoding_worker == NULL)
3695 return;
3696
3697 /* Terminate the worker process, if one is running. */
3698 if (decoding_worker->handle != NULL)
3699 {
3700 BgwHandleStatus status;
3701
3703 /* The worker should really exit before the REPACK command does. */
3707
3708 if (status == BGWH_POSTMASTER_DIED)
3709 ereport(FATAL,
3711 errmsg("postmaster exited during REPACK command"));
3712 }
3713
3714 /*
3715 * Now detach from our shared memory segment. In error cases there might
3716 * still be messages from the worker in the queue, which ProcessInterrupts
3717 * would try to read; this is pointless (and causes an assertion failure),
3718 * so set the global pointer to NULL to have ProcessRepackMessages ignore
3719 * them.
3720 *
3721 * We must also cancel the current sleep, if one is still set up. This is
3722 * critical because the CV lives in the DSM that we're about to detach, so
3723 * if we omit it, later automatic cleanup tries to clear freed memory.
3724 */
3728 if (decoding_worker->seg != NULL)
3732}
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(), and stop_repack_decoding_worker_cb().

◆ stop_repack_decoding_worker_cb()

static void stop_repack_decoding_worker_cb ( int  code,
Datum  arg 
)
static

Definition at line 3736 of file repack.c.

3737{
3739}

References stop_repack_decoding_worker().

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 1499 of file repack.c.

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