PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 (Relation OldHeap, 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 494 of file cluster.c.

495{
496 Relation OldIndex;
497
498 OldIndex = index_open(indexOid, lockmode);
499
500 /*
501 * Check that index is in fact an index on the given relation
502 */
503 if (OldIndex->rd_index == NULL ||
504 OldIndex->rd_index->indrelid != RelationGetRelid(OldHeap))
506 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
507 errmsg("\"%s\" is not an index for table \"%s\"",
508 RelationGetRelationName(OldIndex),
509 RelationGetRelationName(OldHeap))));
510
511 /* Index AM must allow clustering */
512 if (!OldIndex->rd_indam->amclusterable)
514 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
515 errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
516 RelationGetRelationName(OldIndex))));
517
518 /*
519 * Disallow clustering on incomplete indexes (those that might not index
520 * every row of the relation). We could relax this by making a separate
521 * seqscan pass over the table to copy the missing rows, but that seems
522 * expensive and tedious.
523 */
524 if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred, NULL))
526 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
527 errmsg("cannot cluster on partial index \"%s\"",
528 RelationGetRelationName(OldIndex))));
529
530 /*
531 * Disallow if index is left over from a failed CREATE INDEX CONCURRENTLY;
532 * it might well not contain entries for every heap row, or might not even
533 * be internally consistent. (But note that we don't check indcheckxmin;
534 * the worst consequence of following broken HOT chains would be that we
535 * might put recently-dead tuples out-of-order in the new table, and there
536 * is little harm in that.)
537 */
538 if (!OldIndex->rd_index->indisvalid)
540 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
541 errmsg("cannot cluster on invalid index \"%s\"",
542 RelationGetRelationName(OldIndex))));
543
544 /* Drop relcache refcnt on OldIndex, but keep lock */
545 index_close(OldIndex, NoLock);
546}
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#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:456
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
#define NoLock
Definition: lockdefs.h:34
#define RelationGetRelid(relation)
Definition: rel.h:516
#define RelationGetRelationName(relation)
Definition: rel.h:550
bool amclusterable
Definition: amapi.h:268
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
struct HeapTupleData * rd_indextuple
Definition: rel.h:194
Form_pg_index rd_index
Definition: rel.h:192

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

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

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_connection_status(), check_for_data_types_usage(), check_for_incompatible_polymorphics(), check_for_isn_and_int8_passing_mismatch(), check_for_pg_role_prefix(), check_for_prepared_transactions(), check_for_tables_with_oids(), check_for_unicode_update(), check_for_user_defined_encoding_conversions(), check_for_user_defined_postfix_ops(), check_is_install_user(), cluster_conn_opts(), connectToServer(), get_bin_version(), get_control_data(), get_db_conn(), get_db_infos(), get_db_rel_and_slot_infos(), get_major_server_version(), get_sock_dir(), get_subscription_count(), get_template0_info(), jsonb_9_4_check_applicable(), old_9_6_invalidate_hash_indexes(), process_query_result(), process_slot(), report_extension_updates(), set_tablespace_directory_suffix(), standard_ProcessUtility(), start_conn(), start_postmaster(), stop_postmaster(), unicode_version_changed(), and upgrade_task_run().

◆ cluster_rel()

void cluster_rel ( Relation  OldHeap,
Oid  indexOid,
ClusterParams params 
)

Definition at line 311 of file cluster.c.

