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 CatalogTupleUpdateWithInfo(Relation heapRel, const ItemPointerData *otid, HeapTuple tup, CatalogIndexState indstate)
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
void CatalogCloseIndexes(CatalogIndexState indstate)
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
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, 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)
#define InvalidTransactionId
#define TransactionIdIsValid(xid)
#define TransactionIdIsNormal(xid)
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
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)