1 /*-------------------------------------------------------------------------
4 * POSTGRES define, extend and remove index code.
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.22 2000/02/25 02:58:48 tgl Exp $
13 *-------------------------------------------------------------------------
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_index.h"
24 #include "catalog/pg_opclass.h"
25 #include "catalog/pg_proc.h"
26 #include "catalog/pg_type.h"
27 #include "catalog/pg_database.h"
28 #include "catalog/pg_shadow.h"
29 #include "commands/defrem.h"
30 #include "optimizer/clauses.h"
31 #include "optimizer/planmain.h"
32 #include "optimizer/prep.h"
33 #include "parser/parsetree.h"
34 #include "parser/parse_func.h"
35 #include "utils/builtins.h"
36 #include "utils/syscache.h"
37 #include "miscadmin.h" /* ReindexDatabase() */
38 #include "utils/portal.h" /* ReindexDatabase() */
39 #include "catalog/catalog.h" /* ReindexDatabase() */
41 #define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args != NIL)
43 /* non-export function prototypes */
44 static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
45 static void CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid);
46 static void CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
47 static void FuncIndexArgs(IndexElem *funcIndex, FuncIndexInfo *funcInfo,
48 AttrNumber *attNumP, Oid *opOidP, Oid relId);
49 static void NormIndexAttrs(List *attList, AttrNumber *attNumP,
50 Oid *opOidP, Oid relId);
51 static void ProcessAttrTypename(IndexElem *attribute,
52 Oid defType, int32 defTypmod);
53 static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType);
54 static char *GetDefaultOpClass(Oid atttypid);
58 * Creates a new index.
60 * 'attributeList' is a list of IndexElem specifying either a functional
61 * index or a list of attributes to index on.
62 * 'parameterList' is a list of DefElem specified in the with clause.
63 * 'predicate' is the qual specified in the where clause.
64 * 'rangetable' is for the predicate
70 DefineIndex(char *heapRelationName,
71 char *indexRelationName,
72 char *accessMethodName,
83 int numberOfAttributes;
84 AttrNumber *attributeNumberA;
86 uint16 parameterCount = 0;
87 Datum *parameterA = NULL;
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",
104 * compute heap relation id
106 if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid)
108 elog(ERROR, "DefineIndex: %s relation not found",
112 if (unique && strcmp(accessMethodName, "btree") != 0)
113 elog(ERROR, "DefineIndex: unique indices are only available with the btree access method");
115 if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0)
116 elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method");
119 * compute access method id
121 tuple = SearchSysCacheTuple(AMNAME,
122 PointerGetDatum(accessMethodName),
124 if (!HeapTupleIsValid(tuple))
126 elog(ERROR, "DefineIndex: %s access method not found",
129 accessMethodId = tuple->t_data->t_oid;
132 * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
134 foreach(pl, parameterList)
136 DefElem *param = (DefElem *) lfirst(pl);
138 if (!strcasecmp(param->defname, "islossy"))
141 elog(NOTICE, "Unrecognized index attribute '%s' ignored",
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.
151 * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
153 if (predicate != NULL && rangetable != NIL)
155 cnfPred = cnfify((Expr *) copyObject(predicate), true);
156 fix_opids((Node *) cnfPred);
157 CheckPredicate(cnfPred, rangetable, relationId);
160 if (!IsBootstrapProcessingMode() && !IndexesAreActive(relationId, false))
161 elog(ERROR, "existent indexes are inactive. REINDEX first");
162 if (IsFuncIndex(attributeList))
164 IndexElem *funcIndex = lfirst(attributeList);
167 nargs = length(funcIndex->args);
168 if (nargs > INDEX_MAX_KEYS)
169 elog(ERROR, "Index function can take at most %d arguments",
172 FIsetnArgs(&fInfo, nargs);
174 namestrcpy(&fInfo.funcName, funcIndex->name);
176 attributeNumberA = (AttrNumber *) palloc(nargs *
177 sizeof attributeNumberA[0]);
179 classObjectId = (Oid *) palloc(sizeof(Oid));
181 FuncIndexArgs(funcIndex, &fInfo, attributeNumberA,
182 classObjectId, relationId);
184 index_create(heapRelationName,
186 &fInfo, NULL, accessMethodId,
187 numberOfAttributes, attributeNumberA,
188 classObjectId, parameterCount, parameterA,
190 lossy, unique, primary);
194 attributeNumberA = (AttrNumber *) palloc(numberOfAttributes *
195 sizeof attributeNumberA[0]);
197 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
199 NormIndexAttrs(attributeList, attributeNumberA,
200 classObjectId, relationId);
202 index_create(heapRelationName, indexRelationName, NULL,
204 accessMethodId, numberOfAttributes, attributeNumberA,
205 classObjectId, parameterCount, parameterA,
207 lossy, unique, primary);
210 setRelhasindexInplace(relationId, true, false);
216 * Extends a partial index.
222 ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
229 int numberOfAttributes;
230 AttrNumber *attributeNumberA;
233 FuncIndexInfo *funcInfo = NULL;
235 Node *oldPred = NULL;
236 List *cnfPred = NULL;
238 Relation heapRelation;
239 Relation indexRelation;
243 * compute index relation id and access method id
245 tuple = SearchSysCacheTuple(RELNAME,
246 PointerGetDatum(indexRelationName),
248 if (!HeapTupleIsValid(tuple))
250 elog(ERROR, "ExtendIndex: %s index not found",
253 indexId = tuple->t_data->t_oid;
254 accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
257 * find pg_index tuple
259 tuple = SearchSysCacheTuple(INDEXRELID,
260 ObjectIdGetDatum(indexId),
262 if (!HeapTupleIsValid(tuple))
264 elog(ERROR, "ExtendIndex: %s is not an index",
269 * Extract info from the pg_index tuple
271 index = (Form_pg_index) GETSTRUCT(tuple);
272 Assert(index->indexrelid == indexId);
273 relationId = index->indrelid;
274 indproc = index->indproc;
276 for (i = 0; i < INDEX_MAX_KEYS; i++)
278 if (index->indkey[i] == InvalidAttrNumber)
281 numberOfAttributes = i;
283 if (VARSIZE(&index->indpred) != 0)
287 predString = fmgr(F_TEXTOUT, &index->indpred);
288 oldPred = stringToNode(predString);
292 elog(ERROR, "ExtendIndex: %s is not a partial index",
296 * Convert the extension predicate from parsetree form to plan form,
297 * so it can be readily evaluated during index creation. Note:
298 * "predicate" comes in as a list containing (1) the predicate itself
299 * (a where_clause), and (2) a corresponding range table.
301 if (rangetable != NIL)
303 cnfPred = cnfify((Expr *) copyObject(predicate), true);
304 fix_opids((Node *) cnfPred);
305 CheckPredicate(cnfPred, rangetable, relationId);
308 /* make predInfo list to pass to index_build */
309 predInfo = (PredInfo *) palloc(sizeof(PredInfo));
310 predInfo->pred = (Node *) cnfPred;
311 predInfo->oldPred = oldPred;
313 attributeNumberA = (AttrNumber *) palloc(numberOfAttributes *
314 sizeof attributeNumberA[0]);
315 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]);
318 for (i = 0; i < numberOfAttributes; i++)
320 attributeNumberA[i] = index->indkey[i];
321 classObjectId[i] = index->indclass[i];
324 if (indproc != InvalidOid)
327 FIsetnArgs(funcInfo, numberOfAttributes);
329 tuple = SearchSysCacheTuple(PROCOID,
330 ObjectIdGetDatum(indproc),
332 if (!HeapTupleIsValid(tuple))
333 elog(ERROR, "ExtendIndex: index procedure not found");
335 namecpy(&(funcInfo->funcName),
336 &(((Form_pg_proc) GETSTRUCT(tuple))->proname));
338 FIsetProcOid(funcInfo, tuple->t_data->t_oid);
341 heapRelation = heap_open(relationId, ShareLock);
342 indexRelation = index_open(indexId);
344 InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId);
346 index_build(heapRelation, indexRelation, numberOfAttributes,
347 attributeNumberA, 0, NULL, funcInfo, predInfo);
349 /* heap and index rels are closed as a side-effect of index_build */
355 * Checks that the given list of partial-index predicates refer
356 * (via the given range table) only to the given base relation oid,
357 * and that they're in a form the planner can handle, i.e.,
358 * boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
359 * has to be on the left).
363 CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
367 foreach(item, predList)
368 CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
372 CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
377 if (is_opclause(predicate))
379 CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
382 else if (or_clause(predicate) || and_clause(predicate))
383 clauses = ((Expr *) predicate)->args;
385 elog(ERROR, "Unsupported partial-index predicate expression type");
387 foreach(clause, clauses)
388 CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
392 CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
397 pred_var = (Var *) get_leftop(predicate);
398 pred_const = (Const *) get_rightop(predicate);
400 if (!IsA(predicate->oper, Oper) ||
401 !IsA(pred_var, Var) ||
402 !IsA(pred_const, Const))
403 elog(ERROR, "Unsupported partial-index predicate clause type");
405 if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
407 "Partial-index predicates may refer only to the base relation");
412 FuncIndexArgs(IndexElem *funcIndex,
413 FuncIndexInfo *funcInfo,
424 * process the function arguments, which are a list of T_String
425 * (someday ought to allow more general expressions?)
427 MemSet(funcInfo->arglist, 0, FUNC_MAX_ARGS * sizeof(Oid));
429 foreach(rest, funcIndex->args)
431 char *arg = strVal(lfirst(rest));
432 Form_pg_attribute att;
434 tuple = SearchSysCacheTuple(ATTNAME,
435 ObjectIdGetDatum(relId),
436 PointerGetDatum(arg), 0, 0);
438 if (!HeapTupleIsValid(tuple))
439 elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg);
440 att = (Form_pg_attribute) GETSTRUCT(tuple);
441 *attNumP++ = att->attnum;
442 funcInfo->arglist[argn++] = att->atttypid;
446 * Lookup the function procedure to get its OID and result type.
449 tuple = SearchSysCacheTuple(PROCNAME,
450 PointerGetDatum(FIgetname(funcInfo)),
451 Int32GetDatum(FIgetnArgs(funcInfo)),
452 PointerGetDatum(FIgetArglist(funcInfo)),
455 if (!HeapTupleIsValid(tuple))
457 func_error("DefineIndex", FIgetname(funcInfo),
458 FIgetnArgs(funcInfo), FIgetArglist(funcInfo), NULL);
461 FIsetProcOid(funcInfo, tuple->t_data->t_oid);
462 retType = ((Form_pg_proc) GETSTRUCT(tuple))->prorettype;
464 /* Process type and opclass, using func return type as default */
466 ProcessAttrTypename(funcIndex, retType, -1);
468 *opOidP = GetAttrOpClass(funcIndex, retType);
472 NormIndexAttrs(List *attList, /* list of IndexElem's */
480 * process attributeList
482 foreach(rest, attList)
484 IndexElem *attribute = lfirst(rest);
486 Form_pg_attribute attform;
488 if (attribute->name == NULL)
489 elog(ERROR, "missing attribute for define index");
491 atttuple = SearchSysCacheTupleCopy(ATTNAME,
492 ObjectIdGetDatum(relId),
493 PointerGetDatum(attribute->name),
495 if (!HeapTupleIsValid(atttuple))
496 elog(ERROR, "DefineIndex: attribute \"%s\" not found",
498 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
500 *attNumP++ = attform->attnum;
502 ProcessAttrTypename(attribute, attform->atttypid, attform->atttypmod);
504 *classOidP++ = GetAttrOpClass(attribute, attform->atttypid);
506 heap_freetuple(atttuple);
511 ProcessAttrTypename(IndexElem *attribute,
512 Oid defType, int32 defTypmod)
516 /* build a type node so we can set the proper alignment, etc. */
517 if (attribute->typename == NULL)
519 tuple = SearchSysCacheTuple(TYPEOID,
520 ObjectIdGetDatum(defType),
522 if (!HeapTupleIsValid(tuple))
523 elog(ERROR, "DefineIndex: type for attribute '%s' undefined",
526 attribute->typename = makeNode(TypeName);
527 attribute->typename->name = nameout(&((Form_pg_type) GETSTRUCT(tuple))->typname);
528 attribute->typename->typmod = defTypmod;
533 GetAttrOpClass(IndexElem *attribute, Oid attrType)
537 if (attribute->class == NULL)
539 /* no operator class specified, so find the default */
540 attribute->class = GetDefaultOpClass(attrType);
541 if (attribute->class == NULL)
542 elog(ERROR, "Can't find a default operator class for type %u",
546 tuple = SearchSysCacheTuple(CLANAME,
547 PointerGetDatum(attribute->class),
550 if (!HeapTupleIsValid(tuple))
551 elog(ERROR, "DefineIndex: %s opclass not found",
554 return tuple->t_data->t_oid;
558 GetDefaultOpClass(Oid atttypid)
562 tuple = SearchSysCacheTuple(CLADEFTYPE,
563 ObjectIdGetDatum(atttypid),
565 if (!HeapTupleIsValid(tuple))
568 return nameout(&((Form_pg_opclass) GETSTRUCT(tuple))->opcname);
576 * BadArg if name is invalid.
577 * "WARN" if index nonexistent.
581 RemoveIndex(char *name)
585 tuple = SearchSysCacheTuple(RELNAME,
586 PointerGetDatum(name),
589 if (!HeapTupleIsValid(tuple))
590 elog(ERROR, "index \"%s\" nonexistent", name);
592 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
594 elog(ERROR, "relation \"%s\" is of type \"%c\"",
596 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
599 index_drop(tuple->t_data->t_oid);
607 * "ERROR" if index nonexistent.
611 ReindexIndex(const char *name, bool force /* currently unused */)
615 tuple = SearchSysCacheTuple(RELNAME,
616 PointerGetDatum(name),
619 if (!HeapTupleIsValid(tuple))
620 elog(ERROR, "index \"%s\" nonexistent", name);
622 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
624 elog(ERROR, "relation \"%s\" is of type \"%c\"",
626 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
629 reindex_index(tuple->t_data->t_oid, force);
634 * Recreate indexes of a table.
637 * "ERROR" if table nonexistent.
641 ReindexTable(const char *name, bool force)
645 tuple = SearchSysCacheTuple(RELNAME,
646 PointerGetDatum(name),
649 if (!HeapTupleIsValid(tuple))
650 elog(ERROR, "table \"%s\" nonexistent", name);
652 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION)
654 elog(ERROR, "relation \"%s\" is of type \"%c\"",
656 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
659 reindex_relation(tuple->t_data->t_oid, force);
664 * Recreate indexes of a database.
667 * "ERROR" if table nonexistent.
670 extern Oid MyDatabaseId;
672 ReindexDatabase(const char *dbname, bool force, bool all)
674 Relation relation, relationRelation;
675 HeapTuple usertuple, dbtuple, tuple;
677 int4 user_id, db_owner;
682 PortalVariableMemory pmem;
684 int relcnt, relalc, i, oncealc = 200;
685 Oid *relids = (Oid *) NULL;
689 username = GetPgUserName();
690 usertuple = SearchSysCacheTuple(SHADOWNAME, PointerGetDatum(username),
692 if (!HeapTupleIsValid(usertuple))
693 elog(ERROR, "Current user '%s' is invalid.", username);
694 user_id = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesysid;
695 superuser = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesuper;
697 relation = heap_openr(DatabaseRelationName, AccessShareLock);
698 ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
699 F_NAMEEQ, NameGetDatum(dbname));
700 scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scankey);
701 dbtuple = heap_getnext(scan, 0);
702 if (!HeapTupleIsValid(dbtuple))
703 elog(ERROR, "Database '%s' doesn't exist", dbname);
704 db_id = dbtuple->t_data->t_oid;
705 db_owner = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba;
707 if (user_id != db_owner && !superuser)
708 elog(ERROR, "REINDEX DATABASE: Permission denied.");
710 if (db_id != MyDatabaseId)
711 elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");
713 heap_close(relation, NoLock);
714 /** reindex_database(db_id, force, !all); **/
716 CommonSpecialPortalOpen();
717 pmem = CommonSpecialPortalGetMemory();
718 relationRelation = heap_openr(RelationRelationName, AccessShareLock);
719 scan = heap_beginscan(relationRelation, false, SnapshotNow, 0, NULL);
721 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
725 if (!IsSystemRelationName(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)))
727 if (((Form_pg_class) GETSTRUCT(tuple))->relhasrules)
730 if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
732 old = MemoryContextSwitchTo((MemoryContext) pmem);
736 relids = palloc(sizeof(Oid) * relalc);
738 else if (relcnt >= relalc)
741 relids = repalloc(relids, sizeof(Oid) * relalc);
743 MemoryContextSwitchTo(old);
744 relids[relcnt] = tuple->t_data->t_oid;
749 heap_close(relationRelation, AccessShareLock);
751 CommitTransactionCommand();
752 for (i = 0; i < relcnt; i++)
754 StartTransactionCommand();
755 reindex_relation(relids[i], force);
756 CommitTransactionCommand();
758 CommonSpecialPortalClose();
759 StartTransactionCommand();