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_VERBOSE   0x01 /* print progress info */
 
#define CLUOPT_RECHECK   0x02 /* recheck relation state */
 
#define CLUOPT_RECHECK_ISCLUSTERED
 

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, LOCKMODE lockmode)
 
void mark_index_clustered (Relation rel, Oid indexOid, bool is_internal)
 
Oid make_new_heap (Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, 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 cutoffMulti, char newrelpersistence)
 

Macro Definition Documentation

◆ CLUOPT_RECHECK

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

Definition at line 24 of file cluster.h.

◆ CLUOPT_RECHECK_ISCLUSTERED

#define CLUOPT_RECHECK_ISCLUSTERED
Value:
0x04 /* recheck relation state for
* indisclustered */

Definition at line 25 of file cluster.h.

◆ CLUOPT_VERBOSE

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

Definition at line 23 of file cluster.h.

Typedef Documentation

◆ ClusterParams

typedef struct ClusterParams ClusterParams

Function Documentation

◆ check_index_is_clusterable()

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

Definition at line 502 of file cluster.c.

503 {
504  Relation OldIndex;
505 
506  OldIndex = index_open(indexOid, lockmode);
507 
508  /*
509  * Check that index is in fact an index on the given relation
510  */
511  if (OldIndex->rd_index == NULL ||
512  OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
513  ereport(ERROR,
514  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
515  errmsg("\"%s\" is not an index for table \"%s\"",
516  RelationGetRelationName(OldIndex),
517  RelationGetRelationName(OldHeap))));
518 
519  /* Index AM must allow clustering */
520  if (!OldIndex->rd_indam->amclusterable)
521  ereport(ERROR,
522  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
523  errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
524  RelationGetRelationName(OldIndex))));
525 
526  /*
527  * Disallow clustering on incomplete indexes (those that might not index
528  * every row of the relation). We could relax this by making a separate
529  * seqscan pass over the table to copy the missing rows, but that seems
530  * expensive and tedious.
531  */
532  if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
533  ereport(ERROR,
534  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
535  errmsg("cannot cluster on partial index \"%s\"",
536  RelationGetRelationName(OldIndex))));
537 
538  /*
539  * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
540  * it might well not contain entries for every heap row, or might not even
541  * be internally consistent. (But note that we don't check indcheckxmin;
542  * the worst consequence of following broken HOT chains would be that we
543  * might put recently-dead tuples out-of-order in the new table, and there
544  * is little harm in that.)
545  */
546  if (!OldIndex->rd_index->indisvalid)
547  ereport(ERROR,
548  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
549  errmsg("cannot cluster on invalid index \"%s\"",
550  RelationGetRelationName(OldIndex))));
551 
552  /* Drop relcache refcnt on OldIndex, but keep lock */
553  index_close(OldIndex, NoLock);
554 }
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:359
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:158
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:132
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelid(relation)
Definition: rel.h:504
#define RelationGetRelationName(relation)
Definition: rel.h:538
bool amclusterable
Definition: amapi.h:238
struct IndexAmRoutine * rd_indam
Definition: rel.h:205
struct HeapTupleData * rd_indextuple
Definition: rel.h:193
Form_pg_index rd_index
Definition: rel.h:191

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(), cluster(), and cluster_rel().

◆ cluster()

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

Definition at line 111 of file cluster.c.

