]> granicus.if.org Git - postgresql/blob - src/backend/commands/indexcmds.c
Centralize code for interpreting schema references, which had gotten
[postgresql] / src / backend / commands / indexcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * indexcmds.c
4  *        POSTGRES define and remove index code.
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.79 2002/07/29 23:46:35 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "access/heapam.h"
19 #include "catalog/catalog.h"
20 #include "catalog/catname.h"
21 #include "catalog/dependency.h"
22 #include "catalog/index.h"
23 #include "catalog/namespace.h"
24 #include "catalog/pg_opclass.h"
25 #include "catalog/pg_proc.h"
26 #include "commands/defrem.h"
27 #include "miscadmin.h"
28 #include "optimizer/clauses.h"
29 #include "optimizer/planmain.h"
30 #include "optimizer/prep.h"
31 #include "parser/parsetree.h"
32 #include "parser/parse_coerce.h"
33 #include "parser/parse_func.h"
34 #include "utils/acl.h"
35 #include "utils/builtins.h"
36 #include "utils/lsyscache.h"
37 #include "utils/syscache.h"
38
39
40 #define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->funcname != NIL)
41
42 /* non-export function prototypes */
43 static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
44 static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
45                           IndexElem *funcIndex,
46                           Oid relId,
47                           char *accessMethodName, Oid accessMethodId);
48 static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
49                            List *attList,
50                            Oid relId,
51                            char *accessMethodName, Oid accessMethodId);
52 static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
53                            char *accessMethodName, Oid accessMethodId);
54 static Oid      GetDefaultOpClass(Oid attrType, Oid accessMethodId);
55
56 /*
57  * DefineIndex
58  *              Creates a new index.
59  *
60  * 'attributeList' is a list of IndexElem specifying either a functional
61  *              index or a list of attributes to index on.
62  * 'predicate' is the qual specified in the where clause.
63  * 'rangetable' is needed to interpret the predicate.
64  */
65 void
66 DefineIndex(RangeVar *heapRelation,
67                         char *indexRelationName,
68                         char *accessMethodName,
69                         List *attributeList,
70                         bool unique,
71                         bool primary,
72                         bool isconstraint,
73                         Expr *predicate,
74                         List *rangetable)
75 {
76         Oid                *classObjectId;
77         Oid                     accessMethodId;
78         Oid                     relationId;
79         Oid                     namespaceId;
80         Relation        rel;
81         HeapTuple       tuple;
82         Form_pg_am      accessMethodForm;
83         IndexInfo  *indexInfo;
84         int                     numberOfAttributes;
85         List       *cnfPred = NIL;
86
87         /*
88          * count attributes in index
89          */
90         numberOfAttributes = length(attributeList);
91         if (numberOfAttributes <= 0)
92                 elog(ERROR, "DefineIndex: must specify at least one attribute");
93         if (numberOfAttributes > INDEX_MAX_KEYS)
94                 elog(ERROR, "Cannot use more than %d attributes in an index",
95                          INDEX_MAX_KEYS);
96
97         /*
98          * Open heap relation, acquire a suitable lock on it, remember its OID
99          */
100         rel = heap_openrv(heapRelation, ShareLock);
101
102         /* Note: during bootstrap may see uncataloged relation */
103         if (rel->rd_rel->relkind != RELKIND_RELATION &&
104                 rel->rd_rel->relkind != RELKIND_UNCATALOGED)
105                 elog(ERROR, "DefineIndex: relation \"%s\" is not a table",
106                          heapRelation->relname);
107
108         relationId = RelationGetRelid(rel);
109         namespaceId = RelationGetNamespace(rel);
110
111         if (!IsBootstrapProcessingMode() &&
112                 IsSystemRelation(rel) &&
113                 !IndexesAreActive(relationId, false))
114                 elog(ERROR, "Existing indexes are inactive. REINDEX first");
115
116         heap_close(rel, NoLock);
117
118         /*
119          * Verify we (still) have CREATE rights in the rel's namespace.
120          * (Presumably we did when the rel was created, but maybe not anymore.)
121          * Skip check if bootstrapping, since permissions machinery may not
122          * be working yet; also, always allow if it's a temp table.
123          */
124         if (!IsBootstrapProcessingMode() && !isTempNamespace(namespaceId))
125         {
126                 AclResult       aclresult;
127
128                 aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
129                                                                                   ACL_CREATE);
130                 if (aclresult != ACLCHECK_OK)
131                         aclcheck_error(aclresult, get_namespace_name(namespaceId));
132         }
133
134         /*
135          * look up the access method, verify it can handle the requested
136          * features
137          */
138         tuple = SearchSysCache(AMNAME,
139                                                    PointerGetDatum(accessMethodName),
140                                                    0, 0, 0);
141         if (!HeapTupleIsValid(tuple))
142                 elog(ERROR, "DefineIndex: access method \"%s\" not found",
143                          accessMethodName);
144         accessMethodId = HeapTupleGetOid(tuple);
145         accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
146
147         if (unique && !accessMethodForm->amcanunique)
148                 elog(ERROR, "DefineIndex: access method \"%s\" does not support UNIQUE indexes",
149                          accessMethodName);
150         if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
151                 elog(ERROR, "DefineIndex: access method \"%s\" does not support multi-column indexes",
152                          accessMethodName);
153
154         ReleaseSysCache(tuple);
155
156         /*
157          * Convert the partial-index predicate from parsetree form to an
158          * implicit-AND qual expression, for easier evaluation at runtime.
159          * While we are at it, we reduce it to a canonical (CNF or DNF) form
160          * to simplify the task of proving implications.
161          */
162         if (predicate != NULL && rangetable != NIL)
163         {
164                 cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
165                 fix_opids((Node *) cnfPred);
166                 CheckPredicate(cnfPred, rangetable, relationId);
167         }
168
169         /*
170          * Prepare arguments for index_create, primarily an IndexInfo
171          * structure
172          */
173         indexInfo = makeNode(IndexInfo);
174         indexInfo->ii_Predicate = cnfPred;
175         indexInfo->ii_FuncOid = InvalidOid;
176         indexInfo->ii_Unique = unique;
177
178         if (IsFuncIndex(attributeList))
179         {
180                 IndexElem  *funcIndex = (IndexElem *) lfirst(attributeList);
181                 int                     nargs;
182
183                 /* Parser should have given us only one list item, but check */
184                 if (numberOfAttributes != 1)
185                         elog(ERROR, "Functional index can only have one attribute");
186
187                 nargs = length(funcIndex->args);
188                 if (nargs > INDEX_MAX_KEYS)
189                         elog(ERROR, "Index function can take at most %d arguments",
190                                  INDEX_MAX_KEYS);
191
192                 indexInfo->ii_NumIndexAttrs = 1;
193                 indexInfo->ii_NumKeyAttrs = nargs;
194
195                 classObjectId = (Oid *) palloc(sizeof(Oid));
196
197                 FuncIndexArgs(indexInfo, classObjectId, funcIndex,
198                                           relationId, accessMethodName, accessMethodId);
199         }
200         else
201         {
202                 indexInfo->ii_NumIndexAttrs = numberOfAttributes;
203                 indexInfo->ii_NumKeyAttrs = numberOfAttributes;
204
205                 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
206
207                 NormIndexAttrs(indexInfo, classObjectId, attributeList,
208                                            relationId, accessMethodName, accessMethodId);
209         }
210
211         index_create(relationId, indexRelationName,
212                                  indexInfo, accessMethodId, classObjectId,
213                                  primary, isconstraint, allowSystemTableMods);
214
215         /*
216          * We update the relation's pg_class tuple even if it already has
217          * relhasindex = true.  This is needed to cause a shared-cache-inval
218          * message to be sent for the pg_class tuple, which will cause other
219          * backends to flush their relcache entries and in particular their
220          * cached lists of the indexes for this relation.
221          */
222         setRelhasindex(relationId, true, primary, InvalidOid);
223 }
224
225
226 /*
227  * CheckPredicate
228  *              Checks that the given list of partial-index predicates refer
229  *              (via the given range table) only to the given base relation oid.
230  *
231  * This used to also constrain the form of the predicate to forms that
232  * indxpath.c could do something with.  However, that seems overly
233  * restrictive.  One useful application of partial indexes is to apply
234  * a UNIQUE constraint across a subset of a table, and in that scenario
235  * any evaluatable predicate will work.  So accept any predicate here
236  * (except ones requiring a plan), and let indxpath.c fend for itself.
237  */
238
239 static void
240 CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
241 {
242         if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid)
243                 elog(ERROR,
244                  "Partial-index predicates may refer only to the base relation");
245
246         /*
247          * We don't currently support generation of an actual query plan for a
248          * predicate, only simple scalar expressions; hence these
249          * restrictions.
250          */
251         if (contain_subplans((Node *) predList))
252                 elog(ERROR, "Cannot use subselect in index predicate");
253         if (contain_agg_clause((Node *) predList))
254                 elog(ERROR, "Cannot use aggregate in index predicate");
255
256         /*
257          * A predicate using mutable functions is probably wrong, for the
258          * same reasons that we don't allow a functional index to use one.
259          */
260         if (contain_mutable_functions((Node *) predList))
261                 elog(ERROR, "Functions in index predicate must be marked isImmutable");
262 }
263
264
265 static void
266 FuncIndexArgs(IndexInfo *indexInfo,
267                           Oid *classOidP,
268                           IndexElem *funcIndex,
269                           Oid relId,
270                           char *accessMethodName,
271                           Oid accessMethodId)
272 {
273         Oid                     argTypes[FUNC_MAX_ARGS];
274         List       *arglist;
275         int                     nargs = 0;
276         int                     i;
277         FuncDetailCode fdresult;
278         Oid                     funcid;
279         Oid                     rettype;
280         bool            retset;
281         Oid                *true_typeids;
282
283         /*
284          * process the function arguments, which are a list of T_String
285          * (someday ought to allow more general expressions?)
286          *
287          * Note caller already checked that list is not too long.
288          */
289         MemSet(argTypes, 0, sizeof(argTypes));
290
291         foreach(arglist, funcIndex->args)
292         {
293                 char       *arg = strVal(lfirst(arglist));
294                 HeapTuple       tuple;
295                 Form_pg_attribute att;
296
297                 tuple = SearchSysCache(ATTNAME,
298                                                            ObjectIdGetDatum(relId),
299                                                            PointerGetDatum(arg),
300                                                            0, 0);
301                 if (!HeapTupleIsValid(tuple))
302                         elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg);
303                 att = (Form_pg_attribute) GETSTRUCT(tuple);
304                 indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum;
305                 argTypes[nargs] = att->atttypid;
306                 ReleaseSysCache(tuple);
307                 nargs++;
308         }
309
310         /*
311          * Lookup the function procedure to get its OID and result type.
312          *
313          * We rely on parse_func.c to find the correct function in the possible
314          * presence of binary-compatible types.  However, parse_func may do
315          * too much: it will accept a function that requires run-time coercion
316          * of input types, and the executor is not currently set up to support
317          * that.  So, check to make sure that the selected function has
318          * exact-match or binary-compatible input types.
319          */
320         fdresult = func_get_detail(funcIndex->funcname, funcIndex->args,
321                                                            nargs, argTypes,
322                                                            &funcid, &rettype, &retset,
323                                                            &true_typeids);
324         if (fdresult != FUNCDETAIL_NORMAL)
325         {
326                 if (fdresult == FUNCDETAIL_AGGREGATE)
327                         elog(ERROR, "DefineIndex: functional index may not use an aggregate function");
328                 else if (fdresult == FUNCDETAIL_COERCION)
329                         elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
330                                  "\n\tTry specifying the index opclass you want to use, instead");
331                 else
332                         func_error("DefineIndex", funcIndex->funcname, nargs, argTypes,
333                                            NULL);
334         }
335
336         if (retset)
337                 elog(ERROR, "DefineIndex: cannot index on a function returning a set");
338
339         for (i = 0; i < nargs; i++)
340         {
341                 if (!IsBinaryCompatible(argTypes[i], true_typeids[i]))
342                         func_error("DefineIndex", funcIndex->funcname, nargs, argTypes,
343                                            "Index function must be binary-compatible with table datatype");
344         }
345
346         /*
347          * Require that the function be marked immutable.  Using a mutable
348          * function for a functional index is highly questionable, since if
349          * you aren't going to get the same result for the same data every
350          * time, it's not clear what the index entries mean at all.
351          */
352         if (func_volatile(funcid) != PROVOLATILE_IMMUTABLE)
353                 elog(ERROR, "DefineIndex: index function must be marked isImmutable");
354
355         /* Process opclass, using func return type as default type */
356
357         classOidP[0] = GetAttrOpClass(funcIndex, rettype,
358                                                                   accessMethodName, accessMethodId);
359
360         /* OK, return results */
361
362         indexInfo->ii_FuncOid = funcid;
363         /* Need to do the fmgr function lookup now, too */
364         fmgr_info(funcid, &indexInfo->ii_FuncInfo);
365 }
366
367 static void
368 NormIndexAttrs(IndexInfo *indexInfo,
369                            Oid *classOidP,
370                            List *attList,       /* list of IndexElem's */
371                            Oid relId,
372                            char *accessMethodName,
373                            Oid accessMethodId)
374 {
375         List       *rest;
376         int                     attn = 0;
377
378         /*
379          * process attributeList
380          */
381         foreach(rest, attList)
382         {
383                 IndexElem  *attribute = (IndexElem *) lfirst(rest);
384                 HeapTuple       atttuple;
385                 Form_pg_attribute attform;
386
387                 if (attribute->name == NULL)
388                         elog(ERROR, "missing attribute for define index");
389
390                 atttuple = SearchSysCache(ATTNAME,
391                                                                   ObjectIdGetDatum(relId),
392                                                                   PointerGetDatum(attribute->name),
393                                                                   0, 0);
394                 if (!HeapTupleIsValid(atttuple))
395                         elog(ERROR, "DefineIndex: attribute \"%s\" not found",
396                                  attribute->name);
397                 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
398
399                 indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
400
401                 classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
402                                                                            accessMethodName, accessMethodId);
403
404                 ReleaseSysCache(atttuple);
405                 attn++;
406         }
407 }
408
409 static Oid
410 GetAttrOpClass(IndexElem *attribute, Oid attrType,
411                            char *accessMethodName, Oid accessMethodId)
412 {
413         char       *schemaname;
414         char       *opcname;
415         HeapTuple       tuple;
416         Oid                     opClassId,
417                                 opInputType;
418
419         if (attribute->opclass == NIL)
420         {
421                 /* no operator class specified, so find the default */
422                 opClassId = GetDefaultOpClass(attrType, accessMethodId);
423                 if (!OidIsValid(opClassId))
424                         elog(ERROR, "data type %s has no default operator class for access method \"%s\""
425                                  "\n\tYou must specify an operator class for the index or define a"
426                                  "\n\tdefault operator class for the data type",
427                                  format_type_be(attrType), accessMethodName);
428                 return opClassId;
429         }
430
431         /*
432          * Specific opclass name given, so look up the opclass.
433          */
434
435         /* deconstruct the name list */
436         DeconstructQualifiedName(attribute->opclass, &schemaname, &opcname);
437
438         if (schemaname)
439         {
440                 /* Look in specific schema only */
441                 Oid             namespaceId;
442
443                 namespaceId = LookupExplicitNamespace(schemaname);
444                 tuple = SearchSysCache(CLAAMNAMENSP,
445                                                            ObjectIdGetDatum(accessMethodId),
446                                                            PointerGetDatum(opcname),
447                                                            ObjectIdGetDatum(namespaceId),
448                                                            0);
449         }
450         else
451         {
452                 /* Unqualified opclass name, so search the search path */
453                 opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
454                 if (!OidIsValid(opClassId))
455                         elog(ERROR, "DefineIndex: operator class \"%s\" not supported by access method \"%s\"",
456                                  opcname, accessMethodName);
457                 tuple = SearchSysCache(CLAOID,
458                                                            ObjectIdGetDatum(opClassId),
459                                                            0, 0, 0);
460         }
461
462         if (!HeapTupleIsValid(tuple))
463                 elog(ERROR, "DefineIndex: operator class \"%s\" not supported by access method \"%s\"",
464                          NameListToString(attribute->opclass), accessMethodName);
465
466         /*
467          * Verify that the index operator class accepts this
468          * datatype.  Note we will accept binary compatibility.
469          */
470         opClassId = HeapTupleGetOid(tuple);
471         opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
472
473         if (!IsBinaryCompatible(attrType, opInputType))
474                 elog(ERROR, "operator class \"%s\" does not accept data type %s",
475                          NameListToString(attribute->opclass), format_type_be(attrType));
476
477         ReleaseSysCache(tuple);
478
479         return opClassId;
480 }
481
482 static Oid
483 GetDefaultOpClass(Oid attrType, Oid accessMethodId)
484 {
485         OpclassCandidateList opclass;
486         int                     nexact = 0;
487         int                     ncompatible = 0;
488         Oid                     exactOid = InvalidOid;
489         Oid                     compatibleOid = InvalidOid;
490
491         /*
492          * We scan through all the opclasses available for the access method,
493          * looking for one that is marked default and matches the target type
494          * (either exactly or binary-compatibly, but prefer an exact match).
495          *
496          * We could find more than one binary-compatible match, in which case we
497          * require the user to specify which one he wants.      If we find more
498          * than one exact match, then someone put bogus entries in pg_opclass.
499          *
500          * The initial search is done by namespace.c so that we only consider
501          * opclasses visible in the current namespace search path.
502          */
503         for (opclass = OpclassGetCandidates(accessMethodId);
504                  opclass != NULL;
505                  opclass = opclass->next)
506         {
507                 if (opclass->opcdefault)
508                 {
509                         if (opclass->opcintype == attrType)
510                         {
511                                 nexact++;
512                                 exactOid = opclass->oid;
513                         }
514                         else if (IsBinaryCompatible(opclass->opcintype, attrType))
515                         {
516                                 ncompatible++;
517                                 compatibleOid = opclass->oid;
518                         }
519                 }
520         }
521
522         if (nexact == 1)
523                 return exactOid;
524         if (nexact != 0)
525                 elog(ERROR, "pg_opclass contains multiple default opclasses for data type %s",
526                          format_type_be(attrType));
527         if (ncompatible == 1)
528                 return compatibleOid;
529
530         return InvalidOid;
531 }
532
533 /*
534  * RemoveIndex
535  *              Deletes an index.
536  */
537 void
538 RemoveIndex(RangeVar *relation, DropBehavior behavior)
539 {
540         Oid                     indOid;
541         HeapTuple       tuple;
542         ObjectAddress object;
543
544         indOid = RangeVarGetRelid(relation, false);
545         tuple = SearchSysCache(RELOID,
546                                                    ObjectIdGetDatum(indOid),
547                                                    0, 0, 0);
548         if (!HeapTupleIsValid(tuple))
549                 elog(ERROR, "index \"%s\" does not exist", relation->relname);
550
551         if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
552                 elog(ERROR, "relation \"%s\" is of type \"%c\"",
553                          relation->relname, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
554
555         ReleaseSysCache(tuple);
556
557         object.classId = RelOid_pg_class;
558         object.objectId = indOid;
559         object.objectSubId = 0;
560
561         performDeletion(&object, behavior);
562 }
563
564 /*
565  * Reindex
566  *              Recreate an index.
567  *
568  * Exceptions:
569  *              "ERROR" if index nonexistent.
570  *              ...
571  */
572 void
573 ReindexIndex(RangeVar *indexRelation, bool force /* currently unused */ )
574 {
575         Oid                     indOid;
576         HeapTuple       tuple;
577         bool            overwrite = false;
578
579         /*
580          * REINDEX within a transaction block is dangerous, because if the
581          * transaction is later rolled back we have no way to undo truncation
582          * of the index's physical file.  Disallow it.
583          */
584         if (IsTransactionBlock())
585                 elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
586
587         indOid = RangeVarGetRelid(indexRelation, false);
588         tuple = SearchSysCache(RELOID,
589                                                    ObjectIdGetDatum(indOid),
590                                                    0, 0, 0);
591         if (!HeapTupleIsValid(tuple))
592                 elog(ERROR, "index \"%s\" does not exist", indexRelation->relname);
593
594         if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
595                 elog(ERROR, "relation \"%s\" is of type \"%c\"",
596                          indexRelation->relname,
597                          ((Form_pg_class) GETSTRUCT(tuple))->relkind);
598
599         if (IsSystemClass((Form_pg_class) GETSTRUCT(tuple)))
600         {
601                 if (!allowSystemTableMods)
602                         elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -O -P options",
603                                  indexRelation->relname);
604                 if (!IsIgnoringSystemIndexes())
605                         elog(ERROR, "\"%s\" is a system index. call REINDEX under standalone postgres with -P -O options",
606                                  indexRelation->relname);
607         }
608
609         ReleaseSysCache(tuple);
610
611         if (IsIgnoringSystemIndexes())
612                 overwrite = true;
613         if (!reindex_index(indOid, force, overwrite))
614                 elog(WARNING, "index \"%s\" wasn't reindexed", indexRelation->relname);
615 }
616
617 /*
618  * ReindexTable
619  *              Recreate indexes of a table.
620  *
621  * Exceptions:
622  *              "ERROR" if table nonexistent.
623  *              ...
624  */
625 void
626 ReindexTable(RangeVar *relation, bool force)
627 {
628         Oid                     heapOid;
629         HeapTuple       tuple;
630
631         /*
632          * REINDEX within a transaction block is dangerous, because if the
633          * transaction is later rolled back we have no way to undo truncation
634          * of the index's physical file.  Disallow it.
635          */
636         if (IsTransactionBlock())
637                 elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
638
639         heapOid = RangeVarGetRelid(relation, false);
640         tuple = SearchSysCache(RELOID,
641                                                    ObjectIdGetDatum(heapOid),
642                                                    0, 0, 0);
643         if (!HeapTupleIsValid(tuple))
644                 elog(ERROR, "table \"%s\" does not exist", relation->relname);
645
646         if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION)
647                 elog(ERROR, "relation \"%s\" is of type \"%c\"",
648                          relation->relname,
649                          ((Form_pg_class) GETSTRUCT(tuple))->relkind);
650
651         ReleaseSysCache(tuple);
652
653         if (!reindex_relation(heapOid, force))
654                 elog(WARNING, "table \"%s\" wasn't reindexed", relation->relname);
655 }
656
657 /*
658  * ReindexDatabase
659  *              Recreate indexes of a database.
660  */
661 void
662 ReindexDatabase(const char *dbname, bool force, bool all)
663 {
664         Relation        relationRelation;
665         HeapScanDesc scan;
666         HeapTuple       tuple;
667         MemoryContext private_context;
668         MemoryContext old;
669         int                     relcnt,
670                                 relalc,
671                                 i,
672                                 oncealc = 200;
673         Oid                *relids = (Oid *) NULL;
674
675         AssertArg(dbname);
676
677         if (strcmp(dbname, DatabaseName) != 0)
678                 elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");
679
680         if (!(superuser() || is_dbadmin(MyDatabaseId)))
681                 elog(ERROR, "REINDEX DATABASE: Permission denied.");
682
683         if (!allowSystemTableMods)
684                 elog(ERROR, "must be called under standalone postgres with -O -P options");
685         if (!IsIgnoringSystemIndexes())
686                 elog(ERROR, "must be called under standalone postgres with -P -O options");
687
688         /*
689          * We cannot run inside a user transaction block; if we were inside a
690          * transaction, then our commit- and start-transaction-command calls
691          * would not have the intended effect!
692          */
693         if (IsTransactionBlock())
694                 elog(ERROR, "REINDEX DATABASE cannot run inside a BEGIN/END block");
695
696         /*
697          * Create a memory context that will survive forced transaction
698          * commits we do below.  Since it is a child of QueryContext, it will
699          * go away eventually even if we suffer an error; there's no need for
700          * special abort cleanup logic.
701          */
702         private_context = AllocSetContextCreate(QueryContext,
703                                                                                         "ReindexDatabase",
704                                                                                         ALLOCSET_DEFAULT_MINSIZE,
705                                                                                         ALLOCSET_DEFAULT_INITSIZE,
706                                                                                         ALLOCSET_DEFAULT_MAXSIZE);
707
708         /*
709          * Scan pg_class to build a list of the relations we need to reindex.
710          */
711         relationRelation = heap_openr(RelationRelationName, AccessShareLock);
712         scan = heap_beginscan(relationRelation, SnapshotNow, 0, NULL);
713         relcnt = relalc = 0;
714         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
715         {
716                 if (!all)
717                 {
718                         if (!IsSystemClass((Form_pg_class) GETSTRUCT(tuple)))
719                                 continue;
720                 }
721                 if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
722                 {
723                         old = MemoryContextSwitchTo(private_context);
724                         if (relcnt == 0)
725                         {
726                                 relalc = oncealc;
727                                 relids = palloc(sizeof(Oid) * relalc);
728                         }
729                         else if (relcnt >= relalc)
730                         {
731                                 relalc *= 2;
732                                 relids = repalloc(relids, sizeof(Oid) * relalc);
733                         }
734                         MemoryContextSwitchTo(old);
735                         relids[relcnt] = HeapTupleGetOid(tuple);
736                         relcnt++;
737                 }
738         }
739         heap_endscan(scan);
740         heap_close(relationRelation, AccessShareLock);
741
742         /* Now reindex each rel in a separate transaction */
743         CommitTransactionCommand();
744         for (i = 0; i < relcnt; i++)
745         {
746                 StartTransactionCommand();
747                 if (reindex_relation(relids[i], force))
748                         elog(WARNING, "relation %u was reindexed", relids[i]);
749                 CommitTransactionCommand();
750         }
751         StartTransactionCommand();
752
753         MemoryContextDelete(private_context);
754 }