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, bool recheck, bool verbose)
 
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 426 of file cluster.c.

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

Referenced by ATExecClusterOn(), and cluster_rel().

427 {
428  Relation OldIndex;
429 
430  OldIndex = index_open(indexOid, lockmode);
431 
432  /*
433  * Check that index is in fact an index on the given relation
434  */
435  if (OldIndex->rd_index == NULL ||
436  OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
437  ereport(ERROR,
438  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
439  errmsg("\"%s\" is not an index for table \"%s\"",
440  RelationGetRelationName(OldIndex),
441  RelationGetRelationName(OldHeap))));
442 
443  /* Index AM must allow clustering */
444  if (!OldIndex->rd_amroutine->amclusterable)
445  ereport(ERROR,
446  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
447  errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
448  RelationGetRelationName(OldIndex))));
449 
450  /*
451  * Disallow clustering on incomplete indexes (those that might not index
452  * every row of the relation). We could relax this by making a separate
453  * seqscan pass over the table to copy the missing rows, but that seems
454  * expensive and tedious.
455  */
456  if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
457  ereport(ERROR,
458  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
459  errmsg("cannot cluster on partial index \"%s\"",
460  RelationGetRelationName(OldIndex))));
461 
462  /*
463  * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
464  * it might well not contain entries for every heap row, or might not even
465  * be internally consistent. (But note that we don't check indcheckxmin;
466  * the worst consequence of following broken HOT chains would be that we
467  * might put recently-dead tuples out-of-order in the new table, and there
468  * is little harm in that.)
469  */
470  if (!IndexIsValid(OldIndex->rd_index))
471  ereport(ERROR,
472  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
473  errmsg("cannot cluster on invalid index \"%s\"",
474  RelationGetRelationName(OldIndex))));
475 
476  /* Drop relcache refcnt on OldIndex, but keep lock */
477  index_close(OldIndex, NoLock);
478 }
#define IndexIsValid(indexForm)
Definition: pg_index.h:85
int errcode(int sqlerrcode)
Definition: elog.c:575
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:397
struct IndexAmRoutine * rd_amroutine
Definition: rel.h:153
struct HeapTupleData * rd_indextuple
Definition: rel.h:133
Form_pg_index rd_index
Definition: rel.h:131
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:441
#define ereport(elevel, rest)
Definition: elog.h:122
bool amclusterable
Definition: amapi.h:190
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:176
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define RelationGetRelid(relation)
Definition: rel.h:407
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:150

◆ cluster()

void cluster ( ClusterStmt stmt,
bool  isTopLevel 
)

Definition at line 106 of file cluster.c.

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

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

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

◆ cluster_rel()

void cluster_rel ( Oid  tableOid,
Oid  indexOid,
bool  recheck,
bool  verbose 
)

Definition at line 268 of file cluster.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, GetUserId(), HeapTupleIsValid, INDEXRELID, ObjectIdGetDatum, OidIsValid, pg_class_ownercheck(), RelationData::rd_rel, rebuild_relation(), relation_close(), RELATION_IS_OTHER_TEMP, RelationIsPopulated, ReleaseSysCache(), RELOID, SearchSysCache1(), SearchSysCacheExists1, TransferPredicateLocksToHeapRelation(), and try_relation_open().

Referenced by cluster(), and vacuum_rel().