112 {
113  ListCell *lc;
114  ClusterParams params = {0};
115  bool verbose = false;
116  Relation rel = NULL;
117  Oid indexOid = InvalidOid;
118  MemoryContext cluster_context;
119  List *rtcs;
120 
121  /* Parse option list */
122  foreach(lc, stmt->params)
123  {
124  DefElem *opt = (DefElem *) lfirst(lc);
125 
126  if (strcmp(opt->defname, "verbose") == 0)
127  verbose = defGetBoolean(opt);
128  else
129  ereport(ERROR,
130  (errcode(ERRCODE_SYNTAX_ERROR),
131  errmsg("unrecognized CLUSTER option \"%s\"",
132  opt->defname),
133  parser_errposition(pstate, opt->location)));
134  }
135 
136  params.options = (verbose ? CLUOPT_VERBOSE : 0);
137 
138  if (stmt->relation != NULL)
139  {
140  /* This is the single-relation case. */
141  Oid tableOid;
142 
143  /*
144  * Find, lock, and check permissions on the table. We obtain
145  * AccessExclusiveLock right away to avoid lock-upgrade hazard in the
146  * single-transaction case.
147  */
148  tableOid = RangeVarGetRelidExtended(stmt->relation,
150  0,
152  NULL);
153  rel = table_open(tableOid, NoLock);
154 
155  /*
156  * Reject clustering a remote temp table ... their local buffer
157  * manager is not going to cope.
158  */
159  if (RELATION_IS_OTHER_TEMP(rel))
160  ereport(ERROR,
161  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
162  errmsg("cannot cluster temporary tables of other sessions")));
163 
164  if (stmt->indexname == NULL)
165  {
166  ListCell *index;
167 
168  /* We need to find the index that has indisclustered set. */
169  foreach(index, RelationGetIndexList(rel))
170  {
171  indexOid = lfirst_oid(index);
172  if (get_index_isclustered(indexOid))
173  break;
174  indexOid = InvalidOid;
175  }
176 
177  if (!OidIsValid(indexOid))
178  ereport(ERROR,
179  (errcode(ERRCODE_UNDEFINED_OBJECT),
180  errmsg("there is no previously clustered index for table \"%s\"",
181  stmt->relation->relname)));
182  }
183  else
184  {
185  /*
186  * The index is expected to be in the same namespace as the
187  * relation.
188  */
189  indexOid = get_relname_relid(stmt->indexname,
190  rel->rd_rel->relnamespace);
191  if (!OidIsValid(indexOid))
192  ereport(ERROR,
193  (errcode(ERRCODE_UNDEFINED_OBJECT),
194  errmsg("index \"%s\" for table \"%s\" does not exist",
195  stmt->indexname, stmt->relation->relname)));
196  }
197 
198  if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
199  {
200  /* close relation, keep lock till commit */
201  table_close(rel, NoLock);
202 
203  /* Do the job. */
204  cluster_rel(tableOid, indexOid, &params);
205 
206  return;
207  }
208  }
209 
210  /*
211  * By here, we know we are in a multi-table situation. In order to avoid
212  * holding locks for too long, we want to process each table in its own
213  * transaction. This forces us to disallow running inside a user
214  * transaction block.
215  */
216  PreventInTransactionBlock(isTopLevel, "CLUSTER");
217 
218  /* Also, we need a memory context to hold our list of relations */
219  cluster_context = AllocSetContextCreate(PortalContext,
220  "Cluster",
222 
223  /*
224  * Either we're processing a partitioned table, or we were not given any
225  * table name at all. In either case, obtain a list of relations to
226  * process.
227  *
228  * In the former case, an index name must have been given, so we don't
229  * need to recheck its "indisclustered" bit, but we have to check that it
230  * is an index that we can cluster on. In the latter case, we set the
231  * option bit to have indisclustered verified.
232  *
233  * Rechecking the relation itself is necessary here in all cases.
234  */
235  params.options |= CLUOPT_RECHECK;
236  if (rel != NULL)
237  {
238  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
240  rtcs = get_tables_to_cluster_partitioned(cluster_context, indexOid);
241 
242  /* close relation, releasing lock on parent table */
244  }
245  else
246  {
247  rtcs = get_tables_to_cluster(cluster_context);
249  }
250 
251  /* Do the job. */
252  cluster_multiple_rels(rtcs, &params);
253 
254  /* Start a new transaction for the cleanup work. */
256 
257  /* Clean up working storage */
258  MemoryContextDelete(cluster_context);
259 }
#define OidIsValid(objectId)
Definition: c.h:759
void cluster_rel(Oid tableOid, Oid indexOid, ClusterParams *params)
Definition: cluster.c:314
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:502
static List * get_tables_to_cluster(MemoryContext cluster_context)
Definition: cluster.c:1622
static List * get_tables_to_cluster_partitioned(MemoryContext cluster_context, Oid indexOid)
Definition: cluster.c:1676
static void cluster_multiple_rels(List *rtcs, ClusterParams *params)
Definition: cluster.c:269
#define CLUOPT_VERBOSE
Definition: cluster.h:23
#define CLUOPT_RECHECK_ISCLUSTERED
Definition: cluster.h:25
#define CLUOPT_RECHECK
Definition: cluster.h:24
bool defGetBoolean(DefElem *def)
Definition: define.c:108
#define stmt
Definition: indent_codes.h:59
int verbose
Assert(fmt[strlen(fmt) - 1] !='\n')
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define AccessShareLock
Definition: lockdefs.h:36
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3559
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1867
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
MemoryContext PortalContext
Definition: mcxt.c:150
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:239
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:111
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:659
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4739
bits32 options
Definition: cluster.h:30
char * defname
Definition: parsenodes.h:810
int location
Definition: parsenodes.h:814
Definition: pg_list.h:54
Form_pg_class rd_rel
Definition: rel.h:111
Definition: type.h:95
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:16985
void PreventInTransactionBlock(bool isTopLevel, const char *stmtType)
Definition: xact.c:3480
void StartTransactionCommand(void)
Definition: xact.c:2937

