1 /*-------------------------------------------------------------------------
4 * POSTGRES define, extend and remove index code.
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.48 2001/05/30 20:52:32 momjian Exp $
13 *-------------------------------------------------------------------------
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"
45 #define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args != NIL)
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,
54 char *accessMethodName, Oid accessMethodId);
55 static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
58 char *accessMethodName, Oid accessMethodId);
59 static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
60 char *accessMethodName, Oid accessMethodId);
61 static char *GetDefaultOpClass(Oid atttypid);
65 * Creates a new index.
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
74 DefineIndex(char *heapRelationName,
75 char *indexRelationName,
76 char *accessMethodName,
88 int numberOfAttributes;
94 * count attributes in index
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)
107 elog(ERROR, "DefineIndex: relation \"%s\" not found",
111 * compute access method id
113 accessMethodId = GetSysCacheOid(AMNAME,
114 PointerGetDatum(accessMethodName),
116 if (!OidIsValid(accessMethodId))
117 elog(ERROR, "DefineIndex: access method \"%s\" not found",
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!
125 if (unique && accessMethodId != BTREE_AM_OID)
126 elog(ERROR, "DefineIndex: unique indices are only available with the btree access method");
128 if (numberOfAttributes > 1 && accessMethodId != BTREE_AM_OID)
129 elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method");
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, "Existing indexes are inactive. REINDEX first");
164 * Prepare arguments for index_create, primarily an IndexInfo
167 indexInfo = makeNode(IndexInfo);
168 indexInfo->ii_Predicate = (Node *) cnfPred;
169 indexInfo->ii_FuncOid = InvalidOid;
170 indexInfo->ii_Unique = unique;
172 if (IsFuncIndex(attributeList))
174 IndexElem *funcIndex = (IndexElem *) lfirst(attributeList);
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");
181 nargs = length(funcIndex->args);
182 if (nargs > INDEX_MAX_KEYS)
183 elog(ERROR, "Index function can take at most %d arguments",
186 indexInfo->ii_NumIndexAttrs = 1;
187 indexInfo->ii_NumKeyAttrs = nargs;
189 classObjectId = (Oid *) palloc(sizeof(Oid));
191 FuncIndexArgs(indexInfo, classObjectId, funcIndex,
192 relationId, accessMethodName, accessMethodId);
196 indexInfo->ii_NumIndexAttrs = numberOfAttributes;
197 indexInfo->ii_NumKeyAttrs = numberOfAttributes;
199 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
201 NormIndexAttrs(indexInfo, classObjectId, attributeList,
202 relationId, accessMethodName, accessMethodId);
205 index_create(heapRelationName, indexRelationName,
206 indexInfo, accessMethodId, classObjectId,
207 lossy, primary, allowSystemTableMods);
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.
216 setRelhasindex(relationId, true);
222 * Extends a partial index.
225 ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
227 Relation heapRelation;
228 Relation indexRelation;
235 IndexInfo *indexInfo;
239 * Get index's relation id and access method id from pg_class
241 tuple = SearchSysCache(RELNAME,
242 PointerGetDatum(indexRelationName),
244 if (!HeapTupleIsValid(tuple))
245 elog(ERROR, "ExtendIndex: index \"%s\" not found",
247 indexId = tuple->t_data->t_oid;
248 accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
249 ReleaseSysCache(tuple);
252 * Extract info from the pg_index tuple for the index
254 tuple = SearchSysCache(INDEXRELID,
255 ObjectIdGetDatum(indexId),
257 if (!HeapTupleIsValid(tuple))
258 elog(ERROR, "ExtendIndex: relation \"%s\" is not an index",
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);
268 elog(ERROR, "ExtendIndex: \"%s\" is not a partial index",
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.
277 * XXX I think this code is broken --- index_build expects a single
278 * expression not a list --- tgl Jul 00
280 if (rangetable != NIL)
282 cnfPred = cnfify((Expr *) copyObject(predicate), true);
283 fix_opids((Node *) cnfPred);
284 CheckPredicate(cnfPred, rangetable, relationId);
287 /* pass new predicate to index_build */
288 indexInfo->ii_Predicate = (Node *) cnfPred;
290 /* Open heap and index rels, and get suitable locks */
291 heapRelation = heap_open(relationId, ShareLock);
292 indexRelation = index_open(indexId);
294 /* Obtain exclusive lock on it, just to be sure */
295 LockRelation(indexRelation, AccessExclusiveLock);
297 InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
298 indexRelation, accessMethodId);
300 index_build(heapRelation, indexRelation, indexInfo, oldPred);
302 /* heap and index rels are closed as a side-effect of index_build */
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).
316 CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
320 foreach(item, predList)
321 CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
325 CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
330 if (is_opclause(predicate))
332 CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
335 else if (or_clause(predicate) || and_clause(predicate))
336 clauses = ((Expr *) predicate)->args;
338 elog(ERROR, "Unsupported partial-index predicate expression type");
340 foreach(clause, clauses)
341 CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
345 CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
350 pred_var = (Var *) get_leftop(predicate);
351 pred_const = (Const *) get_rightop(predicate);
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");
358 if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
360 "Partial-index predicates may refer only to the base relation");
365 FuncIndexArgs(IndexInfo *indexInfo,
367 IndexElem *funcIndex,
369 char *accessMethodName,
372 Oid argTypes[FUNC_MAX_ARGS];
382 * process the function arguments, which are a list of T_String
383 * (someday ought to allow more general expressions?)
385 * Note caller already checked that list is not too long.
387 MemSet(argTypes, 0, sizeof(argTypes));
389 foreach(arglist, funcIndex->args)
391 char *arg = strVal(lfirst(arglist));
393 Form_pg_attribute att;
395 tuple = SearchSysCache(ATTNAME,
396 ObjectIdGetDatum(relId),
397 PointerGetDatum(arg),
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);
409 * Lookup the function procedure to get its OID and result type.
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.
418 if (!func_get_detail(funcIndex->name, nargs, argTypes,
419 &funcid, &rettype, &retset, &true_typeids))
420 func_error("DefineIndex", funcIndex->name, nargs, argTypes, NULL);
423 elog(ERROR, "DefineIndex: cannot index on a function returning a set");
425 for (i = 0; i < nargs; i++)
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");
433 /* Process opclass, using func return type as default type */
435 classOidP[0] = GetAttrOpClass(funcIndex, rettype,
436 accessMethodName, accessMethodId);
438 /* OK, return results */
440 indexInfo->ii_FuncOid = funcid;
441 /* Need to do the fmgr function lookup now, too */
442 fmgr_info(funcid, &indexInfo->ii_FuncInfo);
446 NormIndexAttrs(IndexInfo *indexInfo,
448 List *attList, /* list of IndexElem's */
450 char *accessMethodName,
457 * process attributeList
459 foreach(rest, attList)
461 IndexElem *attribute = (IndexElem *) lfirst(rest);
463 Form_pg_attribute attform;
465 if (attribute->name == NULL)
466 elog(ERROR, "missing attribute for define index");
468 atttuple = SearchSysCache(ATTNAME,
469 ObjectIdGetDatum(relId),
470 PointerGetDatum(attribute->name),
472 if (!HeapTupleIsValid(atttuple))
473 elog(ERROR, "DefineIndex: attribute \"%s\" not found",
475 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
477 indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
479 classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
480 accessMethodName, accessMethodId);
482 ReleaseSysCache(atttuple);
488 GetAttrOpClass(IndexElem *attribute, Oid attrType,
489 char *accessMethodName, Oid accessMethodId)
493 ScanKeyData entry[2];
497 bool doTypeCheck = true;
499 if (attribute->class == NULL)
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 */
510 opClassId = GetSysCacheOid(CLANAME,
511 PointerGetDatum(attribute->class),
513 if (!OidIsValid(opClassId))
514 elog(ERROR, "DefineIndex: opclass \"%s\" not found",
518 * Assume the opclass is supported by this index access method if we
519 * can find at least one relevant entry in pg_amop.
521 ScanKeyEntryInitialize(&entry[0], 0,
524 ObjectIdGetDatum(accessMethodId));
525 ScanKeyEntryInitialize(&entry[1], 0,
526 Anum_pg_amop_amopclaid,
528 ObjectIdGetDatum(opClassId));
530 relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
531 scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
533 if (!HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
534 elog(ERROR, "DefineIndex: opclass \"%s\" not supported by access method \"%s\"",
535 attribute->class, accessMethodName);
537 oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;
540 heap_close(relation, AccessShareLock);
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.
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...
558 tuple = SearchSysCache(OPEROID,
559 ObjectIdGetDatum(oprId),
561 if (HeapTupleIsValid(tuple))
563 Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
564 Oid opInputType = (optup->oprkind == 'l') ?
565 optup->oprright : optup->oprleft;
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);
579 GetDefaultOpClass(Oid atttypid)
584 tuple = SearchSysCache(CLADEFTYPE,
585 ObjectIdGetDatum(atttypid),
587 if (!HeapTupleIsValid(tuple))
590 result = pstrdup(NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname));
592 ReleaseSysCache(tuple);
601 * BadArg if name is invalid.
602 * "ERROR" if index nonexistent.
606 RemoveIndex(char *name)
610 tuple = SearchSysCache(RELNAME,
611 PointerGetDatum(name),
613 if (!HeapTupleIsValid(tuple))
614 elog(ERROR, "index \"%s\" does not exist", name);
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);
620 index_drop(tuple->t_data->t_oid);
622 ReleaseSysCache(tuple);
630 * "ERROR" if index nonexistent.
634 ReindexIndex(const char *name, bool force /* currently unused */ )
637 bool overwrite = false;
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.
644 if (IsTransactionBlock())
645 elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
647 tuple = SearchSysCache(RELNAME,
648 PointerGetDatum(name),
650 if (!HeapTupleIsValid(tuple))
651 elog(ERROR, "index \"%s\" does not exist", name);
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);
657 if (IsIgnoringSystemIndexes())
659 if (!reindex_index(tuple->t_data->t_oid, force, overwrite))
660 elog(NOTICE, "index \"%s\" wasn't reindexed", name);
662 ReleaseSysCache(tuple);
667 * Recreate indexes of a table.
670 * "ERROR" if table nonexistent.
674 ReindexTable(const char *name, bool force)
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.
683 if (IsTransactionBlock())
684 elog(ERROR, "REINDEX cannot run inside a BEGIN/END block");
686 tuple = SearchSysCache(RELNAME,
687 PointerGetDatum(name),
689 if (!HeapTupleIsValid(tuple))
690 elog(ERROR, "table \"%s\" does not exist", name);
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);
696 if (!reindex_relation(tuple->t_data->t_oid, force))
697 elog(NOTICE, "table \"%s\" wasn't reindexed", name);
699 ReleaseSysCache(tuple);
704 * Recreate indexes of a database.
707 * "ERROR" if table nonexistent.
711 ReindexDatabase(const char *dbname, bool force, bool all)
721 MemoryContext private_context;
727 Oid *relids = (Oid *) NULL;
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;
741 heap_close(relation, NoLock);
743 if (GetUserId() != db_owner && !superuser())
744 elog(ERROR, "REINDEX DATABASE: Permission denied.");
746 if (db_id != MyDatabaseId)
747 elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");
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!
754 if (IsTransactionBlock())
755 elog(ERROR, "REINDEX DATABASE cannot run inside a BEGIN/END block");
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.
763 private_context = AllocSetContextCreate(QueryContext,
765 ALLOCSET_DEFAULT_MINSIZE,
766 ALLOCSET_DEFAULT_INITSIZE,
767 ALLOCSET_DEFAULT_MAXSIZE);
769 relationRelation = heap_openr(RelationRelationName, AccessShareLock);
770 scan = heap_beginscan(relationRelation, false, SnapshotNow, 0, NULL);
772 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
776 if (!IsSystemRelationName(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)))
778 if (((Form_pg_class) GETSTRUCT(tuple))->relhasrules)
781 if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
783 old = MemoryContextSwitchTo(private_context);
787 relids = palloc(sizeof(Oid) * relalc);
789 else if (relcnt >= relalc)
792 relids = repalloc(relids, sizeof(Oid) * relalc);
794 MemoryContextSwitchTo(old);
795 relids[relcnt] = tuple->t_data->t_oid;
800 heap_close(relationRelation, AccessShareLock);
802 CommitTransactionCommand();
803 for (i = 0; i < relcnt; i++)
805 StartTransactionCommand();
806 if (reindex_relation(relids[i], force))
807 elog(NOTICE, "relation %u was reindexed", relids[i]);
808 CommitTransactionCommand();
810 StartTransactionCommand();
812 MemoryContextDelete(private_context);