269 {
270  Relation OldHeap;
271 
272  /* Check for user-requested abort. */
274 
275  /*
276  * We grab exclusive access to the target rel and index for the duration
277  * of the transaction. (This is redundant for the single-transaction
278  * case, since cluster() already did it.) The index lock is taken inside
279  * check_index_is_clusterable.
280  */
281  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
282 
283  /* If the table has gone away, we can skip processing it */
284  if (!OldHeap)
285  return;
286 
287  /*
288  * Since we may open a new transaction for each relation, we have to check
289  * that the relation still is what we think it is.
290  *
291  * If this is a single-transaction CLUSTER, we can skip these tests. We
292  * *must* skip the one on indisclustered since it would reject an attempt
293  * to cluster a not-previously-clustered index.
294  */
295  if (recheck)
296  {
297  HeapTuple tuple;
298  Form_pg_index indexForm;
299 
300  /* Check that the user still owns the relation */
301  if (!pg_class_ownercheck(tableOid, GetUserId()))
302  {
304  return;
305  }
306 
307  /*
308  * Silently skip a temp table for a remote session. Only doing this
309  * check in the "recheck" case is appropriate (which currently means
310  * somebody is executing a database-wide CLUSTER), because there is
311  * another check in cluster() which will stop any attempt to cluster
312  * remote temp tables by name. There is another check in cluster_rel
313  * which is redundant, but we leave it for extra safety.
314  */
315  if (RELATION_IS_OTHER_TEMP(OldHeap))
316  {
318  return;
319  }
320 
321  if (OidIsValid(indexOid))
322  {
323  /*
324  * Check that the index still exists
325  */
327  {
329  return;
330  }
331 
332  /*
333  * Check that the index is still the one with indisclustered set.
334  */
335  tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
336  if (!HeapTupleIsValid(tuple)) /* probably can't happen */
337  {
339  return;
340  }
341  indexForm = (Form_pg_index) GETSTRUCT(tuple);
342  if (!indexForm->indisclustered)
343  {
344  ReleaseSysCache(tuple);
346  return;
347  }
348  ReleaseSysCache(tuple);
349  }
350  }
351 
352  /*
353  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
354  * would work in most respects, but the index would only get marked as
355  * indisclustered in the current database, leading to unexpected behavior
356  * if CLUSTER were later invoked in another database.
357  */
358  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
359  ereport(ERROR,
360  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
361  errmsg("cannot cluster a shared catalog")));
362 
363  /*
364  * Don't process temp tables of other backends ... their local buffer
365  * manager is not going to cope.
366  */
367  if (RELATION_IS_OTHER_TEMP(OldHeap))
368  {
369  if (OidIsValid(indexOid))
370  ereport(ERROR,
371  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
372  errmsg("cannot cluster temporary tables of other sessions")));
373  else
374  ereport(ERROR,
375  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
376  errmsg("cannot vacuum temporary tables of other sessions")));
377  }
378 
379  /*
380  * Also check for active uses of the relation in the current transaction,
381  * including open scans and pending AFTER trigger events.
382  */
383  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
384 
385  /* Check heap and index are valid to cluster on */
386  if (OidIsValid(indexOid))
387  check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
388 
389  /*
390  * Quietly ignore the request if this is a materialized view which has not
391  * been populated from its query. No harm is done because there is no data
392  * to deal with, and we don't want to throw an error if this is part of a
393  * multi-relation request -- for example, CLUSTER was run on the entire
394  * database.
395  */
396  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
397  !RelationIsPopulated(OldHeap))
398  {
400  return;
401  }
402 
403  /*
404  * All predicate locks on the tuples or pages are about to be made
405  * invalid, because we move tuples around. Promote them to relation
406  * locks. Predicate locks on indexes will be promoted when they are
407  * reindexed.
408  */
410 
411  /* rebuild_relation does all the dirty work */
412  rebuild_relation(OldHeap, indexOid, verbose);
413 
414  /* NB: rebuild_relation does heap_close() on OldHeap */
415 }
#define RelationIsPopulated(relation)
Definition: rel.h:557
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1157
Oid GetUserId(void)
Definition: miscinit.c:379
int errcode(int sqlerrcode)
Definition: elog.c:575
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1270
Form_pg_class rd_rel
Definition: rel.h:84
#define OidIsValid(objectId)
Definition: c.h:605
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:182
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#define ERROR
Definition: elog.h:43
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:569
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3209
#define ereport(elevel, rest)
Definition: elog.h:122
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3044
static int verbose
Definition: pg_basebackup.c:88
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:538
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4751
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:797
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
Definition: cluster.c:426
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98

◆ 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 1515 of file cluster.c.

References AccessShareLock, CacheInvalidateCatalog(), CatalogTupleUpdate(), DROP_RESTRICT, elog, ERROR, GETSTRUCT, heap_close, heap_open(), HeapTupleIsValid, i, NAMEDATALEN, NoLock, ObjectIdGetDatum, OidIsValid, PERFORM_DELETION_INTERNAL, performDeletion(), 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, and toast_get_valid_index().

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