References AccessExclusiveLock, AccessShareLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), check_index_is_clusterable(), CLUOPT_RECHECK, CLUOPT_RECHECK_ISCLUSTERED, CLUOPT_VERBOSE, cluster_multiple_rels(), cluster_rel(), defGetBoolean(), DefElem::defname, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), get_relname_relid(), get_tables_to_cluster(), get_tables_to_cluster_partitioned(), InvalidOid, lfirst, lfirst_oid, DefElem::location, MemoryContextDelete(), NoLock, OidIsValid, ClusterParams::options, parser_errposition(), PortalContext, PreventInTransactionBlock(), RangeVarCallbackMaintainsTable(), RangeVarGetRelidExtended(), RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetIndexList(), StartTransactionCommand(), stmt, table_close(), table_open(), and verbose.

Referenced by adjust_data_dir(), check_bin_dir(), check_data_dir(), check_for_aclitem_data_type_usage(), check_for_composite_data_type_usage(), check_for_data_type_usage(), check_for_data_types_usage(), check_for_incompatible_polymorphics(), check_for_isn_and_int8_passing_mismatch(), check_for_jsonb_9_4_usage(), check_for_pg_role_prefix(), check_for_prepared_transactions(), check_for_reg_data_type_usage(), check_for_tables_with_oids(), check_for_user_defined_encoding_conversions(), check_for_user_defined_postfix_ops(), check_is_install_user(), check_proper_datallowconn(), cluster_conn_opts(), connectToServer(), get_bin_version(), get_control_data(), get_db_and_rel_infos(), get_db_conn(), get_db_infos(), get_major_server_version(), get_rel_infos(), get_sock_dir(), get_template0_info(), old_11_check_for_sql_identifier_data_type_usage(), old_9_3_check_for_line_data_type_usage(), old_9_6_check_for_unknown_data_type_usage(), old_9_6_invalidate_hash_indexes(), report_extension_updates(), set_tablespace_directory_suffix(), standard_ProcessUtility(), start_postmaster(), and stop_postmaster().

◆ cluster_rel()

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

Definition at line 314 of file cluster.c.

