51 #include "utils/fmgroids.h"
76 bool verbose,
bool *pSwapToastByContent,
123 if (strcmp(opt->
defname,
"verbose") == 0)
127 (
errcode(ERRCODE_SYNTAX_ERROR),
128 errmsg(
"unrecognized CLUSTER option \"%s\"",
157 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
158 errmsg(
"cannot cluster temporary tables of other sessions")));
175 (
errcode(ERRCODE_UNDEFINED_OBJECT),
176 errmsg(
"there is no previously clustered index for table \"%s\"",
186 rel->
rd_rel->relnamespace);
189 (
errcode(ERRCODE_UNDEFINED_OBJECT),
190 errmsg(
"index \"%s\" for table \"%s\" does not exist",
194 if (rel->
rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
234 Assert(rel->
rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
314 int save_sec_context;
419 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
420 errmsg(
"cannot cluster a shared catalog")));
430 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
431 errmsg(
"cannot cluster temporary tables of other sessions")));
434 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
435 errmsg(
"cannot vacuum temporary tables of other sessions")));
455 if (OldHeap->
rd_rel->relkind == RELKIND_MATVIEW &&
463 OldHeap->
rd_rel->relkind == RELKIND_MATVIEW ||
464 OldHeap->
rd_rel->relkind == RELKIND_TOASTVALUE);
510 (
errcode(ERRCODE_WRONG_OBJECT_TYPE),
511 errmsg(
"\"%s\" is not an index for table \"%s\"",
518 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
519 errmsg(
"cannot cluster on index \"%s\" because access method does not support clustering",
530 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
531 errmsg(
"cannot cluster on partial index \"%s\"",
542 if (!OldIndex->
rd_index->indisvalid)
544 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
545 errmsg(
"cannot cluster on invalid index \"%s\"",
566 if (rel->
rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
568 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
569 errmsg(
"cannot mark index clustered in partitioned table")));
592 elog(
ERROR,
"cache lookup failed for index %u", thisIndexOid);
599 if (indexForm->indisclustered)
601 indexForm->indisclustered =
false;
604 else if (thisIndexOid == indexOid)
607 if (!indexForm->indisvalid)
608 elog(
ERROR,
"cannot cluster on invalid index %u", indexOid);
609 indexForm->indisclustered =
true;
634 Oid accessMethod = OldHeap->
rd_rel->relam;
635 Oid tableSpace = OldHeap->
rd_rel->reltablespace;
638 bool is_system_catalog;
639 bool swap_toast_by_content;
648 relpersistence = OldHeap->
rd_rel->relpersistence;
662 &swap_toast_by_content, &frozenXid, &cutoffMulti);
669 swap_toast_by_content,
false,
true,
670 frozenXid, cutoffMulti,
687 char relpersistence,
LOCKMODE lockmode)
714 elog(
ERROR,
"cache lookup failed for relation %u", OIDOldHeap);
718 reloptions = (
Datum) 0;
720 if (relpersistence == RELPERSISTENCE_TEMP)
737 snprintf(NewHeapName,
sizeof(NewHeapName),
"pg_temp_%u", OIDOldHeap);
745 OldHeap->
rd_rel->relowner,
781 toastid = OldHeap->
rd_rel->reltoastrelid;
787 elog(
ERROR,
"cache lookup failed for relation %u", toastid);
791 reloptions = (
Datum) 0;
829 double num_tuples = 0,
831 tups_recently_dead = 0;
858 Assert(newTupDesc->natts == oldTupDesc->natts);
873 if (OldHeap->
rd_rel->reltoastrelid)
883 if (OldHeap->
rd_rel->reltoastrelid && NewHeap->
rd_rel->reltoastrelid)
885 *pSwapToastByContent =
true;
908 *pSwapToastByContent =
false;
916 &FreezeXid, &MultiXactCutoff);
924 FreezeXid = OldHeap->
rd_rel->relfrozenxid;
931 MultiXactCutoff = OldHeap->
rd_rel->relminmxid;
940 if (OldIndex != NULL && OldIndex->
rd_rel->relam == BTREE_AM_OID)
946 if (OldIndex != NULL && !use_sort)
948 (
errmsg(
"clustering \"%s.%s\" using index scan on \"%s\"",
954 (
errmsg(
"clustering \"%s.%s\" using sequential scan and sort",
959 (
errmsg(
"vacuuming \"%s.%s\"",
970 OldestXmin, &FreezeXid, &MultiXactCutoff,
971 &num_tuples, &tups_vacuumed,
972 &tups_recently_dead);
975 *pFreezeXid = FreezeXid;
976 *pCutoffMulti = MultiXactCutoff;
985 (
errmsg(
"\"%s.%s\": found %.0f removable, %.0f nonremovable row versions in %u pages",
988 tups_vacuumed, num_tuples,
990 errdetail(
"%.0f dead row versions cannot be removed yet.\n"
995 if (OldIndex != NULL)
1005 elog(
ERROR,
"cache lookup failed for relation %u", OIDNewHeap);
1008 relform->relpages = num_pages;
1009 relform->reltuples = num_tuples;
1012 if (OIDOldHeap != RelationRelationId)
1053 bool swap_toast_by_content,
1074 elog(
ERROR,
"cache lookup failed for relation %u", r1);
1079 elog(
ERROR,
"cache lookup failed for relation %u", r2);
1082 relfilenode1 = relform1->relfilenode;
1083 relfilenode2 = relform2->relfilenode;
1091 Assert(!target_is_pg_class);
1093 swaptemp = relform1->relfilenode;
1094 relform1->relfilenode = relform2->relfilenode;
1095 relform2->relfilenode = swaptemp;
1097 swaptemp = relform1->reltablespace;
1098 relform1->reltablespace = relform2->reltablespace;
1099 relform2->reltablespace = swaptemp;
1101 swaptemp = relform1->relam;
1102 relform1->relam = relform2->relam;
1103 relform2->relam = swaptemp;
1105 swptmpchr = relform1->relpersistence;
1106 relform1->relpersistence = relform2->relpersistence;
1107 relform2->relpersistence = swptmpchr;
1110 if (!swap_toast_by_content)
1112 swaptemp = relform1->reltoastrelid;
1113 relform1->reltoastrelid = relform2->reltoastrelid;
1114 relform2->reltoastrelid = swaptemp;
1124 elog(
ERROR,
"cannot swap mapped relation \"%s\" with non-mapped relation",
1134 if (relform1->reltablespace != relform2->reltablespace)
1135 elog(
ERROR,
"cannot change tablespace of mapped relation \"%s\"",
1137 if (relform1->relpersistence != relform2->relpersistence)
1138 elog(
ERROR,
"cannot change persistence of mapped relation \"%s\"",
1140 if (relform1->relam != relform2->relam)
1141 elog(
ERROR,
"cannot change access method of mapped relation \"%s\"",
1143 if (!swap_toast_by_content &&
1144 (relform1->reltoastrelid || relform2->reltoastrelid))
1145 elog(
ERROR,
"cannot swap toast by links for mapped relation \"%s\"",
1153 elog(
ERROR,
"could not find relation mapping for relation \"%s\", OID %u",
1154 NameStr(relform1->relname), r1);
1157 elog(
ERROR,
"could not find relation mapping for relation \"%s\", OID %u",
1158 NameStr(relform2->relname), r2);
1168 *mapped_tables++ = r2;
1199 if (relform1->relkind != RELKIND_INDEX)
1203 relform1->relfrozenxid = frozenXid;
1204 relform1->relminmxid = cutoffMulti;
1211 int32 swap_allvisible;
1213 swap_pages = relform1->relpages;
1214 relform1->relpages = relform2->relpages;
1215 relform2->relpages = swap_pages;
1217 swap_tuples = relform1->reltuples;
1218 relform1->reltuples = relform2->reltuples;
1219 relform2->reltuples = swap_tuples;
1221 swap_allvisible = relform1->relallvisible;
1222 relform1->relallvisible = relform2->relallvisible;
1223 relform2->relallvisible = swap_allvisible;
1235 if (!target_is_pg_class)
1266 if (relform1->reltoastrelid || relform2->reltoastrelid)
1268 if (swap_toast_by_content)
1270 if (relform1->reltoastrelid && relform2->reltoastrelid)
1274 relform2->reltoastrelid,
1276 swap_toast_by_content,
1285 elog(
ERROR,
"cannot swap toast files by content when there's only one");
1312 elog(
ERROR,
"cannot swap toast files by links for system catalogs");
1315 if (relform1->reltoastrelid)
1318 relform1->reltoastrelid,
1321 elog(
ERROR,
"expected one dependency record for TOAST table, found %ld",
1324 if (relform2->reltoastrelid)
1327 relform2->reltoastrelid,
1330 elog(
ERROR,
"expected one dependency record for TOAST table, found %ld",
1335 baseobject.
classId = RelationRelationId;
1337 toastobject.
classId = RelationRelationId;
1340 if (relform1->reltoastrelid)
1343 toastobject.
objectId = relform1->reltoastrelid;
1348 if (relform2->reltoastrelid)
1351 toastobject.
objectId = relform2->reltoastrelid;
1363 if (swap_toast_by_content &&
1364 relform1->relkind == RELKIND_TOASTVALUE &&
1365 relform2->relkind == RELKIND_TOASTVALUE)
1379 swap_toast_by_content,
1418 bool is_system_catalog,
1419 bool swap_toast_by_content,
1420 bool check_constraints,
1424 char newrelpersistence)
1427 Oid mapped_tables[4];
1437 memset(mapped_tables, 0,
sizeof(mapped_tables));
1444 (OIDOldHeap == RelationRelationId),
1445 swap_toast_by_content, is_internal,
1446 frozenXid, cutoffMulti, mapped_tables);
1452 if (is_system_catalog)
1471 if (check_constraints)
1478 if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1480 else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1505 if (OIDOldHeap == RelationRelationId)
1515 elog(
ERROR,
"cache lookup failed for relation %u", OIDOldHeap);
1518 relform->relfrozenxid = frozenXid;
1519 relform->relminmxid = cutoffMulti;
1527 object.classId = RelationRelationId;
1528 object.objectId = OIDNewHeap;
1529 object.objectSubId = 0;
1558 if (!swap_toast_by_content)
1576 NewToastName,
true,
false);
1583 NewToastName,
true,
true);
1597 if (!is_system_catalog)
1631 Anum_pg_index_indisclustered,
1679 foreach(lc, inhoids)
bool pg_database_ownercheck(Oid db_oid, Oid roleid)
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
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)
TransactionId MultiXactId
#define PG_USED_FOR_ASSERTS_ONLY
#define OidIsValid(objectId)
bool IsSystemRelation(Relation relation)
bool IsSharedRelation(Oid relationId)
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
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)
static List * get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
static void copy_table_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, bool *pSwapToastByContent, TransactionId *pFreezeXid, MultiXactId *pCutoffMulti)
static void cluster_multiple_rels(List *rtcs, ClusterParams *params)
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
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 AtEOXact_GUC(bool isCommit, int nestLevel)
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)
Oid IndexGetRelation(Oid indexId, bool missing_ok)
bool reindex_relation(Oid relid, int flags, 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)
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
void LockRelationOid(Oid relid, LOCKMODE lockmode)
#define AccessExclusiveLock
char * get_namespace_name(Oid nspid)
char get_rel_relkind(Oid relid)
bool get_index_isclustered(Oid index_oid)
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 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)
#define ObjectIdGetDatum(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 RelationCloseSmgrByOid(Oid relationId)
void RelationAssumeNewRelfilenode(Relation relation)
void RelationMapRemoveMapping(Oid relationId)
void RelationMapUpdateMap(Oid relationId, Oid fileNode, bool shared, bool immediate)
Oid RelationMapOidToFilenode(Oid relationId, bool shared)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Snapshot GetTransactionSnapshot(void)
void PopActiveSnapshot(void)
void PushActiveSnapshot(Snapshot snap)
void relation_close(Relation relation, LOCKMODE lockmode)
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Relation relation_open(Oid relationId, LOCKMODE lockmode)
#define BTEqualStrategyNumber
struct IndexAmRoutine * rd_indam
SubTransactionId rd_newRelfilenodeSubid
SubTransactionId rd_firstRelfilenodeSubid
struct HeapTupleData * rd_indextuple
SubTransactionId rd_createSubid
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 RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
void CheckTableNotInUse(Relation rel, const char *stmt)
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
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_set_xid_limits(Relation rel, int freeze_min_age, int freeze_table_age, int multixact_freeze_min_age, int multixact_freeze_table_age, TransactionId *oldestXmin, MultiXactId *oldestMxact, TransactionId *freezeLimit, MultiXactId *multiXactCutoff)
void CommandCounterIncrement(void)
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
void StartTransactionCommand(void)
void CommitTransactionCommand(void)