PostgreSQL Source Code  git master
cluster.h File Reference
#include "nodes/parsenodes.h"
#include "parser/parse_node.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.

Data Structures

struct  ClusterParams
 

Macros

#define CLUOPT_RECHECK   0x01 /* recheck relation state */
 
#define CLUOPT_VERBOSE   0x02 /* print progress info */
 

Typedefs

typedef struct ClusterParams ClusterParams
 

Functions

void cluster (ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
 
void cluster_rel (Oid tableOid, Oid indexOid, ClusterParams *params)
 
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)
 

Macro Definition Documentation

◆ CLUOPT_RECHECK

#define CLUOPT_RECHECK   0x01 /* recheck relation state */

Definition at line 23 of file cluster.h.

Referenced by cluster(), and cluster_rel().

◆ CLUOPT_VERBOSE

#define CLUOPT_VERBOSE   0x02 /* print progress info */

Definition at line 24 of file cluster.h.

Referenced by cluster(), cluster_rel(), and vacuum_rel().

Typedef Documentation

◆ ClusterParams

typedef struct ClusterParams ClusterParams

Function Documentation

◆ check_index_is_clusterable()

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

Definition at line 443 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().

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

◆ cluster()

void cluster ( ParseState pstate,
ClusterStmt stmt,
bool  isTopLevel 
)

Definition at line 103 of file cluster.c.

References AccessExclusiveLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, CLUOPT_RECHECK, CLUOPT_VERBOSE, cluster_rel(), CommitTransactionCommand(), defGetBoolean(), DefElem::defname, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), get_relname_relid(), get_tables_to_cluster(), GetTransactionSnapshot(), ClusterStmt::indexname, RelToCluster::indexOid, InvalidOid, lfirst, lfirst_oid, DefElem::location, MemoryContextDelete(), NoLock, OidIsValid, ClusterParams::options, ClusterStmt::params, parser_errposition(), PopActiveSnapshot(), PortalContext, PreventInTransactionBlock(), PushActiveSnapshot(), RangeVarCallbackOwnsTable(), RangeVarGetRelidExtended(), RelationData::rd_rel, ClusterStmt::relation, RELATION_IS_OTHER_TEMP, RelationGetIndexList(), RangeVar::relname, StartTransactionCommand(), table_close(), table_open(), RelToCluster::tableOid, and verbose.

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