315 {
316  Relation OldHeap;
317  Oid save_userid;
318  int save_sec_context;
319  int save_nestlevel;
320  bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
321  bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
322 
323  /* Check for user-requested abort. */
325 
327  if (OidIsValid(indexOid))
330  else
333 
334  /*
335  * We grab exclusive access to the target rel and index for the duration
336  * of the transaction. (This is redundant for the single-transaction
337  * case, since cluster() already did it.) The index lock is taken inside
338  * check_index_is_clusterable.
339  */
340  OldHeap = try_relation_open(tableOid, AccessExclusiveLock);
341 
342  /* If the table has gone away, we can skip processing it */
343  if (!OldHeap)
344  {
346  return;
347  }
348 
349  /*
350  * Switch to the table owner's userid, so that any index functions are run
351  * as that user. Also lock down security-restricted operations and
352  * arrange to make GUC variable changes local to this command.
353  */
354  GetUserIdAndSecContext(&save_userid, &save_sec_context);
355  SetUserIdAndSecContext(OldHeap->rd_rel->relowner,
356  save_sec_context | SECURITY_RESTRICTED_OPERATION);
357  save_nestlevel = NewGUCNestLevel();
358 
359  /*
360  * Since we may open a new transaction for each relation, we have to check
361  * that the relation still is what we think it is.
362  *
363  * If this is a single-transaction CLUSTER, we can skip these tests. We
364  * *must* skip the one on indisclustered since it would reject an attempt
365  * to cluster a not-previously-clustered index.
366  */
367  if (recheck)
368  {
369  /* Check that the user still has privileges for the relation */
370  if (!cluster_is_permitted_for_relation(tableOid, save_userid))
371  {
373  goto out;
374  }
375 
376  /*
377  * Silently skip a temp table for a remote session. Only doing this
378  * check in the "recheck" case is appropriate (which currently means
379  * somebody is executing a database-wide CLUSTER or on a partitioned
380  * table), because there is another check in cluster() which will stop
381  * any attempt to cluster remote temp tables by name. There is
382  * another check in cluster_rel which is redundant, but we leave it
383  * for extra safety.
384  */
385  if (RELATION_IS_OTHER_TEMP(OldHeap))
386  {
388  goto out;
389  }
390 
391  if (OidIsValid(indexOid))
392  {
393  /*
394  * Check that the index still exists
395  */
397  {
399  goto out;
400  }
401 
402  /*
403  * Check that the index is still the one with indisclustered set,
404  * if needed.
405  */
406  if ((params->options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
407  !get_index_isclustered(indexOid))
408  {
410  goto out;
411  }
412  }
413  }
414 
415  /*
416  * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
417  * would work in most respects, but the index would only get marked as
418  * indisclustered in the current database, leading to unexpected behavior
419  * if CLUSTER were later invoked in another database.
420  */
421  if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
422  ereport(ERROR,
423  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
424  errmsg("cannot cluster a shared catalog")));
425 
426  /*
427  * Don't process temp tables of other backends ... their local buffer
428  * manager is not going to cope.
429  */
430  if (RELATION_IS_OTHER_TEMP(OldHeap))
431  {
432  if (OidIsValid(indexOid))
433  ereport(ERROR,
434  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
435  errmsg("cannot cluster temporary tables of other sessions")));
436  else
437  ereport(ERROR,
438  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
439  errmsg("cannot vacuum temporary tables of other sessions")));
440  }
441 
442  /*
443  * Also check for active uses of the relation in the current transaction,
444  * including open scans and pending AFTER trigger events.
445  */
446  CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
447 
448  /* Check heap and index are valid to cluster on */
449  if (OidIsValid(indexOid))
451 
452  /*
453  * Quietly ignore the request if this is a materialized view which has not
454  * been populated from its query. No harm is done because there is no data
455  * to deal with, and we don't want to throw an error if this is part of a
456  * multi-relation request -- for example, CLUSTER was run on the entire
457  * database.
458  */
459  if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
460  !RelationIsPopulated(OldHeap))
461  {
463  goto out;
464  }
465 
466  Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
467  OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
468  OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
469 
470  /*
471  * All predicate locks on the tuples or pages are about to be made
472  * invalid, because we move tuples around. Promote them to relation
473  * locks. Predicate locks on indexes will be promoted when they are
474  * reindexed.
475  */
477 
478  /* rebuild_relation does all the dirty work */
479  rebuild_relation(OldHeap, indexOid, verbose);
480 
481  /* NB: rebuild_relation does table_close() on OldHeap */
482 
483 out:
484  /* Roll back any GUC changes executed by index functions */
485  AtEOXact_GUC(false, save_nestlevel);
486 
487  /* Restore userid and security context */
488  SetUserIdAndSecContext(save_userid, save_sec_context);
489 
491 }
void pgstat_progress_start_command(ProgressCommandType cmdtype, Oid relid)
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_end_command(void)
@ PROGRESS_COMMAND_CLUSTER
static void rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
Definition: cluster.c:635
static bool cluster_is_permitted_for_relation(Oid relid, Oid userid)
Definition: cluster.c:1721
int NewGUCNestLevel(void)
Definition: guc.c:2201
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2215
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:314
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:631
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:638
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3057
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:75
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:74
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:55
#define RelationIsPopulated(relation)
Definition: rel.h:678
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:89
@ RELOID
Definition: syscache.h:89
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:191
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4096