312{
313 Oid tableOid = RelationGetRelid(OldHeap);
314 Oid save_userid;
315 int save_sec_context;
316 int save_nestlevel;
317 bool verbose = ((params->options & CLUOPT_VERBOSE) != 0);
318 bool recheck = ((params->options & CLUOPT_RECHECK) != 0);
320
322
323 /* Check for user-requested abort. */
325
327 if (OidIsValid(indexOid))
330 else
333
334 /*
335 * Switch to the table owner's userid, so that any index functions are run
336 * as that user. Also lock down security-restricted operations and
337 * arrange to make GUC variable changes local to this command.
338 */
339 GetUserIdAndSecContext(&save_userid, &save_sec_context);
340 SetUserIdAndSecContext(OldHeap->rd_rel->relowner,
341 save_sec_context | SECURITY_RESTRICTED_OPERATION);
342 save_nestlevel = NewGUCNestLevel();
344
345 /*
346 * Since we may open a new transaction for each relation, we have to check
347 * that the relation still is what we think it is.
348 *
349 * If this is a single-transaction CLUSTER, we can skip these tests. We
350 * *must* skip the one on indisclustered since it would reject an attempt
351 * to cluster a not-previously-clustered index.
352 */
353 if (recheck)
354 {
355 /* Check that the user still has privileges for the relation */
356 if (!cluster_is_permitted_for_relation(tableOid, save_userid))
357 {
359 goto out;
360 }
361
362 /*
363 * Silently skip a temp table for a remote session. Only doing this
364 * check in the "recheck" case is appropriate (which currently means
365 * somebody is executing a database-wide CLUSTER or on a partitioned
366 * table), because there is another check in cluster() which will stop
367 * any attempt to cluster remote temp tables by name. There is
368 * another check in cluster_rel which is redundant, but we leave it
369 * for extra safety.
370 */
371 if (RELATION_IS_OTHER_TEMP(OldHeap))
372 {
374 goto out;
375 }
376
377 if (OidIsValid(indexOid))
378 {
379 /*
380 * Check that the index still exists
381 */
382 if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(indexOid)))
383 {
385 goto out;
386 }
387
388 /*
389 * Check that the index is still the one with indisclustered set,
390 * if needed.
391 */
392 if ((params->options & CLUOPT_RECHECK_ISCLUSTERED) != 0 &&
393 !get_index_isclustered(indexOid))
394 {
396 goto out;
397 }
398 }
399 }
400
401 /*
402 * We allow VACUUM FULL, but not CLUSTER, on shared catalogs. CLUSTER
403 * would work in most respects, but the index would only get marked as
404 * indisclustered in the current database, leading to unexpected behavior
405 * if CLUSTER were later invoked in another database.
406 */
407 if (OidIsValid(indexOid) && OldHeap->rd_rel->relisshared)
409 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
410 errmsg("cannot cluster a shared catalog")));
411
412 /*
413 * Don't process temp tables of other backends ... their local buffer
414 * manager is not going to cope.
415 */
416 if (RELATION_IS_OTHER_TEMP(OldHeap))
417 {
418 if (OidIsValid(indexOid))
420 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
421 errmsg("cannot cluster temporary tables of other sessions")));
422 else
424 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
425 errmsg("cannot vacuum temporary tables of other sessions")));
426 }
427
428 /*
429 * Also check for active uses of the relation in the current transaction,
430 * including open scans and pending AFTER trigger events.
431 */
432 CheckTableNotInUse(OldHeap, OidIsValid(indexOid) ? "CLUSTER" : "VACUUM");
433
434 /* Check heap and index are valid to cluster on */
435 if (OidIsValid(indexOid))
436 {
437 /* verify the index is good and lock it */
439 /* also open it */
440 index = index_open(indexOid, NoLock);
441 }
442 else
443 index = NULL;
444
445 /*
446 * Quietly ignore the request if this is a materialized view which has not
447 * been populated from its query. No harm is done because there is no data
448 * to deal with, and we don't want to throw an error if this is part of a
449 * multi-relation request -- for example, CLUSTER was run on the entire
450 * database.
451 */
452 if (OldHeap->rd_rel->relkind == RELKIND_MATVIEW &&
453 !RelationIsPopulated(OldHeap))
454 {
456 goto out;
457 }
458
459 Assert(OldHeap->rd_rel->relkind == RELKIND_RELATION ||
460 OldHeap->rd_rel->relkind == RELKIND_MATVIEW ||
461 OldHeap->rd_rel->relkind == RELKIND_TOASTVALUE);
462
463 /*
464 * All predicate locks on the tuples or pages are about to be made
465 * invalid, because we move tuples around. Promote them to relation
466 * locks. Predicate locks on indexes will be promoted when they are
467 * reindexed.
468 */
470
471 /* rebuild_relation does all the dirty work */
472 rebuild_relation(OldHeap, index, verbose);
473 /* rebuild_relation closes OldHeap, and index if valid */
474
475out:
476 /* Roll back any GUC changes executed by index functions */
477 AtEOXact_GUC(false, save_nestlevel);
478
479 /* Restore userid and security context */
480 SetUserIdAndSecContext(save_userid, save_sec_context);
481
483}
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 bool cluster_is_permitted_for_relation(Oid relid, Oid userid)
Definition: cluster.c:1745
static void rebuild_relation(Relation OldHeap, Relation index, bool verbose)
Definition: cluster.c:629
int NewGUCNestLevel(void)
Definition: guc.c:2235
void RestrictSearchPath(void)
Definition: guc.c:2246
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2262
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:334
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:319
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:663
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:670
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3123
#define PROGRESS_CLUSTER_COMMAND_VACUUM_FULL
Definition: progress.h:80
#define PROGRESS_CLUSTER_COMMAND_CLUSTER
Definition: progress.h:79
#define PROGRESS_CLUSTER_COMMAND
Definition: progress.h:60
#define RelationIsPopulated(relation)
Definition: rel.h:688
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
#define SearchSysCacheExists1(cacheId, key1)
Definition: syscache.h:100
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4406

References AccessExclusiveLock, Assert(), AtEOXact_GUC(), CHECK_FOR_INTERRUPTS, check_index_is_clusterable(), CheckRelationLockedByMe(), CheckTableNotInUse(), CLUOPT_RECHECK, CLUOPT_RECHECK_ISCLUSTERED, CLUOPT_VERBOSE, cluster_is_permitted_for_relation(), ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), GetUserIdAndSecContext(), index_open(), NewGUCNestLevel(), NoLock, 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, RelationGetRelid, RelationIsPopulated, RestrictSearchPath(), SearchSysCacheExists1, SECURITY_RESTRICTED_OPERATION, SetUserIdAndSecContext(), TransferPredicateLocksToHeapRelation(), 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 1445 of file cluster.c.

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

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(), 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 705 of file cluster.c.

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

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(), SearchSysCache1(), snprintf, SysCacheGetAttr(), table_close(), and table_open().

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

◆ mark_index_clustered()

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

Definition at line 554 of file cluster.c.

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

References CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, get_index_isclustered(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, 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().