PostgreSQL Source Code  git master
cluster.h File Reference
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/relcache.h"
Include dependency graph for cluster.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void cluster (ClusterStmt *stmt, bool isTopLevel)
 
void cluster_rel (Oid tableOid, Oid indexOid, int options)
 
void check_index_is_clusterable (Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
 
void mark_index_clustered (Relation rel, Oid indexOid, bool is_internal)
 
Oid make_new_heap (Oid OIDOldHeap, Oid NewTableSpace, char relpersistence, 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 minMulti, char newrelpersistence)
 

Function Documentation

◆ check_index_is_clusterable()

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

Definition at line 445 of file cluster.c.

References IndexAmRoutine::amclusterable, ereport, errcode(), errmsg(), ERROR, heap_attisnull(), index_close(), index_open(), NoLock, RelationData::rd_indam, RelationData::rd_index, RelationData::rd_indextuple, RelationGetRelationName, and RelationGetRelid.

Referenced by ATExecClusterOn(), and cluster_rel().

446 {
447  Relation OldIndex;
448 
449  OldIndex = index_open(indexOid, lockmode);
450 
451  /*
452  * Check that index is in fact an index on the given relation
453  */
454  if (OldIndex->rd_index == NULL ||
455  OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
456  ereport(ERROR,
457  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
458  errmsg("\"%s\" is not an index for table \"%s\"",
459  RelationGetRelationName(OldIndex),
460  RelationGetRelationName(OldHeap))));
461 
462  /* Index AM must allow clustering */
463  if (!OldIndex->rd_indam->amclusterable)
464  ereport(ERROR,
465  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
466  errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
467  RelationGetRelationName(OldIndex))));
468 
469  /*
470  * Disallow clustering on incomplete indexes (those that might not index
471  * every row of the relation). We could relax this by making a separate
472  * seqscan pass over the table to copy the missing rows, but that seems
473  * expensive and tedious.
474  */
475  if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
476  ereport(ERROR,
477  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
478  errmsg("cannot cluster on partial index \"%s\"",
479  RelationGetRelationName(OldIndex))));
480 
481  /*
482  * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
483  * it might well not contain entries for every heap row, or might not even
484  * be internally consistent. (But note that we don't check indcheckxmin;
485  * the worst consequence of following broken HOT chains would be that we
486  * might put recently-dead tuples out-of-order in the new table, and there
487  * is little harm in that.)
488  */
489  if (!OldIndex->rd_index->indisvalid)
490  ereport(ERROR,
491  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
492  errmsg("cannot cluster on invalid index \"%s\"",
493  RelationGetRelationName(OldIndex))));
494 
495  /* Drop relcache refcnt on OldIndex, but keep lock */
496  index_close(OldIndex, NoLock);
497 }
struct IndexAmRoutine * rd_indam
Definition: rel.h:157
int errcode(int sqlerrcode)
Definition: elog.c:570
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:359
struct HeapTupleData * rd_indextuple
Definition: rel.h:145
Form_pg_index rd_index
Definition: rel.h:143
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:450
#define ereport(elevel, rest)
Definition: elog.h:141
bool amclusterable
Definition: amapi.h:193
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:152
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define RelationGetRelid(relation)
Definition: rel.h:416
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:126

◆ cluster()

void cluster ( ClusterStmt stmt,
bool  isTopLevel 
)

Definition at line 103 of file cluster.c.

References AccessExclusiveLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CLUOPT_RECHECK, cluster_rel(), CommitTransactionCommand(), elog, ereport, errcode(), errmsg(), ERROR, get_relname_relid(), get_tables_to_cluster(), GETSTRUCT, GetTransactionSnapshot(), HeapTupleIsValid, ClusterStmt::indexname, RelToCluster::indexOid, INDEXRELID, InvalidOid, lfirst, lfirst_oid, MemoryContextDelete(), NoLock, ObjectIdGetDatum, OidIsValid, ClusterStmt::options, PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), PushActiveSnapshot(), RangeVarCallbackOwnsTable(), RangeVarGetRelidExtended(), RelationData::rd_rel, ClusterStmt::relation, RELATION_IS_OTHER_TEMP, RelationGetIndexList(), ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), StartTransactionCommand(), table_close(), table_open(), and RelToCluster::tableOid.

Referenced by standard_ProcessUtility(), start_postmaster(), and stop_postmaster().

