OSDN Git Service

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