104 {
105  ListCell *lc;
106  ClusterParams params = {0};
107  bool verbose = false;
108 
109  /* Parse option list */
110  foreach(lc, stmt->params)
111  {
112  DefElem *opt = (DefElem *) lfirst(lc);
113 
114  if (strcmp(opt->defname, "verbose") == 0)
115  verbose = defGetBoolean(opt);
116  else
117  ereport(ERROR,
118  (errcode(ERRCODE_SYNTAX_ERROR),
119  errmsg("unrecognized CLUSTER option \"%s\"",
120  opt->defname),
121  parser_errposition(pstate, opt->location)));
122  }
123 
124  params.options = (verbose ? CLUOPT_VERBOSE : 0);
125 
126  if (stmt->relation != NULL)
127  {
128  /* This is the single-relation case. */
129  Oid tableOid,
130  indexOid = InvalidOid;
131  Relation rel;
132 
133  /* Find, lock, and check permissions on the table */
134  tableOid = RangeVarGetRelidExtended(stmt->relation,
136  0,
138  rel = table_open(tableOid, NoLock);
139 
140  /*
141  * Reject clustering a remote temp table ... their local buffer
142  * manager is not going to cope.
143  */
144  if (RELATION_IS_OTHER_TEMP(rel))
145  ereport(ERROR,
146  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
147  errmsg("cannot cluster temporary tables of other sessions")));
148 
149  /*
150  * Reject clustering a partitioned table.
151  */
152  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
153  ereport(ERROR,
154  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
155  errmsg("cannot cluster a partitioned table")));
156 
157  if (stmt->indexname == NULL)
158  {
159  ListCell *index;
160 
161  /* We need to find the index that has indisclustered set. */
162  foreach(index, RelationGetIndexList(rel))
163  {
164  indexOid = lfirst_oid(index);
165  if (get_index_isclustered(indexOid))
166  break;
167  indexOid = InvalidOid;
168  }
169 
170  if (!OidIsValid(indexOid))
171  ereport(ERROR,
172  (errcode(ERRCODE_UNDEFINED_OBJECT),
173  errmsg("there is no previously clustered index for table \"%s\"",
174  stmt->relation->relname)));
175  }
176  else
177  {
178  /*
179  * The index is expected to be in the same namespace as the
180  * relation.
181  */
182  indexOid = get_relname_relid(stmt->indexname,
183  rel->rd_rel->relnamespace);
184  if (!OidIsValid(indexOid))
185  ereport(ERROR,
186  (errcode(ERRCODE_UNDEFINED_OBJECT),
187  errmsg("index \"%s\" for table \"%s\" does not exist",
188  stmt->indexname, stmt->relation->relname)));
189  }
190 
191  /* close relation, keep lock till commit */
192  table_close(rel, NoLock);
193 
194  /* Do the job. */
195  cluster_rel(tableOid, indexOid, &params);
196  }
197  else
198  {
199  /*
200  * This is the "multi relation" case. We need to cluster all tables
201  * that have some index with indisclustered set.
202  */
203  MemoryContext cluster_context;
204  List *rvs;
205  ListCell *rv;
206 
207  /*
208  * We cannot run this form of CLUSTER inside a user transaction block;
209  * we'd be holding locks way too long.
210  */
211  PreventInTransactionBlock(isTopLevel, "CLUSTER");
212 
213  /*
214  * Create special memory context for cross-transaction storage.
215  *
216  * Since it is a child of PortalContext, it will go away even in case
217  * of error.
218  */
219  cluster_context = AllocSetContextCreate(PortalContext,
220  "Cluster",
222 
223  /*
224  * Build the list of relations to cluster. Note that this lives in
225  * cluster_context.
226  */
227  rvs = get_tables_to_cluster(cluster_context);
228 
229  /* Commit to get out of starting transaction */
232 
233  /* Ok, now that we've got them all, cluster them one by one */
234  foreach(rv, rvs)
235  {
236  RelToCluster *rvtc = (RelToCluster *) lfirst(rv);
237  ClusterParams cluster_params = params;
238 
239  /* Start a new transaction for each relation. */
241  /* functions in indexes may want a snapshot set */
243  /* Do the job. */
244  cluster_params.options |= CLUOPT_RECHECK;
245  cluster_rel(rvtc->tableOid, rvtc->indexOid,
246  &cluster_params);
249  }
250 
251  /* Start a new transaction for the cleanup work. */
253 
254  /* Clean up working storage */
255  MemoryContextDelete(cluster_context);
256  }
257 }
List * params
Definition: parsenodes.h:3250
void RangeVarCallbackOwnsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:15608
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:212
#define AllocSetContextCreate
Definition: memutils.h:170
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
void CommitTransactionCommand(void)
Definition: xact.c:2939
int errcode(int sqlerrcode)
Definition: elog.c:694
#define CLUOPT_VERBOSE
Definition: cluster.h:24
void PopActiveSnapshot(void)
Definition: snapmgr.c:759
Form_pg_class rd_rel
Definition: rel.h:110
unsigned int Oid
Definition: postgres_ext.h:31
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:250
#define OidIsValid(objectId)
Definition: c.h:710
Oid tableOid
Definition: cluster.c:66
MemoryContext PortalContext
Definition: mcxt.c:53
Definition: type.h:89
char * relname
Definition: primnodes.h:68
bool defGetBoolean(DefElem *def)
Definition: define.c:111
char * indexname
Definition: parsenodes.h:3249
#define ERROR
Definition: elog.h:45
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1856
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define NoLock
Definition: lockdefs.h:34
void PushActiveSnapshot(Snapshot snap)
Definition: snapmgr.c:680
int location
Definition: parsenodes.h:736
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3379
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
Definition: cluster.c:277
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:236
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1538
static int verbose
bits32 options
Definition: cluster.h:29
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:155
#define lfirst(lc)
Definition: pg_list.h:169
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:594
void StartTransactionCommand(void)
Definition: xact.c:2838
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3555
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4526
Oid indexOid
Definition: cluster.c:67
#define CLUOPT_RECHECK
Definition: cluster.h:23
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:905
char * defname
Definition: parsenodes.h:733
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
RangeVar * relation
Definition: parsenodes.h:3248
Definition: pg_list.h:50
#define lfirst_oid(lc)
Definition: pg_list.h:171

◆ cluster_rel()

void cluster_rel ( Oid  tableOid,
Oid  indexOid,
ClusterParams params 
)

Definition at line 277 of file cluster.c.

References AccessExclusiveLock, CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), CLUOPT_RECHECK, CLUOPT_VERBOSE, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), GetUserId(), ObjectIdGetDatum, OidIsValid, ClusterParams::options, 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, RELOID, SearchSysCacheExists1, TransferPredicateLocksToHeapRelation(), try_relation_open(), and verbose.

Referenced by cluster(), and vacuum_rel().