104 {
105  if (stmt->relation != NULL)
106  {
107  /* This is the single-relation case. */
108  Oid tableOid,
109  indexOid = InvalidOid;
110  Relation rel;
111 
112  /* Find, lock, and check permissions on the table */
113  tableOid = RangeVarGetRelidExtended(stmt->relation,
115  0,
117  rel = table_open(tableOid, NoLock);
118 
119  /*
120  * Reject clustering a remote temp table ... their local buffer
121  * manager is not going to cope.
122  */
123  if (RELATION_IS_OTHER_TEMP(rel))
124  ereport(ERROR,
125  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
126  errmsg("cannot cluster temporary tables of other sessions")));
127 
128  /*
129  * Reject clustering a partitioned table.
130  */
131  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
132  ereport(ERROR,
133  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
134  errmsg("cannot cluster a partitioned table")));
135 
136  if (stmt->indexname == NULL)
137  {
138  ListCell *index;
139 
140  /* We need to find the index that has indisclustered set. */
141  foreach(index, RelationGetIndexList(rel))
142  {
143  HeapTuple idxtuple;
144  Form_pg_index indexForm;
145 
146  indexOid = lfirst_oid(index);
147  idxtuple = SearchSysCache1(INDEXRELID,
148  ObjectIdGetDatum(indexOid));
149  if (!HeapTupleIsValid(idxtuple))
150  elog(ERROR, "cache lookup failed for index %u", indexOid);
151  indexForm = (Form_pg_index) GETSTRUCT(idxtuple);
152  if (indexForm->indisclustered)
153  {
154  ReleaseSysCache(idxtuple);
155  break;
156  }
157  ReleaseSysCache(idxtuple);
158  indexOid = InvalidOid;
159  }
160 
161  if (!OidIsValid(indexOid))
162  ereport(ERROR,
163  (errcode(ERRCODE_UNDEFINED_OBJECT),
164  errmsg("there is no previously clustered index for table \"%s\"",
165  stmt->relation->relname)));
166  }
167  else
168  {
169  /*
170  * The index is expected to be in the same namespace as the
171  * relation.
172  */
173  indexOid = get_relname_relid(stmt->indexname,
174  rel->rd_rel->relnamespace);
175  if (!OidIsValid(indexOid))
176  ereport(ERROR,
177  (errcode(ERRCODE_UNDEFINED_OBJECT),
178  errmsg("index \"%s\" for table \"%s\" does not exist",
179  stmt->indexname, stmt->relation->relname)));
180  }
181 
182  /* close relation, keep lock till commit */
183  table_close(rel, NoLock);
184 
185  /* Do the job. */
186  cluster_rel(tableOid, indexOid, stmt->options);
187  }
188  else
189  {
190  /*
191  * This is the "multi relation" case. We need to cluster all tables
192  * that have some index with indisclustered set.
193  */
194  MemoryContext cluster_context;
195  List *rvs;
196  ListCell *rv;
197 
198  /*
199  * We cannot run this form of CLUSTER inside a user transaction block;
200  * we'd be holding locks way too long.
201  */
202  PreventInTransactionBlock(isTopLevel, "CLUSTER");
203 
204  /*
205  * Create special memory context for cross-transaction storage.
206  *
207  * Since it is a child of PortalContext, it will go away even in case
208  * of error.
209  */
210  cluster_context = AllocSetContextCreate(PortalContext,
211  "Cluster",
213 
214  /*
215  * Build the list of relations to cluster. Note that this lives in
216  * cluster_context.
217  */
218  rvs = get_tables_to_cluster(cluster_context);
219 
220  /* Commit to get out of starting transaction */
223 
224  /* Ok, now that we've got them all, cluster them one by one */
225  foreach(rv, rvs)
226  {
227  RelToCluster *rvtc = (RelToCluster *) lfirst(rv);
228 
229  /* Start a new transaction for each relation. */
231  /* functions in indexes may want a snapshot set */
233  /* Do the job. */
234  cluster_rel(rvtc->tableOid, rvtc->indexOid,
235  stmt->options | CLUOPT_RECHECK);
238  }
239 
240  /* Start a new transaction for the cleanup work. */
242 
243  /* Clean up working storage */
244  MemoryContextDelete(cluster_context);
245  }
246 }
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:14745
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:169
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
void CommitTransactionCommand(void)
Definition: xact.c:2895
int errcode(int sqlerrcode)
Definition: elog.c:570
void PopActiveSnapshot(void)
Definition: snapmgr.c:814
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:306
#define OidIsValid(objectId)
Definition: c.h:638
void cluster_rel(Oid tableOid, Oid indexOid, int options)
Definition: cluster.c:266
Oid tableOid
Definition: cluster.c:66
MemoryContext PortalContext
Definition: mcxt.c:53
Definition: type.h:89
char * relname
Definition: primnodes.h:68
char * indexname
Definition: parsenodes.h:3174
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1687
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:191
#define NoLock
Definition: lockdefs.h:34
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:735
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3328
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:228
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1530
#define ereport(elevel, rest)
Definition: elog.h:141
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define lfirst(lc)
Definition: pg_list.h:190
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:546
void StartTransactionCommand(void)
Definition: xact.c:2794
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4348
Oid indexOid
Definition: cluster.c:67
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
RangeVar * relation
Definition: parsenodes.h:3173
Definition: pg_list.h:50
#define lfirst_oid(lc)
Definition: pg_list.h:192