1523 {
1524  ObjectAddress object;
1525  Oid mapped_tables[4];
1526  int reindex_flags;
1527  int i;
1528 
1529  /* Zero out possible results from swapped_relation_files */
1530  memset(mapped_tables, 0, sizeof(mapped_tables));
1531 
1532  /*
1533  * Swap the contents of the heap relations (including any toast tables).
1534  * Also set old heap's relfrozenxid to frozenXid.
1535  */
1536  swap_relation_files(OIDOldHeap, OIDNewHeap,
1537  (OIDOldHeap == RelationRelationId),
1538  swap_toast_by_content, is_internal,
1539  frozenXid, cutoffMulti, mapped_tables);
1540 
1541  /*
1542  * If it's a system catalog, queue a sinval message to flush all catcaches
1543  * on the catalog when we reach CommandCounterIncrement.
1544  */
1545  if (is_system_catalog)
1546  CacheInvalidateCatalog(OIDOldHeap);
1547 
1548  /*
1549  * Rebuild each index on the relation (but not the toast table, which is
1550  * all-new at this point). It is important to do this before the DROP
1551  * step because if we are processing a system catalog that will be used
1552  * during DROP, we want to have its indexes available. There is no
1553  * advantage to the other order anyway because this is all transactional,
1554  * so no chance to reclaim disk space before commit. We do not need a
1555  * final CommandCounterIncrement() because reindex_relation does it.
1556  *
1557  * Note: because index_build is called via reindex_relation, it will never
1558  * set indcheckxmin true for the indexes. This is OK even though in some
1559  * sense we are building new indexes rather than rebuilding existing ones,
1560  * because the new heap won't contain any HOT chains at all, let alone
1561  * broken ones, so it can't be necessary to set indcheckxmin.
1562  */
1563  reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE;
1564  if (check_constraints)
1565  reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS;
1566 
1567  /*
1568  * Ensure that the indexes have the same persistence as the parent
1569  * relation.
1570  */
1571  if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1572  reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1573  else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1574  reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
1575 
1576  reindex_relation(OIDOldHeap, reindex_flags, 0);
1577 
1578  /*
1579  * If the relation being rebuild is pg_class, swap_relation_files()
1580  * couldn't update pg_class's own pg_class entry (check comments in
1581  * swap_relation_files()), thus relfrozenxid was not updated. That's
1582  * annoying because a potential reason for doing a VACUUM FULL is a
1583  * imminent or actual anti-wraparound shutdown. So, now that we can
1584  * access the new relation using it's indices, update relfrozenxid.
1585  * pg_class doesn't have a toast relation, so we don't need to update the
1586  * corresponding toast relation. Not that there's little point moving all
1587  * relfrozenxid updates here since swap_relation_files() needs to write to
1588  * pg_class for non-mapped relations anyway.
1589  */
1590  if (OIDOldHeap == RelationRelationId)
1591  {
1592  Relation relRelation;
1593  HeapTuple reltup;
1594  Form_pg_class relform;
1595 
1596  relRelation = heap_open(RelationRelationId, RowExclusiveLock);
1597 
1598  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
1599  if (!HeapTupleIsValid(reltup))
1600  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1601  relform = (Form_pg_class) GETSTRUCT(reltup);
1602 
1603  relform->relfrozenxid = frozenXid;
1604  relform->relminmxid = cutoffMulti;
1605 
1606  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1607 
1608  heap_close(relRelation, RowExclusiveLock);
1609  }
1610 
1611  /* Destroy new heap with old filenode */
1612  object.classId = RelationRelationId;
1613  object.objectId = OIDNewHeap;
1614  object.objectSubId = 0;
1615 
1616  /*
1617  * The new relation is local to our transaction and we know nothing
1618  * depends on it, so DROP_RESTRICT should be OK.
1619  */
1621 
1622  /* performDeletion does CommandCounterIncrement at end */
1623 
1624  /*
1625  * Now we must remove any relation mapping entries that we set up for the
1626  * transient table, as well as its toast table and toast index if any. If
1627  * we fail to do this before commit, the relmapper will complain about new
1628  * permanent map entries being added post-bootstrap.
1629  */
1630  for (i = 0; OidIsValid(mapped_tables[i]); i++)
1631  RelationMapRemoveMapping(mapped_tables[i]);
1632 
1633  /*
1634  * At this point, everything is kosher except that, if we did toast swap
1635  * by links, the toast table's name corresponds to the transient table.
1636  * The name is irrelevant to the backend because it's referenced by OID,
1637  * but users looking at the catalogs could be confused. Rename it to
1638  * prevent this problem.
1639  *
1640  * Note no lock required on the relation, because we already hold an
1641  * exclusive lock on it.
1642  */
1643  if (!swap_toast_by_content)
1644  {
1645  Relation newrel;
1646 
1647  newrel = heap_open(OIDOldHeap, NoLock);
1648  if (OidIsValid(newrel->rd_rel->reltoastrelid))
1649  {
1650  Oid toastidx;
1651  char NewToastName[NAMEDATALEN];
1652 
1653  /* Get the associated valid index to be renamed */
1654  toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
1655  AccessShareLock);
1656 
1657  /* rename the toast table ... */
1658  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
1659  OIDOldHeap);
1660  RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1661  NewToastName, true);
1662 
1663  /* ... and its valid index too. */
1664  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
1665  OIDOldHeap);
1666 
1667  RenameRelationInternal(toastidx,
1668  NewToastName, true);
1669  }
1670  relation_close(newrel, NoLock);
1671  }
1672 
1673  /* if it's not a catalog table, clear any missing attribute settings */
1674  if (!is_system_catalog)
1675  {
1676  Relation newrel;
1677 
1678  newrel = heap_open(OIDOldHeap, NoLock);
1679  RelationClearMissing(newrel);
1680  relation_close(newrel, NoLock);
1681  }
1682 }
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal)
Definition: tablecmds.c:3112
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
Definition: tuptoaster.c:1429
#define AccessShareLock
Definition: lockdefs.h:36
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1270
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
#define heap_close(r, l)
Definition: heapam.h:97
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition: index.h:140
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:605
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#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:300
void RelationClearMissing(Relation rel)
Definition: heap.c:1944
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
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:1176
void CacheInvalidateCatalog(Oid catalogId)
Definition: inval.c:1209
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition: index.h:142
void RelationMapRemoveMapping(Oid relationId)
Definition: relmapper.c:357
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:211
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:141
FormData_pg_class * Form_pg_class
Definition: pg_class.h:93
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:173
int i
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition: index.h:143
#define elog
Definition: elog.h:219
bool reindex_relation(Oid relid, int flags, int options)
Definition: index.c:3834
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:189