278 {
279  Relation OldHeap;
280  bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
281  bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
282 
283  /* Check for user-requested abort. */
285 
287  if (OidIsValid(indexOid))
290  else
293 
294  /*
295  * We grab exclusive access to the target rel and index for the duration
296  * of the transaction. (This is redundant for the single-transaction
297  * case, since cluster() already did it.) The index lock is taken inside
298  * check_index_is_clusterable.
299  */
300  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
301 
302  /* If the table has gone away, we can skip processing it */
303  if (!OldHeap)
304  {
306  return;
307  }
308 
309  /*
310  * Since we may open a new transaction for each relation, we have to check
311  * that the relation still is what we think it is.
312  *
313  * If this is a single-transaction CLUSTER, we can skip these tests. We
314  * *must* skip the one on indisclustered since it would reject an attempt
315  * to cluster a not-previously-clustered index.
316  */
317  if (recheck)
318  {
319  /* Check that the user still owns the relation */
320  if (!pg_class_ownercheck(tableOid, GetUserId()))
321  {
324  return;
325  }
326 
327  /*
328  * Silently skip a temp table for a remote session. Only doing this
329  * check in the "recheck" case is appropriate (which currently means
330  * somebody is executing a database-wide CLUSTER), because there is
331  * another check in cluster() which will stop any attempt to cluster
332  * remote temp tables by name. There is another check in cluster_rel
333  * which is redundant, but we leave it for extra safety.
334  */
335  if (RELATION_IS_OTHER_TEMP(OldHeap))
336  {
339  return;
340  }
341 
342  if (OidIsValid(indexOid))
343  {
344  /*
345  * Check that the index still exists
346  */
348  {
351  return;
352  }
353 
354  /*
355  * Check that the index is still the one with indisclustered set.
356  */
357  if (!get_index_isclustered(indexOid))
358  {
361  return;
362  }
363  }
364  }
365 
366  /*
367  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
368  * would work in most respects, but the index would only get marked as
369  * indisclustered in the current database, leading to unexpected behavior
370  * if CLUSTER were later invoked in another database.
371  */
372  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
373  ereport(ERROR,
374  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
375  errmsg("cannot cluster a shared catalog")));
376 
377  /*
378  * Don't process temp tables of other backends ... their local buffer
379  * manager is not going to cope.
380  */
381  if (RELATION_IS_OTHER_TEMP(OldHeap))
382  {
383  if (OidIsValid(indexOid))
384  ereport(ERROR,
385  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
386  errmsg("cannot cluster temporary tables of other sessions")));
387  else
388  ereport(ERROR,
389  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
390  errmsg("cannot vacuum temporary tables of other sessions")));
391  }
392 
393  /*
394  * Also check for active uses of the relation in the current transaction,
395  * including open scans and pending AFTER trigger events.
396  */
397  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
398 
399  /* Check heap and index are valid to cluster on */
400  if (OidIsValid(indexOid))
401  check_index_is_clusterable(OldHeap, indexOid, recheck, AccessExclusiveLock);
402 
403  /*
404  * Quietly ignore the request if this is a materialized view which has not
405  * been populated from its query. No harm is done because there is no data
406  * to deal with, and we don't want to throw an error if this is part of a
407  * multi-relation request -- for example, CLUSTER was run on the entire
408  * database.
409  */
410  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
411  !RelationIsPopulated(OldHeap))
412  {
415  return;
416  }
417 
418  /*
419  * All predicate locks on the tuples or pages are about to be made
420  * invalid, because we move tuples around. Promote them to relation
421  * locks. Predicate locks on indexes will be promoted when they are
422  * reindexed.
423  */
425 
426  /* rebuild_relation does all the dirty work */
427  rebuild_relation(OldHeap, indexOid, verbose);
428 
429  /* NB: rebuild_relation does table_close() on OldHeap */
430 
432 }
#define RelationIsPopulated(relation)
Definition: rel.h:613
Oid GetUserId(void)
Definition: miscinit.c:478
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
Definition: pgstat.c:3457
void pgstat_progress_update_param(int index, int64 val)
Definition: pgstat.c:3478
int errcode(int sqlerrcode)
Definition: elog.c:694
#define CLUOPT_VERBOSE
Definition: cluster.h:24
Form_pg_class rd_rel
Definition: rel.h:110
#define OidIsValid(objectId)
Definition: c.h:710
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:184
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:45
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:576
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:75
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:74
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:3699
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:55
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3146
static int verbose
void pgstat_progress_end_command(void)
Definition: pgstat.c:3529
bits32 options
Definition: cluster.h:29
#define ereport(elevel,...)
Definition: elog.h:155
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:594
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4706
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3555
#define CLUOPT_RECHECK
Definition: cluster.h:23
#define AccessExclusiveLock
Definition: lockdefs.h:45
int errmsg(const char *fmt,...)
Definition: elog.c:905
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode)
Definition: cluster.c:443
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:100

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

References 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().

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

◆ make_new_heap()

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

Definition at line 629 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().

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

◆ mark_index_clustered()

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

Definition at line 503 of file cluster.c.

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

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

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