◆ cluster_rel()

void cluster_rel ( Oid  tableOid,
Oid  indexOid,
int  options 
)

Definition at line 266 of file cluster.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), CLUOPT_RECHECK, CLUOPT_VERBOSE, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, GetUserId(), HeapTupleIsValid, INDEXRELID, ObjectIdGetDatum, OidIsValid, pg_class_ownercheck(), pgstat_progress_end_command(), pgstat_progress_start_command(), pgstat_progress_update_param(), PROGRESS_CLUSTER_COMMAND, PROGRESS_CLUSTER_COMMAND_CLUSTER, PROGRESS_CLUSTER_COMMAND_VACUUM_FULL, PROGRESS_COMMAND_CLUSTER, RelationData::rd_rel, rebuild_relation(), relation_close(), RELATION_IS_OTHER_TEMP, RelationIsPopulated, ReleaseSysCache(), RELOID, SearchSysCache1(), SearchSysCacheExists1, TransferPredicateLocksToHeapRelation(), try_relation_open(), and verbose.

Referenced by cluster(), and vacuum_rel().

267 {
268  Relation OldHeap;
269  bool verbose = ((options & CLUOPT_VERBOSE) != 0);
270  bool recheck = ((options & CLUOPT_RECHECK) != 0);
271 
272  /* Check for user-requested abort. */
274 
276  if (OidIsValid(indexOid))
279  else
282 
283  /*
284  * We grab exclusive access to the target rel and index for the duration
285  * of the transaction. (This is redundant for the single-transaction
286  * case, since cluster() already did it.) The index lock is taken inside
287  * check_index_is_clusterable.
288  */
289  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
290 
291  /* If the table has gone away, we can skip processing it */
292  if (!OldHeap)
293  {
295  return;
296  }
297 
298  /*
299  * Since we may open a new transaction for each relation, we have to check
300  * that the relation still is what we think it is.
301  *
302  * If this is a single-transaction CLUSTER, we can skip these tests. We
303  * *must* skip the one on indisclustered since it would reject an attempt
304  * to cluster a not-previously-clustered index.
305  */
306  if (recheck)
307  {
308  HeapTuple tuple;
309  Form_pg_index indexForm;
310 
311  /* Check that the user still owns the relation */
312  if (!pg_class_ownercheck(tableOid, GetUserId()))
313  {
316  return;
317  }
318 
319  /*
320  * Silently skip a temp table for a remote session. Only doing this
321  * check in the "recheck" case is appropriate (which currently means
322  * somebody is executing a database-wide CLUSTER), because there is
323  * another check in cluster() which will stop any attempt to cluster
324  * remote temp tables by name. There is another check in cluster_rel
325  * which is redundant, but we leave it for extra safety.
326  */
327  if (RELATION_IS_OTHER_TEMP(OldHeap))
328  {
331  return;
332  }
333 
334  if (OidIsValid(indexOid))
335  {
336  /*
337  * Check that the index still exists
338  */
340  {
343  return;
344  }
345 
346  /*
347  * Check that the index is still the one with indisclustered set.
348  */
349  tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
350  if (!HeapTupleIsValid(tuple)) /* probably can't happen */
351  {
354  return;
355  }
356  indexForm = (Form_pg_index) GETSTRUCT(tuple);
357  if (!indexForm->indisclustered)
358  {
359  ReleaseSysCache(tuple);
362  return;
363  }
364  ReleaseSysCache(tuple);
365  }
366  }
367 
368  /*
369  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
370  * would work in most respects, but the index would only get marked as
371  * indisclustered in the current database, leading to unexpected behavior
372  * if CLUSTER were later invoked in another database.
373  */
374  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
375  ereport(ERROR,
376  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
377  errmsg("cannot cluster a shared catalog")));
378 
379  /*
380  * Don't process temp tables of other backends ... their local buffer
381  * manager is not going to cope.
382  */
383  if (RELATION_IS_OTHER_TEMP(OldHeap))
384  {
385  if (OidIsValid(indexOid))
386  ereport(ERROR,
387  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
388  errmsg("cannot cluster temporary tables of other sessions")));
389  else
390  ereport(ERROR,
391  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
392  errmsg("cannot vacuum temporary tables of other sessions")));
393  }
394 
395  /*
396  * Also check for active uses of the relation in the current transaction,
397  * including open scans and pending AFTER trigger events.
398  */
399  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
400 
401  /* Check heap and index are valid to cluster on */
402  if (OidIsValid(indexOid))
403  check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
404 
405  /*
406  * Quietly ignore the request if this is a materialized view which has not
407  * been populated from its query. No harm is done because there is no data
408  * to deal with, and we don't want to throw an error if this is part of a
409  * multi-relation request -- for example, CLUSTER was run on the entire
410  * database.
411  */
412  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
413  !RelationIsPopulated(OldHeap))
414  {
417  return;
418  }
419 
420  /*
421  * All predicate locks on the tuples or pages are about to be made
422  * invalid, because we move tuples around. Promote them to relation
423  * locks. Predicate locks on indexes will be promoted when they are
424  * reindexed.
425  */
427 
428  /* rebuild_relation does all the dirty work */
429  rebuild_relation(OldHeap, indexOid, verbose);
430 
431  /* NB: rebuild_relation does table_close() on OldHeap */
432 
434 }
#define RelationIsPopulated(relation)
Definition: rel.h:565
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
Oid GetUserId(void)
Definition: miscinit.c:380
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
Definition: pgstat.c:3199
void pgstat_progress_update_param(int index, int64 val)
Definition: pgstat.c:3220
int errcode(int sqlerrcode)
Definition: elog.c:570
Form_pg_class rd_rel
Definition: rel.h:83
#define OidIsValid(objectId)
Definition: c.h:638
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:183
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:588
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:58
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:57
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3415
#define ereport(elevel, rest)
Definition: elog.h:141
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:38
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3097
static int verbose
Definition: pg_basebackup.c:94
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
void pgstat_progress_end_command(void)
Definition: pgstat.c:3271
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:546
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4755
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:784
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
Definition: cluster.c:445
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99