References AccessExclusiveLock, Assert(), AtEOXact_GUC(), CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckTableNotInUse(), CLUOPT_RECHECK, CLUOPT_RECHECK_ISCLUSTERED, CLUOPT_VERBOSE, cluster_is_permitted_for_relation(), ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), GetUserIdAndSecContext(), NewGUCNestLevel(), ObjectIdGetDatum(), OidIsValid, ClusterParams::options, 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, SECURITY_RESTRICTED_OPERATION, SetUserIdAndSecContext(), TransferPredicateLocksToHeapRelation(), try_relation_open(), and verbose.

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

◆ 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  cutoffMulti,
char  newrelpersistence 
)

Definition at line 1424 of file cluster.c.

1432 {
1433  ObjectAddress object;
1434  Oid mapped_tables[4];
1435  int reindex_flags;
1436  ReindexParams reindex_params = {0};
1437  int i;
1438 
1439  /* Report that we are now swapping relation files */
1442 
1443  /* Zero out possible results from swapped_relation_files */
1444  memset(mapped_tables, 0, sizeof(mapped_tables));
1445 
1446  /*
1447  * Swap the contents of the heap relations (including any toast tables).
1448  * Also set old heap's relfrozenxid to frozenXid.
1449  */
1450  swap_relation_files(OIDOldHeap, OIDNewHeap,
1451  (OIDOldHeap == RelationRelationId),
1452  swap_toast_by_content, is_internal,
1453  frozenXid, cutoffMulti, mapped_tables);
1454 
1455  /*
1456  * If it's a system catalog, queue a sinval message to flush all catcaches
1457  * on the catalog when we reach CommandCounterIncrement.
1458  */
1459  if (is_system_catalog)
1460  CacheInvalidateCatalog(OIDOldHeap);
1461 
1462  /*
1463  * Rebuild each index on the relation (but not the toast table, which is
1464  * all-new at this point). It is important to do this before the DROP
1465  * step because if we are processing a system catalog that will be used
1466  * during DROP, we want to have its indexes available. There is no
1467  * advantage to the other order anyway because this is all transactional,
1468  * so no chance to reclaim disk space before commit. We do not need a
1469  * final CommandCounterIncrement() because reindex_relation does it.
1470  *
1471  * Note: because index_build is called via reindex_relation, it will never
1472  * set indcheckxmin true for the indexes. This is OK even though in some
1473  * sense we are building new indexes rather than rebuilding existing ones,
1474  * because the new heap won't contain any HOT chains at all, let alone
1475  * broken ones, so it can't be necessary to set indcheckxmin.
1476  */
1477  reindex_flags = REINDEX_REL_SUPPRESS_INDEX_USE;
1478  if (check_constraints)
1479  reindex_flags |= REINDEX_REL_CHECK_CONSTRAINTS;
1480 
1481  /*
1482  * Ensure that the indexes have the same persistence as the parent
1483  * relation.
1484  */
1485  if (newrelpersistence == RELPERSISTENCE_UNLOGGED)
1486  reindex_flags |= REINDEX_REL_FORCE_INDEXES_UNLOGGED;
1487  else if (newrelpersistence == RELPERSISTENCE_PERMANENT)
1488  reindex_flags |= REINDEX_REL_FORCE_INDEXES_PERMANENT;
1489 
1490  /* Report that we are now reindexing relations */
1493 
1494  reindex_relation(OIDOldHeap, reindex_flags, &reindex_params);
1495 
1496  /* Report that we are now doing clean up */
1499 
1500  /*
1501  * If the relation being rebuilt is pg_class, swap_relation_files()
1502  * couldn't update pg_class's own pg_class entry (check comments in
1503  * swap_relation_files()), thus relfrozenxid was not updated. That's
1504  * annoying because a potential reason for doing a VACUUM FULL is a
1505  * imminent or actual anti-wraparound shutdown. So, now that we can
1506  * access the new relation using its indices, update relfrozenxid.
1507  * pg_class doesn't have a toast relation, so we don't need to update the
1508  * corresponding toast relation. Not that there's little point moving all
1509  * relfrozenxid updates here since swap_relation_files() needs to write to
1510  * pg_class for non-mapped relations anyway.
1511  */
1512  if (OIDOldHeap == RelationRelationId)
1513  {
1514  Relation relRelation;
1515  HeapTuple reltup;
1516  Form_pg_class relform;
1517 
1518  relRelation = table_open(RelationRelationId, RowExclusiveLock);
1519 
1520  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(OIDOldHeap));
1521  if (!HeapTupleIsValid(reltup))
1522  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
1523  relform = (Form_pg_class) GETSTRUCT(reltup);
1524 
1525  relform->relfrozenxid = frozenXid;
1526  relform->relminmxid = cutoffMulti;
1527 
1528  CatalogTupleUpdate(relRelation, &reltup->t_self, reltup);
1529 
1530  table_close(relRelation, RowExclusiveLock);
1531  }
1532 
1533  /* Destroy new heap with old filenumber */
1534  object.classId = RelationRelationId;
1535  object.objectId = OIDNewHeap;
1536  object.objectSubId = 0;
1537 
1538  /*
1539  * The new relation is local to our transaction and we know nothing
1540  * depends on it, so DROP_RESTRICT should be OK.
1541  */
1543 
1544  /* performDeletion does CommandCounterIncrement at end */
1545 
1546  /*
1547  * Now we must remove any relation mapping entries that we set up for the
1548  * transient table, as well as its toast table and toast index if any. If
1549  * we fail to do this before commit, the relmapper will complain about new
1550  * permanent map entries being added post-bootstrap.
1551  */
1552  for (i = 0; OidIsValid(mapped_tables[i]); i++)
1553  RelationMapRemoveMapping(mapped_tables[i]);
1554 
1555  /*
1556  * At this point, everything is kosher except that, if we did toast swap
1557  * by links, the toast table's name corresponds to the transient table.
1558  * The name is irrelevant to the backend because it's referenced by OID,
1559  * but users looking at the catalogs could be confused. Rename it to
1560  * prevent this problem.
1561  *
1562  * Note no lock required on the relation, because we already hold an
1563  * exclusive lock on it.
1564  */
1565  if (!swap_toast_by_content)
1566  {
1567  Relation newrel;
1568 
1569  newrel = table_open(OIDOldHeap, NoLock);
1570  if (OidIsValid(newrel->rd_rel->reltoastrelid))
1571  {
1572  Oid toastidx;
1573  char NewToastName[NAMEDATALEN];
1574 
1575  /* Get the associated valid index to be renamed */
1576  toastidx = toast_get_valid_index(newrel->rd_rel->reltoastrelid,
1577  NoLock);
1578 
1579  /* rename the toast table ... */
1580  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u",
1581  OIDOldHeap);
1582  RenameRelationInternal(newrel->rd_rel->reltoastrelid,
1583  NewToastName, true, false);
1584 
1585  /* ... and its valid index too. */
1586  snprintf(NewToastName, NAMEDATALEN, "pg_toast_%u_index",
1587  OIDOldHeap);
1588 
1589  RenameRelationInternal(toastidx,
1590  NewToastName, true, true);
1591 
1592  /*
1593  * Reset the relrewrite for the toast. The command-counter
1594  * increment is required here as we are about to update the tuple
1595  * that is updated as part of RenameRelationInternal.
1596  */
1598  ResetRelRewrite(newrel->rd_rel->reltoastrelid);
1599  }
1600  relation_close(newrel, NoLock);
1601  }
1602 
1603  /* if it's not a catalog table, clear any missing attribute settings */
1604  if (!is_system_catalog)
1605  {
1606  Relation newrel;
1607 
1608  newrel = table_open(OIDOldHeap, NoLock);
1609  RelationClearMissing(newrel);
1610  relation_close(newrel, NoLock);
1611  }
1612 }
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:1057
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:328
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:136
void RelationClearMissing(Relation rel)
Definition: heap.c:1935
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
bool reindex_relation(Oid relid, int flags, ReindexParams *params)
Definition: index.c:3873
#define REINDEX_REL_FORCE_INDEXES_UNLOGGED
Definition: index.h:158
#define REINDEX_REL_SUPPRESS_INDEX_USE
Definition: index.h:156
#define REINDEX_REL_FORCE_INDEXES_PERMANENT
Definition: index.h:159
#define REINDEX_REL_CHECK_CONSTRAINTS
Definition: index.h:157
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CacheInvalidateCatalog(Oid catalogId)
Definition: inval.c:1339
int i
Definition: isn.c:73
#define RowExclusiveLock
Definition: lockdefs.h:38
@ DROP_RESTRICT
Definition: parsenodes.h:2155
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
#define NAMEDATALEN
#define snprintf
Definition: port.h:238
#define PROGRESS_CLUSTER_PHASE
Definition: progress.h:56
#define PROGRESS_CLUSTER_PHASE_REBUILD_INDEX
Definition: progress.h:70
#define PROGRESS_CLUSTER_PHASE_FINAL_CLEANUP
Definition: progress.h:71
#define PROGRESS_CLUSTER_PHASE_SWAP_REL_FILES
Definition: progress.h:69
void RelationMapRemoveMapping(Oid relationId)
Definition: relmapper.c:438
ItemPointerData t_self
Definition: htup.h:65
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:182
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4043
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:3953
Oid toast_get_valid_index(Oid toastoid, LOCKMODE lock)
void CommandCounterIncrement(void)
Definition: xact.c:1078

