]> granicus.if.org Git - postgresql/blob - src/backend/commands/indexcmds.c
Remove OLD_FILE_NAMING code. No longer used.
[postgresql] / src / backend / commands / indexcmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * indexcmds.c
4  *        POSTGRES define, extend and remove index code.
5  *
6  * Portions Copyright (c) 1996-2001, 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.48 2001/05/30 20:52:32 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "access/genam.h"
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/catname.h"
22 #include "catalog/heap.h"
23 #include "catalog/index.h"
24 #include "catalog/pg_am.h"
25 #include "catalog/pg_amop.h"
26 #include "catalog/pg_database.h"
27 #include "catalog/pg_index.h"
28 #include "catalog/pg_opclass.h"
29 #include "catalog/pg_operator.h"
30 #include "catalog/pg_proc.h"
31 #include "catalog/pg_shadow.h"
32 #include "commands/defrem.h"
33 #include "miscadmin.h"
34 #include "optimizer/clauses.h"
35 #include "optimizer/planmain.h"
36 #include "optimizer/prep.h"
37 #include "parser/parsetree.h"
38 #include "parser/parse_coerce.h"
39 #include "parser/parse_func.h"
40 #include "parser/parse_type.h"
41 #include "utils/builtins.h"
42 #include "utils/fmgroids.h"
43 #include "utils/syscache.h"
44
45 #define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args != NIL)
46
47 /* non-export function prototypes */
48 static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
49 static void CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid);
50 static void CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
51 static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
52                           IndexElem *funcIndex,
53                           Oid relId,
54                           char *accessMethodName, Oid accessMethodId);
55 static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
56                            List *attList,
57                            Oid relId,
58                            char *accessMethodName, Oid accessMethodId);
59 static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
60                            char *accessMethodName, Oid accessMethodId);
61 static char *GetDefaultOpClass(Oid atttypid);
62
63 /*
64  * DefineIndex
65  *              Creates a new index.
66  *
67  * 'attributeList' is a list of IndexElem specifying either a functional
68  *              index or a list of attributes to index on.
69  * 'parameterList' is a list of DefElem specified in the with clause.
70  * 'predicate' is the qual specified in the where clause.
71  * 'rangetable' is needed to interpret the predicate
72  */
73 void
74 DefineIndex(char *heapRelationName,
75                         char *indexRelationName,
76                         char *accessMethodName,
77                         List *attributeList,
78                         List *parameterList,
79                         bool unique,
80                         bool primary,
81                         Expr *predicate,
82                         List *rangetable)
83 {
84         Oid                *classObjectId;
85         Oid                     accessMethodId;
86         Oid                     relationId;
87         IndexInfo  *indexInfo;
88         int                     numberOfAttributes;
89         List       *cnfPred = NIL;
90         bool            lossy = false;
91         List       *pl;
92
93         /*
94          * count attributes in index
95          */
96         numberOfAttributes = length(attributeList);
97         if (numberOfAttributes <= 0)
98                 elog(ERROR, "DefineIndex: must specify at least one attribute");
99         if (numberOfAttributes > INDEX_MAX_KEYS)
100                 elog(ERROR, "Cannot use more than %d attributes in an index",
101                          INDEX_MAX_KEYS);
102
103         /*
104          * compute heap relation id
105          */
106         if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid)
107                 elog(ERROR, "DefineIndex: relation \"%s\" not found",
108                          heapRelationName);
109
110         /*
111          * compute access method id
112          */
113         accessMethodId = GetSysCacheOid(AMNAME,
114                                                                         PointerGetDatum(accessMethodName),
115                                                                         0, 0, 0);
116         if (!OidIsValid(accessMethodId))
117                 elog(ERROR, "DefineIndex: access method \"%s\" not found",
118                          accessMethodName);
119
120         /*
121          * XXX Hardwired hacks to check for limitations on supported index
122          * types. We really ought to be learning this info from entries in the
123          * pg_am table, instead of having it wired in here!
124          */
125         if (unique && accessMethodId != BTREE_AM_OID)
126                 elog(ERROR, "DefineIndex: unique indices are only available with the btree access method");
127
128         if (numberOfAttributes > 1 && accessMethodId != BTREE_AM_OID)
129                 elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method");
130
131         /*
132          * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
133          */
134         foreach(pl, parameterList)
135         {
136                 DefElem    *param = (DefElem *) lfirst(pl);
137
138                 if (!strcasecmp(param->defname, "islossy"))
139                         lossy = true;
140                 else
141                         elog(NOTICE, "Unrecognized index attribute \"%s\" ignored",
142                                  param->defname);
143         }
144
145         /*
146          * Convert the partial-index predicate from parsetree form to plan
147          * form, so it can be readily evaluated during index creation. Note:
148          * "predicate" comes in as a list containing (1) the predicate itself
149          * (a where_clause), and (2) a corresponding range table.
150          *
151          * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
152          */
153         if (predicate != NULL && rangetable != NIL)
154         {
155                 cnfPred = cnfify((Expr *) copyObject(predicate), true);
156                 fix_opids((Node *) cnfPred);
157                 CheckPredicate(cnfPred, rangetable, relationId);
158         }
159
160         if (!IsBootstrapProcessingMode() && !IndexesAreActive(relationId, false))
161                 elog(ERROR, "Existing indexes are inactive. REINDEX first");
162
163         /*
164          * Prepare arguments for index_create, primarily an IndexInfo
165          * structure
166          */
167         indexInfo = makeNode(IndexInfo);
168         indexInfo->ii_Predicate = (Node *) cnfPred;
169         indexInfo->ii_FuncOid = InvalidOid;
170         indexInfo->ii_Unique = unique;
171
172         if (IsFuncIndex(attributeList))
173         {
174                 IndexElem  *funcIndex = (IndexElem *) lfirst(attributeList);
175                 int                     nargs;
176
177                 /* Parser should have given us only one list item, but check */
178                 if (numberOfAttributes != 1)
179                         elog(ERROR, "Functional index can only have one attribute");
180
181                 nargs = length(funcIndex->args);
182                 if (nargs > INDEX_MAX_KEYS)
183                         elog(ERROR, "Index function can take at most %d arguments",
184                                  INDEX_MAX_KEYS);
185
186                 indexInfo->ii_NumIndexAttrs = 1;
187                 indexInfo->ii_NumKeyAttrs = nargs;
188
189                 classObjectId = (Oid *) palloc(sizeof(Oid));
190
191                 FuncIndexArgs(indexInfo, classObjectId, funcIndex,
192                                           relationId, accessMethodName, accessMethodId);
193         }
194         else
195         {
196                 indexInfo->ii_NumIndexAttrs = numberOfAttributes;
197                 indexInfo->ii_NumKeyAttrs = numberOfAttributes;
198
199                 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
200
201                 NormIndexAttrs(indexInfo, classObjectId, attributeList,
202                                            relationId, accessMethodName, accessMethodId);
203         }
204
205         index_create(heapRelationName, indexRelationName,
206                                  indexInfo, accessMethodId, classObjectId,
207                                  lossy, primary, allowSystemTableMods);
208
209         /*
210          * We update the relation's pg_class tuple even if it already has
211          * relhasindex = true.  This is needed to cause a shared-cache-inval
212          * message to be sent for the pg_class tuple, which will cause other
213          * backends to flush their relcache entries and in particular their
214          * cached lists of the indexes for this relation.
215          */
216         setRelhasindex(relationId, true);
217 }
218
219
220 /*
221  * ExtendIndex
222  *              Extends a partial index.
223  */
224 void
225 ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
226 {
227         Relation        heapRelation;
228         Relation        indexRelation;
229         Oid                     accessMethodId,
230                                 indexId,
231                                 relationId;
232         HeapTuple       tuple;
233         Form_pg_index index;
234         List       *cnfPred = NIL;
235         IndexInfo  *indexInfo;
236         Node       *oldPred;
237
238         /*
239          * Get index's relation id and access method id from pg_class
240          */
241         tuple = SearchSysCache(RELNAME,
242                                                    PointerGetDatum(indexRelationName),
243                                                    0, 0, 0);
244         if (!HeapTupleIsValid(tuple))
245                 elog(ERROR, "ExtendIndex: index \"%s\" not found",
246                          indexRelationName);
247         indexId = tuple->t_data->t_oid;
248         accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
249         ReleaseSysCache(tuple);
250
251         /*
252          * Extract info from the pg_index tuple for the index
253          */
254         tuple = SearchSysCache(INDEXRELID,
255                                                    ObjectIdGetDatum(indexId),
256                                                    0, 0, 0);
257         if (!HeapTupleIsValid(tuple))
258                 elog(ERROR, "ExtendIndex: relation \"%s\" is not an index",
259                          indexRelationName);
260         index = (Form_pg_index) GETSTRUCT(tuple);
261         Assert(index->indexrelid == indexId);
262         relationId = index->indrelid;
263         indexInfo = BuildIndexInfo(tuple);
264         oldPred = indexInfo->ii_Predicate;
265         ReleaseSysCache(tuple);
266
267         if (oldPred == NULL)
268                 elog(ERROR, "ExtendIndex: \"%s\" is not a partial index",
269                          indexRelationName);
270
271         /*
272          * Convert the extension predicate from parsetree form to plan form,
273          * so it can be readily evaluated during index creation. Note:
274          * "predicate" comes in two parts (1) the predicate expression itself,
275          * and (2) a corresponding range table.
276          *
277          * XXX I think this code is broken --- index_build expects a single
278          * expression not a list --- tgl Jul 00
279          */
280         if (rangetable != NIL)
281         {
282                 cnfPred = cnfify((Expr *) copyObject(predicate), true);
283                 fix_opids((Node *) cnfPred);
284                 CheckPredicate(cnfPred, rangetable, relationId);
285         }
286
287         /* pass new predicate to index_build */
288         indexInfo->ii_Predicate = (Node *) cnfPred;
289
290         /* Open heap and index rels, and get suitable locks */
291         heapRelation = heap_open(relationId, ShareLock);
292         indexRelation = index_open(indexId);
293
294         /* Obtain exclusive lock on it, just to be sure */
295         LockRelation(indexRelation, AccessExclusiveLock);
296
297         InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
298                                           indexRelation, accessMethodId);
299
300         index_build(heapRelation, indexRelation, indexInfo, oldPred);
301
302         /* heap and index rels are closed as a side-effect of index_build */
303 }
304
305
306 /*
307  * CheckPredicate
308  *              Checks that the given list of partial-index predicates refer
309  *              (via the given range table) only to the given base relation oid,
310  *              and that they're in a form the planner can handle, i.e.,
311  *              boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
312  *              has to be on the left).
313  */
314
315 static void
316 CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
317 {
318         List       *item;
319
320         foreach(item, predList)
321                 CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
322 }
323
324 static void
325 CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
326 {
327         List       *clauses = NIL,
328                            *clause;
329
330         if (is_opclause(predicate))
331         {
332                 CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
333                 return;
334         }
335         else if (or_clause(predicate) || and_clause(predicate))
336                 clauses = ((Expr *) predicate)->args;
337         else
338                 elog(ERROR, "Unsupported partial-index predicate expression type");
339
340         foreach(clause, clauses)
341                 CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
342 }
343
344 static void
345 CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
346 {
347         Var                *pred_var;
348         Const      *pred_const;
349
350         pred_var = (Var *) get_leftop(predicate);
351         pred_const = (Const *) get_rightop(predicate);
352
353         if (!IsA(predicate->oper, Oper) ||
354                 !IsA(pred_var, Var) ||
355                 !IsA(pred_const, Const))
356                 elog(ERROR, "Unsupported partial-index predicate clause type");
357
358         if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
359                 elog(ERROR,
360                  "Partial-index predicates may refer only to the base relation");
361 }
362
363
364 static void
365 FuncIndexArgs(IndexInfo *indexInfo,
366                           Oid *classOidP,
367                           IndexElem *funcIndex,
368                           Oid relId,
369                           char *accessMethodName,
370                           Oid accessMethodId)
371 {
372         Oid                     argTypes[FUNC_MAX_ARGS];
373         List       *arglist;
374         int                     nargs = 0;
375         int                     i;
376         Oid                     funcid;
377         Oid                     rettype;
378         bool            retset;
379         Oid                *true_typeids;
380
381         /*
382          * process the function arguments, which are a list of T_String
383          * (someday ought to allow more general expressions?)
384          *
385          * Note caller already checked that list is not too long.
386          */
387         MemSet(argTypes, 0, sizeof(argTypes));
388
389         foreach(arglist, funcIndex->args)
390         {
391                 char       *arg = strVal(lfirst(arglist));
392                 HeapTuple       tuple;
393                 Form_pg_attribute att;
394
395                 tuple = SearchSysCache(ATTNAME,
396                                                            ObjectIdGetDatum(relId),
397                                                            PointerGetDatum(arg),
398                                                            0, 0);
399                 if (!HeapTupleIsValid(tuple))
400                         elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg);
401                 att = (Form_pg_attribute) GETSTRUCT(tuple);
402                 indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum;
403                 argTypes[nargs] = att->atttypid;
404                 ReleaseSysCache(tuple);
405                 nargs++;
406         }
407
408         /*
409          * Lookup the function procedure to get its OID and result type.
410          *
411          * We rely on parse_func.c to find the correct function in the possible
412          * presence of binary-compatible types.  However, parse_func may do
413          * too much: it will accept a function that requires run-time coercion
414          * of input types, and the executor is not currently set up to support
415          * that.  So, check to make sure that the selected function has
416          * exact-match or binary-compatible input types.
417          */
418         if (!func_get_detail(funcIndex->name, nargs, argTypes,
419                                                  &funcid, &rettype, &retset, &true_typeids))
420                 func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
421
422         if (retset)
423                 elog(ERROR, "DefineIndex: cannot index on a function returning a set");
424
425         for (i = 0; i < nargs; i++)
426         {
427                 if (argTypes[i] != true_typeids[i] &&
428                         !IS_BINARY_COMPATIBLE(argTypes[i], true_typeids[i]))
429                         func_error("DefineIndex", funcIndex->name, nargs, argTypes,
430                                            "Index function must be binary-compatible with table datatype");
431         }
432
433         /* Process opclass, using func return type as default type */
434
435         classOidP[0] = GetAttrOpClass(funcIndex, rettype,
436                                                                   accessMethodName, accessMethodId);
437
438         /* OK, return results */
439
440         indexInfo->ii_FuncOid = funcid;
441         /* Need to do the fmgr function lookup now, too */
442         fmgr_info(funcid, &indexInfo->ii_FuncInfo);
443 }
444
445 static void
446 NormIndexAttrs(IndexInfo *indexInfo,
447                            Oid *classOidP,
448                            List *attList,       /* list of IndexElem's */
449                            Oid relId,
450                            char *accessMethodName,
451                            Oid accessMethodId)
452 {
453         List       *rest;
454         int                     attn = 0;
455
456         /*
457          * process attributeList
458          */
459         foreach(rest, attList)
460         {
461                 IndexElem  *attribute = (IndexElem *) lfirst(rest);
462                 HeapTuple       atttuple;
463                 Form_pg_attribute attform;
464
465                 if (attribute->name == NULL)
466                         elog(ERROR, "missing attribute for define index");
467
468                 atttuple = SearchSysCache(ATTNAME,
469                                                                   ObjectIdGetDatum(relId),
470                                                                   PointerGetDatum(attribute->name),
471                                                                   0, 0);
472                 if (!HeapTupleIsValid(atttuple))
473                         elog(ERROR, "DefineIndex: attribute \"%s\" not found",
474                                  attribute->name);
475                 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
476
477                 indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
478
479                 classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
480                                                                            accessMethodName, accessMethodId);
481
482                 ReleaseSysCache(atttuple);
483                 attn++;
484         }
485 }
486
487 static Oid
488 GetAttrOpClass(IndexElem *attribute, Oid attrType,
489                            char *accessMethodName, Oid accessMethodId)
490 {
491         Relation        relation;
492         HeapScanDesc scan;
493         ScanKeyData entry[2];
494         HeapTuple       tuple;
495         Oid                     opClassId,
496                                 oprId;
497         bool            doTypeCheck = true;
498
499         if (attribute->class == NULL)
500         {
501                 /* no operator class specified, so find the default */
502                 attribute->class = GetDefaultOpClass(attrType);
503                 if (attribute->class == NULL)
504                         elog(ERROR, "DefineIndex: type %s has no default operator class",
505                                  typeidTypeName(attrType));
506                 /* assume we need not check type compatibility */
507                 doTypeCheck = false;
508         }
509
510         opClassId = GetSysCacheOid(CLANAME,
511                                                            PointerGetDatum(attribute->class),
512                                                            0, 0, 0);
513         if (!OidIsValid(opClassId))
514                 elog(ERROR, "DefineIndex: opclass \"%s\" not found",
515                          attribute->class);
516
517         /*
518          * Assume the opclass is supported by this index access method if we
519          * can find at least one relevant entry in pg_amop.
520          */
521         ScanKeyEntryInitialize(&entry[0], 0,
522                                                    Anum_pg_amop_amopid,
523                                                    F_OIDEQ,
524                                                    ObjectIdGetDatum(accessMethodId));
525         ScanKeyEntryInitialize(&entry[1], 0,
526                                                    Anum_pg_amop_amopclaid,
527                                                    F_OIDEQ,
528                                                    ObjectIdGetDatum(opClassId));
529
530         relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
531         scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
532
533         if (!HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
534                 elog(ERROR, "DefineIndex: opclass \"%s\" not supported by access method \"%s\"",
535                          attribute->class, accessMethodName);
536
537         oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;
538
539         heap_endscan(scan);
540         heap_close(relation, AccessShareLock);
541
542         /*
543          * Make sure the operators associated with this opclass actually
544          * accept the column data type.  This prevents possible coredumps
545          * caused by user errors like applying text_ops to an int4 column.      We
546          * will accept an opclass as OK if the operator's input datatype is
547          * binary-compatible with the actual column datatype.  Note we assume
548          * that all the operators associated with an opclass accept the same
549          * datatypes, so checking the first one we happened to find in the
550          * table is sufficient.
551          *
552          * If the opclass was the default for the datatype, assume we can skip
553          * this check --- that saves a few cycles in the most common case. If
554          * pg_opclass is wrong then we're probably screwed anyway...
555          */
556         if (doTypeCheck)
557         {
558                 tuple = SearchSysCache(OPEROID,
559                                                            ObjectIdGetDatum(oprId),
560                                                            0, 0, 0);
561                 if (HeapTupleIsValid(tuple))
562                 {
563                         Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
564                         Oid                     opInputType = (optup->oprkind == 'l') ?
565                         optup->oprright : optup->oprleft;
566
567                         if (attrType != opInputType &&
568                                 !IS_BINARY_COMPATIBLE(attrType, opInputType))
569                                 elog(ERROR, "DefineIndex: opclass \"%s\" does not accept datatype \"%s\"",
570                                          attribute->class, typeidTypeName(attrType));
571                         ReleaseSysCache(tuple);
572                 }
573         }
574
575         return opClassId;
576 }
577
578 static char *
579 GetDefaultOpClass(Oid atttypid)
580 {
581         HeapTuple       tuple;
582         char       *result;
583
584         tuple = SearchSysCache(CLADEFTYPE,
585                                                    ObjectIdGetDatum(atttypid),
586                                                    0, 0, 0);
587         if (!HeapTupleIsValid(tuple))
588                 return NULL;
589
590         result = pstrdup(NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname));
591
592         ReleaseSysCache(tuple);
593         return result;
594 }
595
596 /*
597  * RemoveIndex
598  *              Deletes an index.
599  *
600  * Exceptions:
601  *              BadArg if name is invalid.
602  *              "ERROR" if index nonexistent.
603  *              ...
604  */
605 void
606 RemoveIndex(char *name)
607 {
608         HeapTuple       tuple;
609
610         tuple = SearchSysCache(RELNAME,
611                                                    PointerGetDatum(name),
612                                                    0, 0, 0);
613         if (!HeapTupleIsValid(tuple))
614                 elog(ERROR, "index \"%s\" does not exist", name);
615
616         if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
617                 elog(ERROR, "relation \"%s\" is of type \"%c\"",
618                          name, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
619
620         index_drop(tuple->t_data->t_oid);
621
622         ReleaseSysCache(tuple);
623 }
624
625 /*
626  * Reindex
627  *              Recreate an index.
628  *
629  * Exceptions:
630  *              "ERROR" if index nonexistent.
631  *              ...
632  */
633 void
634 ReindexIndex(const char *name, bool force /* currently unused */ )
635 {
636         HeapTuple       tuple;
637         bool            overwrite = false;
638
639         /*
640          * REINDEX within a transaction block is dangerous, because if the
641          * transaction is later rolled back we have no way to undo truncation
642          * of the index's physical file.  Disallow it.
643          */
644         if (IsTransactionBlock())
645                 elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
646
647         tuple = SearchSysCache(RELNAME,
648                                                    PointerGetDatum(name),
649                                                    0, 0, 0);
650         if (!HeapTupleIsValid(tuple))
651                 elog(ERROR, "index \"%s\" does not exist", name);
652
653         if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
654                 elog(ERROR, "relation \"%s\" is of type \"%c\"",
655                          name, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
656
657         if (IsIgnoringSystemIndexes())
658                 overwrite = true;
659         if (!reindex_index(tuple->t_data->t_oid, force, overwrite))
660                 elog(NOTICE, "index \"%s\" wasn't reindexed", name);
661
662         ReleaseSysCache(tuple);
663 }
664
665 /*
666  * ReindexTable
667  *              Recreate indexes of a table.
668  *
669  * Exceptions:
670  *              "ERROR" if table nonexistent.
671  *              ...
672  */
673 void
674 ReindexTable(const char *name, bool force)
675 {
676         HeapTuple       tuple;
677
678         /*
679          * REINDEX within a transaction block is dangerous, because if the
680          * transaction is later rolled back we have no way to undo truncation
681          * of the index's physical file.  Disallow it.
682          */
683         if (IsTransactionBlock())
684                 elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
685
686         tuple = SearchSysCache(RELNAME,
687                                                    PointerGetDatum(name),
688                                                    0, 0, 0);
689         if (!HeapTupleIsValid(tuple))
690                 elog(ERROR, "table \"%s\" does not exist", name);
691
692         if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION)
693                 elog(ERROR, "relation \"%s\" is of type \"%c\"",
694                          name, ((Form_pg_class) GETSTRUCT(tuple))->relkind);
695
696         if (!reindex_relation(tuple->t_data->t_oid, force))
697                 elog(NOTICE, "table \"%s\" wasn't reindexed", name);
698
699         ReleaseSysCache(tuple);
700 }
701
702 /*
703  * ReindexDatabase
704  *              Recreate indexes of a database.
705  *
706  * Exceptions:
707  *              "ERROR" if table nonexistent.
708  *              ...
709  */
710 void
711 ReindexDatabase(const char *dbname, bool force, bool all)
712 {
713         Relation        relation,
714                                 relationRelation;
715         HeapTuple       dbtuple,
716                                 tuple;
717         HeapScanDesc scan;
718         int4            db_owner;
719         Oid                     db_id;
720         ScanKeyData scankey;
721         MemoryContext private_context;
722         MemoryContext old;
723         int                     relcnt,
724                                 relalc,
725                                 i,
726                                 oncealc = 200;
727         Oid                *relids = (Oid *) NULL;
728
729         AssertArg(dbname);
730
731         relation = heap_openr(DatabaseRelationName, AccessShareLock);
732         ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
733                                                    F_NAMEEQ, NameGetDatum(dbname));
734         scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scankey);
735         dbtuple = heap_getnext(scan, 0);
736         if (!HeapTupleIsValid(dbtuple))
737                 elog(ERROR, "Database \"%s\" does not exist", dbname);
738         db_id = dbtuple->t_data->t_oid;
739         db_owner = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba;
740         heap_endscan(scan);
741         heap_close(relation, NoLock);
742
743         if (GetUserId() != db_owner && !superuser())
744                 elog(ERROR, "REINDEX DATABASE: Permission denied.");
745
746         if (db_id != MyDatabaseId)
747                 elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");
748
749         /*
750          * We cannot run inside a user transaction block; if we were inside a
751          * transaction, then our commit- and start-transaction-command calls
752          * would not have the intended effect!
753          */
754         if (IsTransactionBlock())
755                 elog(ERROR, "REINDEX DATABASE cannot run inside a BEGIN/END block");
756
757         /*
758          * Create a memory context that will survive forced transaction
759          * commits we do below.  Since it is a child of QueryContext, it will
760          * go away eventually even if we suffer an error; there's no need for
761          * special abort cleanup logic.
762          */
763         private_context = AllocSetContextCreate(QueryContext,
764                                                                                         "ReindexDatabase",
765                                                                                         ALLOCSET_DEFAULT_MINSIZE,
766                                                                                         ALLOCSET_DEFAULT_INITSIZE,
767                                                                                         ALLOCSET_DEFAULT_MAXSIZE);
768
769         relationRelation = heap_openr(RelationRelationName, AccessShareLock);
770         scan = heap_beginscan(relationRelation, false, SnapshotNow, 0, NULL);
771         relcnt = relalc = 0;
772         while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
773         {
774                 if (!all)
775                 {
776                         if (!IsSystemRelationName(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)))
777                                 continue;
778                         if (((Form_pg_class) GETSTRUCT(tuple))->relhasrules)
779                                 continue;
780                 }
781                 if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
782                 {
783                         old = MemoryContextSwitchTo(private_context);
784                         if (relcnt == 0)
785                         {
786                                 relalc = oncealc;
787                                 relids = palloc(sizeof(Oid) * relalc);
788                         }
789                         else if (relcnt >= relalc)
790                         {
791                                 relalc *= 2;
792                                 relids = repalloc(relids, sizeof(Oid) * relalc);
793                         }
794                         MemoryContextSwitchTo(old);
795                         relids[relcnt] = tuple->t_data->t_oid;
796                         relcnt++;
797                 }
798         }
799         heap_endscan(scan);
800         heap_close(relationRelation, AccessShareLock);
801
802         CommitTransactionCommand();
803         for (i = 0; i < relcnt; i++)
804         {
805                 StartTransactionCommand();
806                 if (reindex_relation(relids[i], force))
807                         elog(NOTICE, "relation %u was reindexed", relids[i]);
808                 CommitTransactionCommand();
809         }
810         StartTransactionCommand();
811
812         MemoryContextDelete(private_context);
813 }