49#include "utils/fmgroids.h"
74 bool verbose,
bool *pSwapToastByContent,
118 foreach(lc,
stmt->params)
122 if (strcmp(opt->
defname,
"verbose") == 0)
126 (
errcode(ERRCODE_SYNTAX_ERROR),
127 errmsg(
"unrecognized CLUSTER option \"%s\"",
134 if (
stmt->relation != NULL)
157 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
158 errmsg(
"cannot cluster temporary tables of other sessions")));
160 if (
stmt->indexname == NULL)
175 (
errcode(ERRCODE_UNDEFINED_OBJECT),
176 errmsg(
"there is no previously clustered index for table \"%s\"",
177 stmt->relation->relname)));
186 rel->
rd_rel->relnamespace);
189 (
errcode(ERRCODE_UNDEFINED_OBJECT),
190 errmsg(
"index \"%s\" for table \"%s\" does not exist",
191 stmt->indexname,
stmt->relation->relname)));
195 if (rel->
rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
232 Assert(rel->
rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
315 int save_sec_context;
409 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
410 errmsg(
"cannot cluster a shared catalog")));
420 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
421 errmsg(
"cannot cluster temporary tables of other sessions")));
424 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
425 errmsg(
"cannot vacuum temporary tables of other sessions")));
452 if (OldHeap->
rd_rel->relkind == RELKIND_MATVIEW &&
460 OldHeap->
rd_rel->relkind == RELKIND_MATVIEW ||
461 OldHeap->
rd_rel->relkind == RELKIND_TOASTVALUE);
506 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
507 errmsg(
"\"%s\" is not an index for table \"%s\"",
514 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
515 errmsg(
"cannot cluster on index \"%s\" because access method does not support clustering",
526 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
527 errmsg(
"cannot cluster on partial index \"%s\"",
538 if (!OldIndex->
rd_index->indisvalid)
540 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
541 errmsg(
"cannot cluster on invalid index \"%s\"",
562 if (rel->
rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
564 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
565 errmsg(
"cannot mark index clustered in partitioned table")));
588 elog(
ERROR,
"cache lookup failed for index %u", thisIndexOid);
595 if (indexForm->indisclustered)
597 indexForm->indisclustered =
false;
600 else if (thisIndexOid == indexOid)
603 if (!indexForm->indisvalid)
604 elog(
ERROR,
"cannot cluster on invalid index %u", indexOid);
605 indexForm->indisclustered =
true;
632 Oid accessMethod = OldHeap->
rd_rel->relam;
633 Oid tableSpace = OldHeap->
rd_rel->reltablespace;
637 bool is_system_catalog;
638 bool swap_toast_by_content;
650 relpersistence = OldHeap->
rd_rel->relpersistence;
668 &swap_toast_by_content, &frozenXid, &cutoffMulti);
688 swap_toast_by_content,
false,
true,
689 frozenXid, cutoffMulti,
706 char relpersistence,
LOCKMODE lockmode)
733 elog(
ERROR,
"cache lookup failed for relation %u", OIDOldHeap);
737 reloptions = (
Datum) 0;
739 if (relpersistence == RELPERSISTENCE_TEMP)
756 snprintf(NewHeapName,
sizeof(NewHeapName),
"pg_temp_%u", OIDOldHeap);
764 OldHeap->
rd_rel->relowner,
800 toastid = OldHeap->
rd_rel->reltoastrelid;
806 elog(
ERROR,
"cache lookup failed for relation %u", toastid);
810 reloptions = (
Datum) 0;
843 double num_tuples = 0,
845 tups_recently_dead = 0;
862 Assert(newTupDesc->natts == oldTupDesc->natts);
877 if (OldHeap->
rd_rel->reltoastrelid)
887 if (OldHeap->
rd_rel->reltoastrelid && NewHeap->
rd_rel->reltoastrelid)
889 *pSwapToastByContent =
true;
912 *pSwapToastByContent =
false;
952 if (OldIndex != NULL && OldIndex->
rd_rel->relam == BTREE_AM_OID)
959 if (OldIndex != NULL && !use_sort)
961 (
errmsg(
"clustering \"%s.%s\" using index scan on \"%s\"",
967 (
errmsg(
"clustering \"%s.%s\" using sequential scan and sort",
972 (
errmsg(
"vacuuming \"%s.%s\"",
985 &num_tuples, &tups_vacuumed,
986 &tups_recently_dead);
999 (
errmsg(
"\"%s.%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
1002 tups_vacuumed, num_tuples,
1004 errdetail(
"%.0f dead row versions cannot be removed yet.\n"
1015 elog(
ERROR,
"cache lookup failed for relation %u",
1019 relform->relpages = num_pages;
1020 relform->reltuples = num_tuples;
1064 bool swap_toast_by_content,
1087 elog(
ERROR,
"cache lookup failed for relation %u", r1);
1092 elog(
ERROR,
"cache lookup failed for relation %u", r2);
1095 relfilenumber1 = relform1->relfilenode;
1096 relfilenumber2 = relform2->relfilenode;
1097 relam1 = relform1->relam;
1098 relam2 = relform2->relam;
1107 Assert(!target_is_pg_class);
1109 swaptemp = relform1->relfilenode;
1110 relform1->relfilenode = relform2->relfilenode;
1111 relform2->relfilenode = swaptemp;
1113 swaptemp = relform1->reltablespace;
1114 relform1->reltablespace = relform2->reltablespace;
1115 relform2->reltablespace = swaptemp;
1117 swaptemp = relform1->relam;
1118 relform1->relam = relform2->relam;
1119 relform2->relam = swaptemp;
1121 swptmpchr = relform1->relpersistence;
1122 relform1->relpersistence = relform2->relpersistence;
1123 relform2->relpersistence = swptmpchr;
1126 if (!swap_toast_by_content)
1128 swaptemp = relform1->reltoastrelid;
1129 relform1->reltoastrelid = relform2->reltoastrelid;
1130 relform2->reltoastrelid = swaptemp;
1141 elog(
ERROR,
"cannot swap mapped relation \"%s\" with non-mapped relation",
1151 if (relform1->reltablespace != relform2->reltablespace)
1152 elog(
ERROR,
"cannot change tablespace of mapped relation \"%s\"",
1154 if (relform1->relpersistence != relform2->relpersistence)
1155 elog(
ERROR,
"cannot change persistence of mapped relation \"%s\"",
1157 if (relform1->relam != relform2->relam)
1158 elog(
ERROR,
"cannot change access method of mapped relation \"%s\"",
1160 if (!swap_toast_by_content &&
1161 (relform1->reltoastrelid || relform2->reltoastrelid))
1162 elog(
ERROR,
"cannot swap toast by links for mapped relation \"%s\"",
1170 elog(
ERROR,
"could not find relation mapping for relation \"%s\", OID %u",
1171 NameStr(relform1->relname), r1);
1174 elog(
ERROR,
"could not find relation mapping for relation \"%s\", OID %u",
1175 NameStr(relform2->relname), r2);
1185 *mapped_tables++ = r2;
1216 if (relform1->relkind != RELKIND_INDEX)
1220 relform1->relfrozenxid = frozenXid;
1221 relform1->relminmxid = cutoffMulti;
1228 int32 swap_allvisible;
1229 int32 swap_allfrozen;
1231 swap_pages = relform1->relpages;
1232 relform1->relpages = relform2->relpages;
1233 relform2->relpages = swap_pages;
1235 swap_tuples = relform1->reltuples;
1236 relform1->reltuples = relform2->reltuples;
1237 relform2->reltuples = swap_tuples;
1239 swap_allvisible = relform1->relallvisible;
1240 relform1->relallvisible = relform2->relallvisible;
1241 relform2->relallvisible = swap_allvisible;
1243 swap_allfrozen = relform1->relallfrozen;
1244 relform1->relallfrozen = relform2->relallfrozen;
1245 relform2->relallfrozen = swap_allfrozen;
1257 if (!target_is_pg_class)
1280 if (relam1 != relam2)
1284 AccessMethodRelationId,
1287 elog(
ERROR,
"could not change access method dependency for relation \"%s.%s\"",
1292 AccessMethodRelationId,
1295 elog(
ERROR,
"could not change access method dependency for relation \"%s.%s\"",
1313 if (relform1->reltoastrelid || relform2->reltoastrelid)
1315 if (swap_toast_by_content)
1317 if (relform1->reltoastrelid && relform2->reltoastrelid)
1321 relform2->reltoastrelid,
1323 swap_toast_by_content,
1332 elog(
ERROR,
"cannot swap toast files by content when there's only one");
1359 elog(
ERROR,
"cannot swap toast files by links for system catalogs");
1362 if (relform1->reltoastrelid)
1365 relform1->reltoastrelid,
1368 elog(
ERROR,
"expected one dependency record for TOAST table, found %ld",
1371 if (relform2->reltoastrelid)
1374 relform2->reltoastrelid,
1377 elog(
ERROR,
"expected one dependency record for TOAST table, found %ld",
1382 baseobject.
classId = RelationRelationId;
1384 toastobject.
classId = RelationRelationId;
1387 if (relform1->reltoastrelid)
1390 toastobject.
objectId = relform1->reltoastrelid;
1395 if (relform2->reltoastrelid)
1398 toastobject.
objectId = relform2->reltoastrelid;
1410 if (swap_toast_by_content &&
1411 relform1->relkind == RELKIND_TOASTVALUE &&
1412 relform2->relkind == RELKIND_TOASTVALUE)
1426 swap_toast_by_content,
1446 bool is_system_catalog,
1447 bool swap_toast_by_content,
1448 bool check_constraints,
1452 char newrelpersistence)
1455 Oid mapped_tables[4];
1465 memset(mapped_tables, 0,
sizeof(mapped_tables));
1472 (OIDOldHeap == RelationRelationId),
1473 swap_toast_by_content, is_internal,
1474 frozenXid, cutoffMulti, mapped_tables);
1480 if (is_system_catalog)
1499 if (check_constraints)
1506 if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1508 else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1533 if (OIDOldHeap == RelationRelationId)
1543 elog(
ERROR,
"cache lookup failed for relation %u", OIDOldHeap);
1546 relform->relfrozenxid = frozenXid;
1547 relform->relminmxid = cutoffMulti;
1555 object.classId = RelationRelationId;
1556 object.objectId = OIDNewHeap;
1557 object.objectSubId = 0;
1586 if (!swap_toast_by_content)
1604 NewToastName,
true,
false);
1611 NewToastName,
true,
true);
1625 if (!is_system_catalog)
1659 Anum_pg_index_indisclustered,
1707 foreach(lc, inhoids)
1751 (
errmsg(
"permission denied to cluster \"%s\", skipping it",
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_CLUSTER
#define RelationGetNumberOfBlocks(reln)
#define PG_USED_FOR_ASSERTS_ONLY
TransactionId MultiXactId
#define OidIsValid(objectId)
bool IsSystemRelation(Relation relation)
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
static void copy_table_data(Relation NewHeap, Relation OldHeap, Relation OldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
static List * get_tables_to_cluster(MemoryContext cluster_context)
void cluster_rel(Relation OldHeap, Oid indexOid, ClusterParams *params)
static List * get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
static bool cluster_is_permitted_for_relation(Oid relid, Oid userid)
static void cluster_multiple_rels(List *rtcs, ClusterParams *params)
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
static void rebuild_relation(Relation OldHeap, Relation index, bool verbose)
void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
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)
#define CLUOPT_RECHECK_ISCLUSTERED
bool defGetBoolean(DefElem *def)
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
#define PERFORM_DELETION_INTERNAL
int errdetail(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
int NewGUCNestLevel(void)
void RestrictSearchPath(void)
void AtEOXact_GUC(bool isCommit, int nestLevel)
Assert(PointerIsAligned(start, uint64))
void RelationClearMissing(Relation rel)
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)
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
void heap_freetuple(HeapTuple htup)
#define HeapTupleIsValid(tuple)
static void * GETSTRUCT(const HeapTupleData *tuple)
Oid IndexGetRelation(Oid indexId, bool missing_ok)
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
#define REINDEX_REL_SUPPRESS_INDEX_USE
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
#define REINDEX_REL_CHECK_CONSTRAINTS
void index_close(Relation relation, LOCKMODE lockmode)
Relation index_open(Oid relationId, LOCKMODE lockmode)
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
void CatalogCloseIndexes(CatalogIndexState indstate)
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
void CatalogTupleUpdateWithInfo(Relation heapRel, ItemPointer otid, HeapTuple tup, CatalogIndexState indstate)
void CacheInvalidateCatalog(Oid catalogId)
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
List * lappend(List *list, void *datum)
void LockRelationOid(Oid relid, LOCKMODE lockmode)
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
#define AccessExclusiveLock
char * get_rel_name(Oid relid)
char get_rel_relkind(Oid relid)
Oid get_rel_namespace(Oid relid)
bool get_index_isclustered(Oid index_oid)
char * get_namespace_name(Oid nspid)
Oid get_relname_relid(const char *relname, Oid relnamespace)
void MemoryContextDelete(MemoryContext context)
MemoryContext PortalContext
#define AllocSetContextCreate
#define ALLOCSET_DEFAULT_SIZES
#define SECURITY_RESTRICTED_OPERATION
#define CHECK_FOR_INTERRUPTS()
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
void SetUserIdAndSecContext(Oid userid, int sec_context)
bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
#define MultiXactIdIsValid(multi)
#define InvalidMultiXactId
Oid LookupCreationNamespace(const char *nspname)
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
int parser_errposition(ParseState *pstate, int location)
FormData_pg_class * Form_pg_class
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
FormData_pg_index * Form_pg_index
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
const char * pg_rusage_show(const PGRUsage *ru0)
void pg_rusage_init(PGRUsage *ru0)
bool plan_cluster_use_sort(Oid tableOid, Oid indexOid)
static Datum BoolGetDatum(bool X)
static Datum ObjectIdGetDatum(Oid X)
void TransferPredicateLocksToHeapRelation(Relation relation)
#define PROGRESS_CLUSTER_PHASE
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP
#define PROGRESS_CLUSTER_COMMAND
#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES
#define RelationGetRelid(relation)
#define RelationGetDescr(relation)
#define RelationIsMapped(relation)
#define RelationGetRelationName(relation)
#define RelationIsPopulated(relation)
#define RELATION_IS_OTHER_TEMP(relation)
#define RelationGetNamespace(relation)
List * RelationGetIndexList(Relation relation)
void RelationAssumeNewRelfilelocator(Relation relation)
void RelationMapRemoveMapping(Oid relationId)
RelFileNumber RelationMapOidToFilenumber(Oid relationId, bool shared)
void RelationMapUpdateMap(Oid relationId, RelFileNumber fileNumber, bool shared, bool immediate)
#define RelFileNumberIsValid(relnumber)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Snapshot GetTransactionSnapshot(void)
void PushActiveSnapshot(Snapshot snapshot)
void PopActiveSnapshot(void)
void relation_close(Relation relation, LOCKMODE lockmode)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
#define BTEqualStrategyNumber
struct IndexAmRoutine * rd_indam
SubTransactionId rd_firstRelfilelocatorSubid
struct HeapTupleData * rd_indextuple
SubTransactionId rd_newRelfilelocatorSubid
SubTransactionId rd_createSubid
TransactionId FreezeLimit
TransactionId relfrozenxid
MultiXactId MultiXactCutoff
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
#define SearchSysCacheCopy1(cacheId, key1)
#define SearchSysCacheExists1(cacheId, key1)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
static void table_endscan(TableScanDesc scan)
static void table_relation_copy_for_cluster(Relation OldTable, Relation NewTable, Relation OldIndex, bool use_sort, TransactionId OldestXmin, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead)
void ResetRelRewrite(Oid myrelid)
void CheckTableNotInUse(Relation rel, const char *stmt)
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, Oid OIDOldToast)
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
#define InvalidTransactionId
#define TransactionIdIsValid(xid)
#define TransactionIdIsNormal(xid)
bool vacuum_get_cutoffs(Relation rel, const VacuumParams *params, struct VacuumCutoffs *cutoffs)
void CommandCounterIncrement(void)
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
void StartTransactionCommand(void)
void CommitTransactionCommand(void)