References CacheInvalidateCatalog(), CatalogTupleUpdate(), CommandCounterIncrement(), 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(), ResetRelRewrite(), 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().

◆ make_new_heap()

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

Definition at line 690 of file cluster.c.

692 {
693  TupleDesc OldHeapDesc;
694  char NewHeapName[NAMEDATALEN];
695  Oid OIDNewHeap;
696  Oid toastid;
697  Relation OldHeap;
698  HeapTuple tuple;
699  Datum reloptions;
700  bool isNull;
701  Oid namespaceid;
702 
703  OldHeap = table_open(OIDOldHeap, lockmode);
704  OldHeapDesc = RelationGetDescr(OldHeap);
705 
706  /*
707  * Note that the NewHeap will not receive any of the defaults or
708  * constraints associated with the OldHeap; we don't need 'em, and there's
709  * no reason to spend cycles inserting them into the catalogs only to
710  * delete them.
711  */
712 
713  /*
714  * But we do want to use reloptions of the old heap for new heap.
715  */
716  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(OIDOldHeap));
717  if (!HeapTupleIsValid(tuple))
718  elog(ERROR, "cache lookup failed for relation %u", OIDOldHeap);
719  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
720  &isNull);
721  if (isNull)
722  reloptions = (Datum) 0;
723 
724  if (relpersistence == RELPERSISTENCE_TEMP)
725  namespaceid = LookupCreationNamespace("pg_temp");
726  else
727  namespaceid = RelationGetNamespace(OldHeap);
728 
729  /*
730  * Create the new heap, using a temporary name in the same namespace as
731  * the existing table. NOTE: there is some risk of collision with user
732  * relnames. Working around this seems more trouble than it's worth; in
733  * particular, we can't create the new heap in a different namespace from
734  * the old, or we will have problems with the TEMP status of temp tables.
735  *
736  * Note: the new heap is not a shared relation, even if we are rebuilding
737  * a shared rel. However, we do make the new heap mapped if the source is
738  * mapped. This simplifies swap_relation_files, and is absolutely
739  * necessary for rebuilding pg_class, for reasons explained there.
740  */
741  snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", OIDOldHeap);
742 
743  OIDNewHeap = heap_create_with_catalog(NewHeapName,
744  namespaceid,
745  NewTableSpace,
746  InvalidOid,
747  InvalidOid,
748  InvalidOid,
749  OldHeap->rd_rel->relowner,
750  NewAccessMethod,
751  OldHeapDesc,
752  NIL,
753  RELKIND_RELATION,
754  relpersistence,
755  false,
756  RelationIsMapped(OldHeap),
758  reloptions,
759  false,
760  true,
761  true,
762  OIDOldHeap,
763  NULL);
764  Assert(OIDNewHeap != InvalidOid);
765 
766  ReleaseSysCache(tuple);
767 
768  /*
769  * Advance command counter so that the newly-created relation's catalog
770  * tuples will be visible to table_open.
771  */
773 
774  /*
775  * If necessary, create a TOAST table for the new relation.
776  *
777  * If the relation doesn't have a TOAST table already, we can't need one
778  * for the new relation. The other way around is possible though: if some
779  * wide columns have been dropped, NewHeapCreateToastTable can decide that
780  * no TOAST table is needed for the new table.
781  *
782  * Note that NewHeapCreateToastTable ends with CommandCounterIncrement, so
783  * that the TOAST table will be visible for insertion.
784  */
785  toastid = OldHeap->rd_rel->reltoastrelid;
786  if (OidIsValid(toastid))
787  {
788  /* keep the existing toast table's reloptions, if any */
789  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
790  if (!HeapTupleIsValid(tuple))
791  elog(ERROR, "cache lookup failed for relation %u", toastid);
792  reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
793  &isNull);
794  if (isNull)
795  reloptions = (Datum) 0;
796 
797  NewHeapCreateToastTable(OIDNewHeap, reloptions, lockmode, toastid);
798 
799  ReleaseSysCache(tuple);
800  }
801 
802  table_close(OldHeap, NoLock);
803 
804  return OIDNewHeap;
805 }
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:1091
Oid LookupCreationNamespace(const char *nspname)
Definition: namespace.c:2979
#define NIL
Definition: pg_list.h:68
uintptr_t Datum
Definition: postgres.h:64
@ ONCOMMIT_NOOP
Definition: primnodes.h:49
#define RelationGetDescr(relation)
Definition: rel.h:530
#define RelationIsMapped(relation)
Definition: rel.h:553
#define RelationGetNamespace(relation)
Definition: rel.h:545
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:866
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:818
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1079
void NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, Oid OIDOldToast)
Definition: toasting.c:66

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