◆ make_new_heap()

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

Definition at line 622 of file cluster.c.

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

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

624 {
625  TupleDesc OldHeapDesc;
626  char NewHeapName[NAMEDATALEN];
627  Oid OIDNewHeap;
628  Oid toastid;
629  Relation OldHeap;
630  HeapTuple tuple;
631  Datum reloptions;
632  bool isNull;
633  Oid namespaceid;
634 
635  OldHeap = heap_open(OIDOldHeap, lockmode);
636  OldHeapDesc = RelationGetDescr(OldHeap);
637 
638  /*
639  * Note that the NewHeap will not receive any of the defaults or
640  * constraints associated with the OldHeap; we don't need 'em, and there's
641  * no reason to spend cycles inserting them into the catalogs only to
642  * delete them.
643  */
644 
645  /*
646  * But we do want to use reloptions of the old heap for new heap.
647  */
648  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
649  if (!HeapTupleIsValid(tuple))
650  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
651  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
652  &isNull);
653  if (isNull)
654  reloptions = (Datum) 0;
655 
656  if (relpersistence == RELPERSISTENCE_TEMP)
657  namespaceid = LookupCreationNamespace("pg_temp");
658  else
659  namespaceid = RelationGetNamespace(OldHeap);
660 
661  /*
662  * Create the new heap, using a temporary name in the same namespace as
663  * the existing table. NOTE: there is some risk of collision with user
664  * relnames. Working around this seems more trouble than it's worth; in
665  * particular, we can't create the new heap in a different namespace from
666  * the old, or we will have problems with the TEMP status of temp tables.
667  *
668  * Note: the new heap is not a shared relation, even if we are rebuilding
669  * a shared rel. However, we do make the new heap mapped if the source is
670  * mapped. This simplifies swap_relation_files, and is absolutely
671  * necessary for rebuilding pg_class, for reasons explained there.
672  */
673  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
674 
675  OIDNewHeap = heap_create_with_catalog(NewHeapName,
676  namespaceid,
677  NewTableSpace,
678  InvalidOid,
679  InvalidOid,
680  InvalidOid,
681  OldHeap->rd_rel->relowner,
682  OldHeapDesc,
683  NIL,
684  RELKIND_RELATION,
686  false,
687  RelationIsMapped(OldHeap),
688  true,
689  0,
691  reloptions,
692  false,
693  true,
694  true,
695  OIDOldHeap,
696  NULL);
697  Assert(OIDNewHeap != InvalidOid);
698 
699  ReleaseSysCache(tuple);
700 
701  /*
702  * Advance command counter so that the newly-created relation's catalog
703  * tuples will be visible to heap_open.
704  */
706 
707  /*
708  * If necessary, create a TOAST table for the new relation.
709  *
710  * If the relation doesn't have a TOAST table already, we can't need one
711  * for the new relation. The other way around is possible though: if some
712  * wide columns have been dropped, NewHeapCreateToastTable can decide that
713  * no TOAST table is needed for the new table.
714  *
715  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
716  * that the TOAST table will be visible for insertion.
717  */
718  toastid = OldHeap->rd_rel->reltoastrelid;
719  if (OidIsValid(toastid))
720  {
721  /* keep the existing toast table's reloptions, if any */
722  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
723  if (!HeapTupleIsValid(tuple))
724  elog(ERROR, "cache lookup failed for relation %u", toastid);
725  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
726  &isNull);
727  if (isNull)
728  reloptions = (Datum) 0;
729 
730  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode);
731 
732  ReleaseSysCache(tuple);
733  }
734 
735  heap_close(OldHeap, NoLock);
736 
737  return OIDNewHeap;
738 }
#define NIL
Definition: pg_list.h:69
#define RelationGetDescr(relation)
Definition: rel.h:433
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2915
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:84
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:605
#define NAMEDATALEN
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, bool oidislocal, int oidinhcount, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition: heap.c:1027
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#define ERROR
Definition: elog.h:43
char relpersistence
Definition: pg_class.h:50
#define NoLock
Definition: lockdefs.h:34
#define RelationIsMapped(relation)
Definition: rel.h:458
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
uintptr_t Datum
Definition: postgres.h:367
void CommandCounterIncrement(void)
Definition: xact.c:914
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:699
#define elog
Definition: elog.h:219
#define RelationGetNamespace(relation)
Definition: rel.h:448
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:65