◆ 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,
TransactionId  frozenXid,
MultiXactId  minMulti,
char  newrelpersistence 
)

Definition at line 1341 of file cluster.c.

References AccessShareLock, CacheInvalidateCatalog(), CatalogTupleUpdate(), DROP_RESTRICT, elog, ERROR, GETSTRUCT, HeapTupleIsValid, i, NAMEDATALEN, NoLock, ObjectIdGetDatum, OidIsValid, PERFORM_DELETION_INTERNAL, performDeletion(), pgstat_progress_update_param(), PROGRESS_CLUSTER_PHASE, PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP, PROGRESS_CLUSTER_PHASE_REBUILD_INDEX, PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES, RelationData::rd_rel, 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(), RELOID, RenameRelationInternal(), RowExclusiveLock, SearchSysCacheCopy1, snprintf, swap_relation_files(), HeapTupleData::t_self, table_close(), table_open(), and toast_get_valid_index().

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

1349 {
1350  ObjectAddress object;
1351  Oid mapped_tables[4];
1352  int reindex_flags;
1353  int i;
1354 
1355  /* Report that we are now swapping relation files */
1358 
1359  /* Zero out possible results from swapped_relation_files */
1360  memset(mapped_tables, 0, sizeof(mapped_tables));
1361 
1362  /*
1363  * Swap the contents of the heap relations (including any toast tables).
1364  * Also set old heap's relfrozenxid to frozenXid.
1365  */
1366  swap_relation_files(OIDOldHeap, OIDNewHeap,
1367  (OIDOldHeap == RelationRelationId),
1368  swap_toast_by_content, is_internal,
1369  frozenXid, cutoffMulti, mapped_tables);
1370 
1371  /*
1372  * If it's a system catalog, queue a sinval message to flush all catcaches
1373  * on the catalog when we reach CommandCounterIncrement.
1374  */
1375  if (is_system_catalog)
1376  CacheInvalidateCatalog(OIDOldHeap);
1377 
1378  /*
1379  * Rebuild each index on the relation (but not the toast table, which is
1380  * all-new at this point). It is important to do this before the DROP
1381  * step because if we are processing a system catalog that will be used
1382  * during DROP, we want to have its indexes available. There is no
1383  * advantage to the other order anyway because this is all transactional,
1384  * so no chance to reclaim disk space before commit. We do not need a
1385  * final CommandCounterIncrement() because reindex_relation does it.
1386  *
1387  * Note: because index_build is called via reindex_relation, it will never
1388  * set indcheckxmin true for the indexes. This is OK even though in some
1389  * sense we are building new indexes rather than rebuilding existing ones,
1390  * because the new heap won't contain any HOT chains at all, let alone
1391  * broken ones, so it can't be necessary to set indcheckxmin.
1392  */
1393  reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE;
1394  if (check_constraints)
1395  reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS;
1396 
1397  /*
1398  * Ensure that the indexes have the same persistence as the parent
1399  * relation.
1400  */
1401  if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1402  reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1403  else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1404  reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
1405 
1406  /* Report that we are now reindexing relations */
1409 
1410  reindex_relation(OIDOldHeap, reindex_flags, 0);
1411 
1412  /* Report that we are now doing clean up */
1415 
1416  /*
1417  * If the relation being rebuild is pg_class, swap_relation_files()
1418  * couldn't update pg_class's own pg_class entry (check comments in
1419  * swap_relation_files()), thus relfrozenxid was not updated. That's
1420  * annoying because a potential reason for doing a VACUUM FULL is a
1421  * imminent or actual anti-wraparound shutdown. So, now that we can
1422  * access the new relation using its indices, update relfrozenxid.
1423  * pg_class doesn't have a toast relation, so we don't need to update the
1424  * corresponding toast relation. Not that there's little point moving all
1425  * relfrozenxid updates here since swap_relation_files() needs to write to
1426  * pg_class for non-mapped relations anyway.
1427  */
1428  if (OIDOldHeap == RelationRelationId)
1429  {
1430  Relation relRelation;
1431  HeapTuple reltup;
1432  Form_pg_class relform;
1433 
1434  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1435 
1436  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
1437  if (!HeapTupleIsValid(reltup))
1438  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1439  relform = (Form_pg_class) GETSTRUCT(reltup);
1440 
1441  relform->relfrozenxid = frozenXid;
1442  relform->relminmxid = cutoffMulti;
1443 
1444  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1445 
1446  table_close(relRelation, RowExclusiveLock);
1447  }
1448 
1449  /* Destroy new heap with old filenode */
1450  object.classId = RelationRelationId;
1451  object.objectId = OIDNewHeap;
1452  object.objectSubId = 0;
1453 
1454  /*
1455  * The new relation is local to our transaction and we know nothing
1456  * depends on it, so DROP_RESTRICT should be OK.
1457  */
1459 
1460  /* performDeletion does CommandCounterIncrement at end */
1461 
1462  /*
1463  * Now we must remove any relation mapping entries that we set up for the
1464  * transient table, as well as its toast table and toast index if any. If
1465  * we fail to do this before commit, the relmapper will complain about new
1466  * permanent map entries being added post-bootstrap.
1467  */
1468  for (i = 0; OidIsValid(mapped_tables[i]); i++)
1469  RelationMapRemoveMapping(mapped_tables[i]);
1470 
1471  /*
1472  * At this point, everything is kosher except that, if we did toast swap
1473  * by links, the toast table's name corresponds to the transient table.
1474  * The name is irrelevant to the backend because it's referenced by OID,
1475  * but users looking at the catalogs could be confused. Rename it to
1476  * prevent this problem.
1477  *
1478  * Note no lock required on the relation, because we already hold an
1479  * exclusive lock on it.
1480  */
1481  if (!swap_toast_by_content)
1482  {
1483  Relation newrel;
1484 
1485  newrel = table_open(OIDOldHeap, NoLock);
1486  if (OidIsValid(newrel->rd_rel->reltoastrelid))
1487  {
1488  Oid toastidx;
1489  char NewToastName[NAMEDATALEN];
1490 
1491  /* Get the associated valid index to be renamed */
1492  toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
1493  AccessShareLock);
1494 
1495  /* rename the toast table ... */
1496  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
1497  OIDOldHeap);
1498  RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1499  NewToastName, true, false);
1500 
1501  /* ... and its valid index too. */
1502  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
1503  OIDOldHeap);
1504 
1505  RenameRelationInternal(toastidx,
1506  NewToastName, true, true);
1507  }
1508  relation_close(newrel, NoLock);
1509  }
1510 
1511  /* if it's not a catalog table, clear any missing attribute settings */
1512  if (!is_system_catalog)
1513  {
1514  Relation newrel;
1515 
1516  newrel = table_open(OIDOldHeap, NoLock);
1517  RelationClearMissing(newrel);
1518  relation_close(newrel, NoLock);
1519  }
1520 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
void pgstat_progress_update_param(int index, int64 val)
Definition: pgstat.c:3220
#define AccessShareLock
Definition: lockdefs.h:36
#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES
Definition: progress.h:52
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition: index.h:137
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:315
void RelationClearMissing(Relation rel)
Definition: heap.c:1997
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: cluster.c:1002
void CacheInvalidateCatalog(Oid catalogId)
Definition: inval.c:1246
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition: index.h:139
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
void RelationMapRemoveMapping(Oid relationId)
Definition: relmapper.c:373
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:138
#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP
Definition: progress.h:54
FormData_pg_class * Form_pg_class
Definition: pg_class.h:150
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:174
#define elog(elevel,...)
Definition: elog.h:226
int i
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition: index.h:140
#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX
Definition: progress.h:53
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:3313
#define PROGRESS_CLUSTER_PHASE
Definition: progress.h:39
bool reindex_relation(Oid relid, int flags, int options)
Definition: index.c:3535
#define snprintf
Definition: port.h:192
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:134

