1 /*-------------------------------------------------------------------------
4 * POSTGRES define and remove index code.
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.120 2004/05/26 04:41:11 neilc Exp $
13 *-------------------------------------------------------------------------
18 #include "access/heapam.h"
19 #include "catalog/catalog.h"
20 #include "catalog/catname.h"
21 #include "catalog/dependency.h"
22 #include "catalog/heap.h"
23 #include "catalog/index.h"
24 #include "catalog/namespace.h"
25 #include "catalog/pg_opclass.h"
26 #include "catalog/pg_proc.h"
27 #include "commands/dbcommands.h"
28 #include "commands/defrem.h"
29 #include "commands/tablecmds.h"
30 #include "executor/executor.h"
31 #include "miscadmin.h"
32 #include "optimizer/clauses.h"
33 #include "optimizer/prep.h"
34 #include "parser/analyze.h"
35 #include "parser/parsetree.h"
36 #include "parser/parse_coerce.h"
37 #include "parser/parse_expr.h"
38 #include "parser/parse_func.h"
39 #include "utils/acl.h"
40 #include "utils/builtins.h"
41 #include "utils/lsyscache.h"
42 #include "utils/relcache.h"
43 #include "utils/syscache.h"
46 /* non-export function prototypes */
47 static void CheckPredicate(Expr *predicate);
48 static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
51 char *accessMethodName, Oid accessMethodId,
53 static Oid GetIndexOpClass(List *opclass, Oid attrType,
54 char *accessMethodName, Oid accessMethodId);
55 static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
56 static char *CreateIndexName(const char *table_name, const char *column_name,
57 const char *label, Oid inamespace);
58 static bool relationHasPrimaryKey(Relation rel);
63 * Creates a new index.
65 * 'heapRelation': the relation the index will apply to.
66 * 'indexRelationName': the name for the new index, or NULL to indicate
67 * that a nonconflicting default name should be picked.
68 * 'accessMethodName': name of the AM to use.
69 * 'attributeList': a list of IndexElem specifying columns and expressions
71 * 'predicate': the partial-index condition, or NULL if none.
72 * 'rangetable': needed to interpret the predicate.
73 * 'unique': make the index enforce uniqueness.
74 * 'primary': mark the index as a primary key in the catalogs.
75 * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
76 * so build a pg_constraint entry for it.
77 * 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
78 * 'check_rights': check for CREATE rights in the namespace. (This should
79 * be true except when ALTER is deleting/recreating an index.)
80 * 'skip_build': make the catalog entries but leave the index file empty;
81 * it will be filled later.
82 * 'quiet': suppress the NOTICE chatter ordinarily provided for constraints.
85 DefineIndex(RangeVar *heapRelation,
86 char *indexRelationName,
87 char *accessMethodName,
105 Form_pg_am accessMethodForm;
106 IndexInfo *indexInfo;
107 int numberOfAttributes;
110 * count attributes in index
112 numberOfAttributes = list_length(attributeList);
113 if (numberOfAttributes <= 0)
115 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
116 errmsg("must specify at least one column")));
117 if (numberOfAttributes > INDEX_MAX_KEYS)
119 (errcode(ERRCODE_TOO_MANY_COLUMNS),
120 errmsg("cannot use more than %d columns in an index",
124 * Open heap relation, acquire a suitable lock on it, remember its OID
126 rel = heap_openrv(heapRelation, ShareLock);
128 /* Note: during bootstrap may see uncataloged relation */
129 if (rel->rd_rel->relkind != RELKIND_RELATION &&
130 rel->rd_rel->relkind != RELKIND_UNCATALOGED)
132 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
133 errmsg("\"%s\" is not a table",
134 heapRelation->relname)));
136 relationId = RelationGetRelid(rel);
137 namespaceId = RelationGetNamespace(rel);
140 * Verify we (still) have CREATE rights in the rel's namespace.
141 * (Presumably we did when the rel was created, but maybe not
142 * anymore.) Skip check if caller doesn't want it. Also skip check
143 * if bootstrapping, since permissions machinery may not be working yet.
145 if (check_rights && !IsBootstrapProcessingMode())
149 aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
151 if (aclresult != ACLCHECK_OK)
152 aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
153 get_namespace_name(namespaceId));
157 * Select name for index if caller didn't specify
159 if (indexRelationName == NULL)
162 indexRelationName = CreateIndexName(RelationGetRelationName(rel),
168 IndexElem *iparam = (IndexElem *) linitial(attributeList);
170 indexRelationName = CreateIndexName(RelationGetRelationName(rel),
178 * look up the access method, verify it can handle the requested
181 tuple = SearchSysCache(AMNAME,
182 PointerGetDatum(accessMethodName),
184 if (!HeapTupleIsValid(tuple))
186 (errcode(ERRCODE_UNDEFINED_OBJECT),
187 errmsg("access method \"%s\" does not exist",
189 accessMethodId = HeapTupleGetOid(tuple);
190 accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
192 if (unique && !accessMethodForm->amcanunique)
194 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
195 errmsg("access method \"%s\" does not support unique indexes",
197 if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
199 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
200 errmsg("access method \"%s\" does not support multicolumn indexes",
203 ReleaseSysCache(tuple);
206 * If a range table was created then check that only the base rel is
209 if (rangetable != NIL)
211 if (list_length(rangetable) != 1 || getrelid(1, rangetable) != relationId)
213 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
214 errmsg("index expressions and predicates may refer only to the table being indexed")));
218 * Validate predicate, if given
221 CheckPredicate(predicate);
224 * Extra checks when creating a PRIMARY KEY index.
232 * If ALTER TABLE, check that there isn't already a PRIMARY KEY.
233 * In CREATE TABLE, we have faith that the parser rejected multiple
234 * pkey clauses; and CREATE INDEX doesn't have a way to say
235 * PRIMARY KEY, so it's no problem either.
237 if (is_alter_table &&
238 relationHasPrimaryKey(rel))
241 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
242 errmsg("multiple primary keys for table \"%s\" are not allowed",
243 RelationGetRelationName(rel))));
247 * Check that all of the attributes in a primary key are marked as not
248 * null, otherwise attempt to ALTER TABLE .. SET NOT NULL
251 foreach(keys, attributeList)
253 IndexElem *key = (IndexElem *) lfirst(keys);
258 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
259 errmsg("primary keys cannot be expressions")));
261 /* System attributes are never null, so no problem */
262 if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids))
265 atttuple = SearchSysCacheAttName(relationId, key->name);
266 if (HeapTupleIsValid(atttuple))
268 if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull)
270 /* Add a subcommand to make this one NOT NULL */
271 AlterTableCmd *cmd = makeNode(AlterTableCmd);
273 cmd->subtype = AT_SetNotNull;
274 cmd->name = key->name;
276 cmds = lappend(cmds, cmd);
278 ReleaseSysCache(atttuple);
283 * This shouldn't happen during CREATE TABLE, but can
284 * happen during ALTER TABLE. Keep message in sync with
285 * transformIndexConstraints() in parser/analyze.c.
288 (errcode(ERRCODE_UNDEFINED_COLUMN),
289 errmsg("column \"%s\" named in key does not exist",
295 * XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade
296 * to child tables? Currently, since the PRIMARY KEY
297 * itself doesn't cascade, we don't cascade the
298 * notnull constraint(s) either; but this is pretty debatable.
300 * XXX: possible future improvement: when being called from
301 * ALTER TABLE, it would be more efficient to merge this with
302 * the outer ALTER TABLE, so as to avoid two scans. But that
303 * seems to complicate DefineIndex's API unduly.
306 AlterTableInternal(relationId, cmds, false);
310 * Prepare arguments for index_create, primarily an IndexInfo
311 * structure. Note that ii_Predicate must be in implicit-AND format.
313 indexInfo = makeNode(IndexInfo);
314 indexInfo->ii_NumIndexAttrs = numberOfAttributes;
315 indexInfo->ii_Expressions = NIL; /* for now */
316 indexInfo->ii_ExpressionsState = NIL;
317 indexInfo->ii_Predicate = make_ands_implicit(predicate);
318 indexInfo->ii_PredicateState = NIL;
319 indexInfo->ii_Unique = unique;
321 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
322 ComputeIndexAttrs(indexInfo, classObjectId, attributeList,
323 relationId, accessMethodName, accessMethodId,
326 heap_close(rel, NoLock);
329 * Report index creation if appropriate (delay this till after most
330 * of the error checks)
332 if (isconstraint && !quiet)
334 (errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
335 is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
336 primary ? "PRIMARY KEY" : "UNIQUE",
337 indexRelationName, RelationGetRelationName(rel))));
339 index_create(relationId, indexRelationName,
340 indexInfo, accessMethodId, classObjectId,
341 primary, isconstraint,
342 allowSystemTableMods, skip_build);
345 * We update the relation's pg_class tuple even if it already has
346 * relhasindex = true. This is needed to cause a shared-cache-inval
347 * message to be sent for the pg_class tuple, which will cause other
348 * backends to flush their relcache entries and in particular their
349 * cached lists of the indexes for this relation.
351 setRelhasindex(relationId, true, primary, InvalidOid);
357 * Checks that the given partial-index predicate is valid.
359 * This used to also constrain the form of the predicate to forms that
360 * indxpath.c could do something with. However, that seems overly
361 * restrictive. One useful application of partial indexes is to apply
362 * a UNIQUE constraint across a subset of a table, and in that scenario
363 * any evaluatable predicate will work. So accept any predicate here
364 * (except ones requiring a plan), and let indxpath.c fend for itself.
367 CheckPredicate(Expr *predicate)
370 * We don't currently support generation of an actual query plan for a
371 * predicate, only simple scalar expressions; hence these
374 if (contain_subplans((Node *) predicate))
376 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
377 errmsg("cannot use subquery in index predicate")));
378 if (contain_agg_clause((Node *) predicate))
380 (errcode(ERRCODE_GROUPING_ERROR),
381 errmsg("cannot use aggregate in index predicate")));
384 * A predicate using mutable functions is probably wrong, for the same
385 * reasons that we don't allow an index expression to use one.
387 if (contain_mutable_functions((Node *) predicate))
389 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
390 errmsg("functions in index predicate must be marked IMMUTABLE")));
394 ComputeIndexAttrs(IndexInfo *indexInfo,
396 List *attList, /* list of IndexElem's */
398 char *accessMethodName,
406 * process attributeList
408 foreach(rest, attList)
410 IndexElem *attribute = (IndexElem *) lfirst(rest);
413 if (attribute->name != NULL)
415 /* Simple index attribute */
417 Form_pg_attribute attform;
419 Assert(attribute->expr == NULL);
420 atttuple = SearchSysCacheAttName(relId, attribute->name);
421 if (!HeapTupleIsValid(atttuple))
423 /* difference in error message spellings is historical */
426 (errcode(ERRCODE_UNDEFINED_COLUMN),
427 errmsg("column \"%s\" named in key does not exist",
431 (errcode(ERRCODE_UNDEFINED_COLUMN),
432 errmsg("column \"%s\" does not exist",
435 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
436 indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
437 atttype = attform->atttypid;
438 ReleaseSysCache(atttuple);
440 else if (attribute->expr && IsA(attribute->expr, Var))
442 /* Tricky tricky, he wrote (column) ... treat as simple attr */
443 Var *var = (Var *) attribute->expr;
445 indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
446 atttype = get_atttype(relId, var->varattno);
450 /* Index expression */
451 Assert(attribute->expr != NULL);
452 indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
453 indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
455 atttype = exprType(attribute->expr);
458 * We don't currently support generation of an actual query
459 * plan for an index expression, only simple scalar
460 * expressions; hence these restrictions.
462 if (contain_subplans(attribute->expr))
464 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
465 errmsg("cannot use subquery in index expression")));
466 if (contain_agg_clause(attribute->expr))
468 (errcode(ERRCODE_GROUPING_ERROR),
469 errmsg("cannot use aggregate function in index expression")));
472 * A expression using mutable functions is probably wrong,
473 * since if you aren't going to get the same result for the
474 * same data every time, it's not clear what the index entries
477 if (contain_mutable_functions(attribute->expr))
479 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
480 errmsg("functions in index expression must be marked IMMUTABLE")));
483 classOidP[attn] = GetIndexOpClass(attribute->opclass,
492 * Resolve possibly-defaulted operator class specification
495 GetIndexOpClass(List *opclass, Oid attrType,
496 char *accessMethodName, Oid accessMethodId)
505 * Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so
506 * we ignore those opclass names so the default *_ops is used. This
507 * can be removed in some later release. bjm 2000/02/07
509 * Release 7.1 removes lztext_ops, so suppress that too for a while. tgl
512 * Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
513 * too for awhile. I'm starting to think we need a better approach.
516 * Release 7.5 removes bigbox_ops (which was dead code for a long while
517 * anyway). tgl 2003/11/11
519 if (list_length(opclass) == 1)
521 char *claname = strVal(linitial(opclass));
523 if (strcmp(claname, "network_ops") == 0 ||
524 strcmp(claname, "timespan_ops") == 0 ||
525 strcmp(claname, "datetime_ops") == 0 ||
526 strcmp(claname, "lztext_ops") == 0 ||
527 strcmp(claname, "timestamp_ops") == 0 ||
528 strcmp(claname, "bigbox_ops") == 0)
534 /* no operator class specified, so find the default */
535 opClassId = GetDefaultOpClass(attrType, accessMethodId);
536 if (!OidIsValid(opClassId))
538 (errcode(ERRCODE_UNDEFINED_OBJECT),
539 errmsg("data type %s has no default operator class for access method \"%s\"",
540 format_type_be(attrType), accessMethodName),
541 errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
546 * Specific opclass name given, so look up the opclass.
549 /* deconstruct the name list */
550 DeconstructQualifiedName(opclass, &schemaname, &opcname);
554 /* Look in specific schema only */
557 namespaceId = LookupExplicitNamespace(schemaname);
558 tuple = SearchSysCache(CLAAMNAMENSP,
559 ObjectIdGetDatum(accessMethodId),
560 PointerGetDatum(opcname),
561 ObjectIdGetDatum(namespaceId),
566 /* Unqualified opclass name, so search the search path */
567 opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
568 if (!OidIsValid(opClassId))
570 (errcode(ERRCODE_UNDEFINED_OBJECT),
571 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
572 opcname, accessMethodName)));
573 tuple = SearchSysCache(CLAOID,
574 ObjectIdGetDatum(opClassId),
578 if (!HeapTupleIsValid(tuple))
580 (errcode(ERRCODE_UNDEFINED_OBJECT),
581 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
582 NameListToString(opclass), accessMethodName)));
585 * Verify that the index operator class accepts this datatype. Note
586 * we will accept binary compatibility.
588 opClassId = HeapTupleGetOid(tuple);
589 opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
591 if (!IsBinaryCoercible(attrType, opInputType))
593 (errcode(ERRCODE_DATATYPE_MISMATCH),
594 errmsg("operator class \"%s\" does not accept data type %s",
595 NameListToString(opclass), format_type_be(attrType))));
597 ReleaseSysCache(tuple);
603 GetDefaultOpClass(Oid attrType, Oid accessMethodId)
605 OpclassCandidateList opclass;
608 Oid exactOid = InvalidOid;
609 Oid compatibleOid = InvalidOid;
611 /* If it's a domain, look at the base type instead */
612 attrType = getBaseType(attrType);
615 * We scan through all the opclasses available for the access method,
616 * looking for one that is marked default and matches the target type
617 * (either exactly or binary-compatibly, but prefer an exact match).
619 * We could find more than one binary-compatible match, in which case we
620 * require the user to specify which one he wants. If we find more
621 * than one exact match, then someone put bogus entries in pg_opclass.
623 * The initial search is done by namespace.c so that we only consider
624 * opclasses visible in the current namespace search path. (See also
625 * typcache.c, which applies the same logic, but over all opclasses.)
627 for (opclass = OpclassGetCandidates(accessMethodId);
629 opclass = opclass->next)
631 if (opclass->opcdefault)
633 if (opclass->opcintype == attrType)
636 exactOid = opclass->oid;
638 else if (IsBinaryCoercible(attrType, opclass->opcintype))
641 compatibleOid = opclass->oid;
650 (errcode(ERRCODE_DUPLICATE_OBJECT),
651 errmsg("there are multiple default operator classes for data type %s",
652 format_type_be(attrType))));
653 if (ncompatible == 1)
654 return compatibleOid;
660 * Select a nonconflicting name for an index.
663 CreateIndexName(const char *table_name, const char *column_name,
664 const char *label, Oid inamespace)
668 char typename[NAMEDATALEN];
671 * The type name for makeObjectName is label, or labelN if that's
672 * necessary to prevent collision with existing indexes.
674 strncpy(typename, label, sizeof(typename));
678 iname = makeObjectName(table_name, column_name, typename);
680 if (!OidIsValid(get_relname_relid(iname, inamespace)))
683 /* found a conflict, so try a new name component */
685 snprintf(typename, sizeof(typename), "%s%d", label, ++pass);
692 * relationHasPrimaryKey -
694 * See whether an existing relation has a primary key.
697 relationHasPrimaryKey(Relation rel)
701 ListCell *indexoidscan;
704 * Get the list of index OIDs for the table from the relcache, and
705 * look up each one in the pg_index syscache until we find one marked
706 * primary key (hopefully there isn't more than one such).
708 indexoidlist = RelationGetIndexList(rel);
710 foreach(indexoidscan, indexoidlist)
712 Oid indexoid = lfirst_oid(indexoidscan);
713 HeapTuple indexTuple;
715 indexTuple = SearchSysCache(INDEXRELID,
716 ObjectIdGetDatum(indexoid),
718 if (!HeapTupleIsValid(indexTuple)) /* should not happen */
719 elog(ERROR, "cache lookup failed for index %u", indexoid);
720 result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
721 ReleaseSysCache(indexTuple);
726 list_free(indexoidlist);
737 RemoveIndex(RangeVar *relation, DropBehavior behavior)
741 ObjectAddress object;
743 indOid = RangeVarGetRelid(relation, false);
744 relkind = get_rel_relkind(indOid);
745 if (relkind != RELKIND_INDEX)
747 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
748 errmsg("\"%s\" is not an index",
749 relation->relname)));
751 object.classId = RelOid_pg_class;
752 object.objectId = indOid;
753 object.objectSubId = 0;
755 performDeletion(&object, behavior);
763 ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
768 indOid = RangeVarGetRelid(indexRelation, false);
769 tuple = SearchSysCache(RELOID,
770 ObjectIdGetDatum(indOid),
772 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
773 elog(ERROR, "cache lookup failed for relation %u", indOid);
775 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
777 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
778 errmsg("\"%s\" is not an index",
779 indexRelation->relname)));
781 /* Check permissions */
782 if (!pg_class_ownercheck(indOid, GetUserId()))
783 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
784 indexRelation->relname);
786 ReleaseSysCache(tuple);
788 reindex_index(indOid);
793 * Recreate indexes of a table.
796 ReindexTable(RangeVar *relation, bool force /* currently unused */ )
801 heapOid = RangeVarGetRelid(relation, false);
802 tuple = SearchSysCache(RELOID,
803 ObjectIdGetDatum(heapOid),
805 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
806 elog(ERROR, "cache lookup failed for relation %u", heapOid);
808 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION &&
809 ((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_TOASTVALUE)
811 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
812 errmsg("\"%s\" is not a table",
813 relation->relname)));
815 /* Check permissions */
816 if (!pg_class_ownercheck(heapOid, GetUserId()))
817 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
820 /* Can't reindex shared tables except in standalone mode */
821 if (((Form_pg_class) GETSTRUCT(tuple))->relisshared && IsUnderPostmaster)
823 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
824 errmsg("shared table \"%s\" can only be reindexed in stand-alone mode",
825 relation->relname)));
827 ReleaseSysCache(tuple);
829 if (!reindex_relation(heapOid, true))
831 (errmsg("table \"%s\" has no indexes",
832 relation->relname)));
837 * Recreate indexes of a database.
839 * To reduce the probability of deadlocks, each table is reindexed in a
840 * separate transaction, so we can release the lock on it right away.
843 ReindexDatabase(const char *dbname, bool force /* currently unused */,
846 Relation relationRelation;
849 MemoryContext private_context;
856 if (strcmp(dbname, get_database_name(MyDatabaseId)) != 0)
858 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
859 errmsg("can only reindex the currently open database")));
861 if (!pg_database_ownercheck(MyDatabaseId, GetUserId()))
862 aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
866 * We cannot run inside a user transaction block; if we were inside a
867 * transaction, then our commit- and start-transaction-command calls
868 * would not have the intended effect!
870 PreventTransactionChain((void *) dbname, "REINDEX DATABASE");
873 * Create a memory context that will survive forced transaction
874 * commits we do below. Since it is a child of PortalContext, it will
875 * go away eventually even if we suffer an error; there's no need for
876 * special abort cleanup logic.
878 private_context = AllocSetContextCreate(PortalContext,
880 ALLOCSET_DEFAULT_MINSIZE,
881 ALLOCSET_DEFAULT_INITSIZE,
882 ALLOCSET_DEFAULT_MAXSIZE);
885 * We always want to reindex pg_class first. This ensures that if
886 * there is any corruption in pg_class' indexes, they will be fixed
887 * before we process any other tables. This is critical because
888 * reindexing itself will try to update pg_class.
890 old = MemoryContextSwitchTo(private_context);
891 relids = lappend_oid(relids, RelOid_pg_class);
892 MemoryContextSwitchTo(old);
895 * Scan pg_class to build a list of the relations we need to reindex.
897 * We only consider plain relations here (toast rels will be processed
898 * indirectly by reindex_relation).
900 relationRelation = heap_openr(RelationRelationName, AccessShareLock);
901 scan = heap_beginscan(relationRelation, SnapshotNow, 0, NULL);
902 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
904 Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
906 if (classtuple->relkind != RELKIND_RELATION)
909 if (!all) /* only system tables? */
911 if (!IsSystemClass(classtuple))
915 if (IsUnderPostmaster) /* silently ignore shared tables */
917 if (classtuple->relisshared)
921 if (HeapTupleGetOid(tuple) == RelOid_pg_class)
922 continue; /* got it already */
924 old = MemoryContextSwitchTo(private_context);
925 relids = lappend_oid(relids, HeapTupleGetOid(tuple));
926 MemoryContextSwitchTo(old);
929 heap_close(relationRelation, AccessShareLock);
931 /* Now reindex each rel in a separate transaction */
932 CommitTransactionCommand();
935 Oid relid = lfirst_oid(l);
937 StartTransactionCommand();
938 SetQuerySnapshot(); /* might be needed for functions in
940 if (reindex_relation(relid, true))
942 (errmsg("table \"%s\" was reindexed",
943 get_rel_name(relid))));
944 CommitTransactionCommand();
946 StartTransactionCommand();
948 MemoryContextDelete(private_context);