◆ mark_index_clustered()

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

Definition at line 486 of file cluster.c.

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

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

487 {
488  HeapTuple indexTuple;
489  Form_pg_index indexForm;
490  Relation pg_index;
491  ListCell *index;
492 
493  /* Disallow applying to a partitioned table */
494  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
495  ereport(ERROR,
496  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
497  errmsg("cannot mark index clustered in partitioned table")));
498 
499  /*
500  * If the index is already marked clustered, no need to do anything.
501  */
502  if (OidIsValid(indexOid))
503  {
504  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid));
505  if (!HeapTupleIsValid(indexTuple))
506  elog(ERROR, "cache lookup failed for index %u", indexOid);
507  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
508 
509  if (indexForm->indisclustered)
510  {
511  ReleaseSysCache(indexTuple);
512  return;
513  }
514 
515  ReleaseSysCache(indexTuple);
516  }
517 
518  /*
519  * Check each index of the relation and set/clear the bit as needed.
520  */
521  pg_index = heap_open(IndexRelationId, RowExclusiveLock);
522 
523  foreach(index, RelationGetIndexList(rel))
524  {
525  Oid thisIndexOid = lfirst_oid(index);
526 
527  indexTuple = SearchSysCacheCopy1(INDEXRELID,
528  ObjectIdGetDatum(thisIndexOid));
529  if (!HeapTupleIsValid(indexTuple))
530  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
531  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
532 
533  /*
534  * Unset the bit if set. We know it's wrong because we checked this
535  * earlier.
536  */
537  if (indexForm->indisclustered)
538  {
539  indexForm->indisclustered = false;
540  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
541  }
542  else if (thisIndexOid == indexOid)
543  {
544  /* this was checked earlier, but let's be real sure */
545  if (!IndexIsValid(indexForm))
546  elog(ERROR, "cannot cluster on invalid index %u", indexOid);
547  indexForm->indisclustered = true;
548  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
549  }
550 
551  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
552  InvalidOid, is_internal);
553 
554  heap_freetuple(indexTuple);
555  }
556 
557  heap_close(pg_index, RowExclusiveLock);
558 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
#define IndexIsValid(indexForm)
Definition: pg_index.h:85
int errcode(int sqlerrcode)
Definition: elog.c:575
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:84
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1773
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:605
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:166
Definition: type.h:89
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#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:122
FormData_pg_index * Form_pg_index
Definition: pg_index.h:66
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1294
#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:211
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4190
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:173
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define elog
Definition: elog.h:219
#define lfirst_oid(lc)
Definition: pg_list.h:108