◆ make_new_heap()

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

Definition at line 641 of file cluster.c.

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

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

643 {
644  TupleDesc OldHeapDesc;
645  char NewHeapName[NAMEDATALEN];
646  Oid OIDNewHeap;
647  Oid toastid;
648  Relation OldHeap;
649  HeapTuple tuple;
650  Datum reloptions;
651  bool isNull;
652  Oid namespaceid;
653 
654  OldHeap = table_open(OIDOldHeap, lockmode);
655  OldHeapDesc = RelationGetDescr(OldHeap);
656 
657  /*
658  * Note that the NewHeap will not receive any of the defaults or
659  * constraints associated with the OldHeap; we don't need 'em, and there's
660  * no reason to spend cycles inserting them into the catalogs only to
661  * delete them.
662  */
663 
664  /*
665  * But we do want to use reloptions of the old heap for new heap.
666  */
667  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
668  if (!HeapTupleIsValid(tuple))
669  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
670  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
671  &isNull);
672  if (isNull)
673  reloptions = (Datum) 0;
674 
675  if (relpersistence == RELPERSISTENCE_TEMP)
676  namespaceid = LookupCreationNamespace("pg_temp");
677  else
678  namespaceid = RelationGetNamespace(OldHeap);
679 
680  /*
681  * Create the new heap, using a temporary name in the same namespace as
682  * the existing table. NOTE: there is some risk of collision with user
683  * relnames. Working around this seems more trouble than it's worth; in
684  * particular, we can't create the new heap in a different namespace from
685  * the old, or we will have problems with the TEMP status of temp tables.
686  *
687  * Note: the new heap is not a shared relation, even if we are rebuilding
688  * a shared rel. However, we do make the new heap mapped if the source is
689  * mapped. This simplifies swap_relation_files, and is absolutely
690  * necessary for rebuilding pg_class, for reasons explained there.
691  */
692  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
693 
694  OIDNewHeap = heap_create_with_catalog(NewHeapName,
695  namespaceid,
696  NewTableSpace,
697  InvalidOid,
698  InvalidOid,
699  InvalidOid,
700  OldHeap->rd_rel->relowner,
701  OldHeap->rd_rel->relam,
702  OldHeapDesc,
703  NIL,
704  RELKIND_RELATION,
706  false,
707  RelationIsMapped(OldHeap),
709  reloptions,
710  false,
711  true,
712  true,
713  OIDOldHeap,
714  NULL);
715  Assert(OIDNewHeap != InvalidOid);
716 
717  ReleaseSysCache(tuple);
718 
719  /*
720  * Advance command counter so that the newly-created relation's catalog
721  * tuples will be visible to table_open.
722  */
724 
725  /*
726  * If necessary, create a TOAST table for the new relation.
727  *
728  * If the relation doesn't have a TOAST table already, we can't need one
729  * for the new relation. The other way around is possible though: if some
730  * wide columns have been dropped, NewHeapCreateToastTable can decide that
731  * no TOAST table is needed for the new table.
732  *
733  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
734  * that the TOAST table will be visible for insertion.
735  */
736  toastid = OldHeap->rd_rel->reltoastrelid;
737  if (OidIsValid(toastid))
738  {
739  /* keep the existing toast table's reloptions, if any */
740  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
741  if (!HeapTupleIsValid(tuple))
742  elog(ERROR, "cache lookup failed for relation %u", toastid);
743  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
744  &isNull);
745  if (isNull)
746  reloptions = (Datum) 0;
747 
748  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode);
749 
750  ReleaseSysCache(tuple);
751  }
752 
753  table_close(OldHeap, NoLock);
754 
755  return OIDNewHeap;
756 }
#define NIL
Definition: pg_list.h:65
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define RelationGetDescr(relation)
Definition: rel.h:442
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2928
Form_pg_class rd_rel
Definition: rel.h:83
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
char relpersistence
Definition: pg_class.h:78
#define NoLock
Definition: lockdefs.h:34
#define RelationIsMapped(relation)
Definition: rel.h:465
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
uintptr_t Datum
Definition: postgres.h:367
void CommandCounterIncrement(void)
Definition: xact.c:1003
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1385
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:732
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:1065
#define elog(elevel,...)
Definition: elog.h:226
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define snprintf
Definition: port.h:192
#define RelationGetNamespace(relation)
Definition: rel.h:457
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:66