◆ mark_index_clustered()

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

Definition at line 562 of file cluster.c.

563 {
564  HeapTuple indexTuple;
565  Form_pg_index indexForm;
566  Relation pg_index;
567  ListCell *index;
568 
569  /* Disallow applying to a partitioned table */
570  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
571  ereport(ERROR,
572  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
573  errmsg("cannot mark index clustered in partitioned table")));
574 
575  /*
576  * If the index is already marked clustered, no need to do anything.
577  */
578  if (OidIsValid(indexOid))
579  {
580  if (get_index_isclustered(indexOid))
581  return;
582  }
583 
584  /*
585  * Check each index of the relation and set/clear the bit as needed.
586  */
587  pg_index = table_open(IndexRelationId, RowExclusiveLock);
588 
589  foreach(index, RelationGetIndexList(rel))
590  {
591  Oid thisIndexOid = lfirst_oid(index);
592 
593  indexTuple = SearchSysCacheCopy1(INDEXRELID,
594  ObjectIdGetDatum(thisIndexOid));
595  if (!HeapTupleIsValid(indexTuple))
596  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
597  indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
598 
599  /*
600  * Unset the bit if set. We know it's wrong because we checked this
601  * earlier.
602  */
603  if (indexForm->indisclustered)
604  {
605  indexForm->indisclustered = false;
606  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
607  }
608  else if (thisIndexOid == indexOid)
609  {
610  /* this was checked earlier, but let's be real sure */
611  if (!indexForm->indisvalid)
612  elog(ERROR, "cannot cluster on invalid index %u", indexOid);
613  indexForm->indisclustered = true;
614  CatalogTupleUpdate(pg_index, &indexTuple->t_self, indexTuple);
615  }
616 
617  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
618  InvalidOid, is_internal);
619 
620  heap_freetuple(indexTuple);
621  }
622 
623  table_close(pg_index, RowExclusiveLock);
624 }
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
@ INDEXRELID
Definition: syscache.h:66

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