◆ mark_index_clustered()

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

Definition at line 505 of file cluster.c.

References CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, INDEXRELID, InvalidOid, InvokeObjectPostAlterHookArg, lfirst_oid, ObjectIdGetDatum, OidIsValid, RelationData::rd_rel, RelationGetIndexList(), ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

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

506 {
507  HeapTuple indexTuple;
508  Form_pg_index indexForm;
509  Relation pg_index;
510  ListCell *index;
511 
512  /* Disallow applying to a partitioned table */
513  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
514  ereport(ERROR,
515  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
516  errmsg("cannot mark index clustered in partitioned table")));
517 
518  /*
519  * If the index is already marked clustered, no need to do anything.
520  */
521  if (OidIsValid(indexOid))
522  {
523  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
524  if (!HeapTupleIsValid(indexTuple))
525  elog(ERROR, "cache lookup failed for index %u", indexOid);
526  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
527 
528  if (indexForm->indisclustered)
529  {
530  ReleaseSysCache(indexTuple);
531  return;
532  }
533 
534  ReleaseSysCache(indexTuple);
535  }
536 
537  /*
538  * Check each index of the relation and set/clear the bit as needed.
539  */
540  pg_index = table_open(IndexRelationId, RowExclusiveLock);
541 
542  foreach(index, RelationGetIndexList(rel))
543  {
544  Oid thisIndexOid = lfirst_oid(index);
545 
546  indexTuple = SearchSysCacheCopy1(INDEXRELID,
547  ObjectIdGetDatum(thisIndexOid));
548  if (!HeapTupleIsValid(indexTuple))
549  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
550  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
551 
552  /*
553  * Unset the bit if set. We know it's wrong because we checked this
554  * earlier.
555  */
556  if (indexForm->indisclustered)
557  {
558  indexForm->indisclustered = false;
559  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
560  }
561  else if (thisIndexOid == indexOid)
562  {
563  /* this was checked earlier, but let's be real sure */
564  if (!indexForm->indisvalid)
565  elog(ERROR, "cannot cluster on invalid index %u", indexOid);
566  indexForm->indisclustered = true;
567  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
568  }
569 
570  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
571  InvalidOid, is_internal);
572 
573  heap_freetuple(indexTuple);
574  }
575 
576  table_close(pg_index, RowExclusiveLock);
577 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
int errcode(int sqlerrcode)
Definition: elog.c:570
Form_pg_class rd_rel
Definition: rel.h:83
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:166
Definition: type.h:89
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
#define ereport(elevel, rest)
Definition: elog.h:141
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1124
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4348
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:174
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define lfirst_oid(lc)
Definition: pg_list.h:192