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.31 2000/06/17 23:41:36 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_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() */
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(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);
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 for the predicate
77 DefineIndex(char *heapRelationName,
78 char *indexRelationName,
79 char *accessMethodName,
90 int numberOfAttributes;
91 AttrNumber *attributeNumberA;
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",
109 * compute heap relation id
111 if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid)
113 elog(ERROR, "DefineIndex: relation \"%s\" not found",
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!
122 if (unique && strcmp(accessMethodName, "btree") != 0)
123 elog(ERROR, "DefineIndex: unique indices are only available with the btree access method");
125 if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0)
126 elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method");
129 * compute access method id
131 tuple = SearchSysCacheTuple(AMNAME,
132 PointerGetDatum(accessMethodName),
134 if (!HeapTupleIsValid(tuple))
136 elog(ERROR, "DefineIndex: access method \"%s\" not found",
139 accessMethodId = tuple->t_data->t_oid;
142 * WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
144 foreach(pl, parameterList)
146 DefElem *param = (DefElem *) lfirst(pl);
148 if (!strcasecmp(param->defname, "islossy"))
151 elog(NOTICE, "Unrecognized index attribute \"%s\" ignored",
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.
161 * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
163 if (predicate != NULL && rangetable != NIL)
165 cnfPred = cnfify((Expr *) copyObject(predicate), true);
166 fix_opids((Node *) cnfPred);
167 CheckPredicate(cnfPred, rangetable, relationId);
170 if (!IsBootstrapProcessingMode() && !IndexesAreActive(relationId, false))
171 elog(ERROR, "Existing indexes are inactive. REINDEX first");
173 if (IsFuncIndex(attributeList))
175 IndexElem *funcIndex = lfirst(attributeList);
178 nargs = length(funcIndex->args);
179 if (nargs > INDEX_MAX_KEYS)
180 elog(ERROR, "Index function can take at most %d arguments",
183 FIsetnArgs(&fInfo, nargs);
185 namestrcpy(&fInfo.funcName, funcIndex->name);
187 attributeNumberA = (AttrNumber *) palloc(nargs *
188 sizeof attributeNumberA[0]);
190 classObjectId = (Oid *) palloc(sizeof(Oid));
192 FuncIndexArgs(funcIndex, &fInfo, attributeNumberA,
193 classObjectId, relationId,
194 accessMethodName, accessMethodId);
196 index_create(heapRelationName, indexRelationName,
198 accessMethodId, numberOfAttributes, attributeNumberA,
201 lossy, unique, primary);
205 attributeNumberA = (AttrNumber *) palloc(numberOfAttributes *
206 sizeof attributeNumberA[0]);
208 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
210 NormIndexAttrs(attributeList, attributeNumberA,
211 classObjectId, relationId,
212 accessMethodName, accessMethodId);
214 index_create(heapRelationName, indexRelationName,
216 accessMethodId, numberOfAttributes, attributeNumberA,
219 lossy, unique, primary);
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.
229 setRelhasindexInplace(relationId, true, false);
235 * Extends a partial index.
241 ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
248 int numberOfAttributes;
249 AttrNumber *attributeNumberA;
252 FuncIndexInfo *funcInfo = NULL;
255 Node *oldPred = NULL;
256 List *cnfPred = NULL;
258 Relation heapRelation;
259 Relation indexRelation;
263 * compute index relation id and access method id
265 tuple = SearchSysCacheTuple(RELNAME,
266 PointerGetDatum(indexRelationName),
268 if (!HeapTupleIsValid(tuple))
270 elog(ERROR, "ExtendIndex: index \"%s\" not found",
273 indexId = tuple->t_data->t_oid;
274 accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
277 * find pg_index tuple
279 tuple = SearchSysCacheTuple(INDEXRELID,
280 ObjectIdGetDatum(indexId),
282 if (!HeapTupleIsValid(tuple))
284 elog(ERROR, "ExtendIndex: relation \"%s\" is not an index",
289 * Extract info from the pg_index tuple
291 index = (Form_pg_index) GETSTRUCT(tuple);
292 Assert(index->indexrelid == indexId);
293 relationId = index->indrelid;
294 indproc = index->indproc;
295 unique = index->indisunique;
297 for (i = 0; i < INDEX_MAX_KEYS; i++)
299 if (index->indkey[i] == InvalidAttrNumber)
302 numberOfAttributes = i;
304 if (VARSIZE(&index->indpred) != 0)
308 predString = textout(&index->indpred);
309 oldPred = stringToNode(predString);
313 elog(ERROR, "ExtendIndex: \"%s\" is not a partial index",
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.
322 if (rangetable != NIL)
324 cnfPred = cnfify((Expr *) copyObject(predicate), true);
325 fix_opids((Node *) cnfPred);
326 CheckPredicate(cnfPred, rangetable, relationId);
329 /* make predInfo list to pass to index_build */
330 predInfo = (PredInfo *) palloc(sizeof(PredInfo));
331 predInfo->pred = (Node *) cnfPred;
332 predInfo->oldPred = oldPred;
334 attributeNumberA = (AttrNumber *) palloc(numberOfAttributes *
335 sizeof attributeNumberA[0]);
336 classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]);
339 for (i = 0; i < numberOfAttributes; i++)
341 attributeNumberA[i] = index->indkey[i];
342 classObjectId[i] = index->indclass[i];
345 if (indproc != InvalidOid)
348 FIsetnArgs(funcInfo, numberOfAttributes);
350 tuple = SearchSysCacheTuple(PROCOID,
351 ObjectIdGetDatum(indproc),
353 if (!HeapTupleIsValid(tuple))
354 elog(ERROR, "ExtendIndex: index procedure %u not found",
357 namecpy(&(funcInfo->funcName),
358 &(((Form_pg_proc) GETSTRUCT(tuple))->proname));
360 FIsetProcOid(funcInfo, tuple->t_data->t_oid);
363 heapRelation = heap_open(relationId, ShareLock);
364 indexRelation = index_open(indexId);
366 InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId);
368 index_build(heapRelation, indexRelation, numberOfAttributes,
369 attributeNumberA, funcInfo, predInfo, unique);
371 /* heap and index rels are closed as a side-effect of index_build */
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).
385 CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
389 foreach(item, predList)
390 CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
394 CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
399 if (is_opclause(predicate))
401 CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
404 else if (or_clause(predicate) || and_clause(predicate))
405 clauses = ((Expr *) predicate)->args;
407 elog(ERROR, "Unsupported partial-index predicate expression type");
409 foreach(clause, clauses)
410 CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
414 CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
419 pred_var = (Var *) get_leftop(predicate);
420 pred_const = (Const *) get_rightop(predicate);
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");
427 if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
429 "Partial-index predicates may refer only to the base relation");
434 FuncIndexArgs(IndexElem *funcIndex,
435 FuncIndexInfo *funcInfo,
439 char *accessMethodName,
448 * process the function arguments, which are a list of T_String
449 * (someday ought to allow more general expressions?)
451 MemSet(funcInfo->arglist, 0, FUNC_MAX_ARGS * sizeof(Oid));
453 foreach(rest, funcIndex->args)
455 char *arg = strVal(lfirst(rest));
456 Form_pg_attribute att;
458 tuple = SearchSysCacheTuple(ATTNAME,
459 ObjectIdGetDatum(relId),
460 PointerGetDatum(arg), 0, 0);
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;
470 * Lookup the function procedure to get its OID and result type.
473 tuple = SearchSysCacheTuple(PROCNAME,
474 PointerGetDatum(FIgetname(funcInfo)),
475 Int32GetDatum(FIgetnArgs(funcInfo)),
476 PointerGetDatum(FIgetArglist(funcInfo)),
479 if (!HeapTupleIsValid(tuple))
481 func_error("DefineIndex", FIgetname(funcInfo),
482 FIgetnArgs(funcInfo), FIgetArglist(funcInfo), NULL);
485 FIsetProcOid(funcInfo, tuple->t_data->t_oid);
486 retType = ((Form_pg_proc) GETSTRUCT(tuple))->prorettype;
488 /* Process type and opclass, using func return type as default */
490 ProcessAttrTypename(funcIndex, retType, -1);
492 *opOidP = GetAttrOpClass(funcIndex, retType,
493 accessMethodName, accessMethodId);
497 NormIndexAttrs(List *attList, /* list of IndexElem's */
501 char *accessMethodName,
507 * process attributeList
509 foreach(rest, attList)
511 IndexElem *attribute = lfirst(rest);
513 Form_pg_attribute attform;
515 if (attribute->name == NULL)
516 elog(ERROR, "missing attribute for define index");
518 atttuple = SearchSysCacheTupleCopy(ATTNAME,
519 ObjectIdGetDatum(relId),
520 PointerGetDatum(attribute->name),
522 if (!HeapTupleIsValid(atttuple))
523 elog(ERROR, "DefineIndex: attribute \"%s\" not found",
525 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
527 *attNumP++ = attform->attnum;
529 ProcessAttrTypename(attribute, attform->atttypid, attform->atttypmod);
531 *classOidP++ = GetAttrOpClass(attribute, attform->atttypid,
532 accessMethodName, accessMethodId);
534 heap_freetuple(atttuple);
539 ProcessAttrTypename(IndexElem *attribute,
540 Oid defType, int32 defTypmod)
544 /* build a type node so we can set the proper alignment, etc. */
545 if (attribute->typename == NULL)
547 tuple = SearchSysCacheTuple(TYPEOID,
548 ObjectIdGetDatum(defType),
550 if (!HeapTupleIsValid(tuple))
551 elog(ERROR, "DefineIndex: type for attribute \"%s\" undefined",
554 attribute->typename = makeNode(TypeName);
555 attribute->typename->name = nameout(&((Form_pg_type) GETSTRUCT(tuple))->typname);
556 attribute->typename->typmod = defTypmod;
561 GetAttrOpClass(IndexElem *attribute, Oid attrType,
562 char *accessMethodName, Oid accessMethodId)
566 ScanKeyData entry[2];
570 bool doTypeCheck = true;
572 if (attribute->class == NULL)
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 */
583 tuple = SearchSysCacheTuple(CLANAME,
584 PointerGetDatum(attribute->class),
586 if (!HeapTupleIsValid(tuple))
587 elog(ERROR, "DefineIndex: opclass \"%s\" not found",
589 opClassId = tuple->t_data->t_oid;
592 * Assume the opclass is supported by this index access method
593 * if we can find at least one relevant entry in pg_amop.
595 ScanKeyEntryInitialize(&entry[0], 0,
598 ObjectIdGetDatum(accessMethodId));
599 ScanKeyEntryInitialize(&entry[1], 0,
600 Anum_pg_amop_amopclaid,
602 ObjectIdGetDatum(opClassId));
604 relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
605 scan = heap_beginscan(relation, false, SnapshotNow, 2, entry);
607 if (! HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
609 elog(ERROR, "DefineIndex: opclass \"%s\" not supported by access method \"%s\"",
610 attribute->class, accessMethodName);
613 oprId = ((Form_pg_amop) GETSTRUCT(tuple))->amopopr;
616 heap_close(relation, AccessShareLock);
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.
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...
633 tuple = SearchSysCacheTuple(OPEROID,
634 ObjectIdGetDatum(oprId),
636 if (HeapTupleIsValid(tuple))
638 Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tuple);
639 Oid opInputType = (optup->oprkind == 'l') ?
640 optup->oprright : optup->oprleft;
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));
653 GetDefaultOpClass(Oid atttypid)
657 tuple = SearchSysCacheTuple(CLADEFTYPE,
658 ObjectIdGetDatum(atttypid),
660 if (!HeapTupleIsValid(tuple))
663 return nameout(&((Form_pg_opclass) GETSTRUCT(tuple))->opcname);
671 * BadArg if name is invalid.
672 * "WARN" if index nonexistent.
676 RemoveIndex(char *name)
680 tuple = SearchSysCacheTuple(RELNAME,
681 PointerGetDatum(name),
684 if (!HeapTupleIsValid(tuple))
685 elog(ERROR, "index \"%s\" nonexistent", name);
687 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
689 elog(ERROR, "relation \"%s\" is of type \"%c\"",
691 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
694 index_drop(tuple->t_data->t_oid);
702 * "ERROR" if index nonexistent.
706 ReindexIndex(const char *name, bool force /* currently unused */ )
710 tuple = SearchSysCacheTuple(RELNAME,
711 PointerGetDatum(name),
714 if (!HeapTupleIsValid(tuple))
715 elog(ERROR, "index \"%s\" nonexistent", name);
717 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
719 elog(ERROR, "relation \"%s\" is of type \"%c\"",
721 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
724 if (!reindex_index(tuple->t_data->t_oid, force))
725 elog(NOTICE, "index '%s' wasn't reindexed", name);
730 * Recreate indexes of a table.
733 * "ERROR" if table nonexistent.
737 ReindexTable(const char *name, bool force)
741 tuple = SearchSysCacheTuple(RELNAME,
742 PointerGetDatum(name),
745 if (!HeapTupleIsValid(tuple))
746 elog(ERROR, "table \"%s\" nonexistent", name);
748 if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_RELATION)
750 elog(ERROR, "relation \"%s\" is of type \"%c\"",
752 ((Form_pg_class) GETSTRUCT(tuple))->relkind);
755 if (!reindex_relation(tuple->t_data->t_oid, force))
756 elog(NOTICE, "table '%s' wasn't reindexed", name);
761 * Recreate indexes of a database.
764 * "ERROR" if table nonexistent.
767 extern Oid MyDatabaseId;
769 ReindexDatabase(const char *dbname, bool force, bool all)
783 PortalVariableMemory pmem;
789 Oid *relids = (Oid *) NULL;
793 username = GetPgUserName();
794 usertuple = SearchSysCacheTuple(SHADOWNAME, PointerGetDatum(username),
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;
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;
811 if (user_id != db_owner && !superuser)
812 elog(ERROR, "REINDEX DATABASE: Permission denied.");
814 if (db_id != MyDatabaseId)
815 elog(ERROR, "REINDEX DATABASE: Can be executed only on the currently open database.");
817 heap_close(relation, NoLock);
819 CommonSpecialPortalOpen();
820 pmem = CommonSpecialPortalGetMemory();
821 relationRelation = heap_openr(RelationRelationName, AccessShareLock);
822 scan = heap_beginscan(relationRelation, false, SnapshotNow, 0, NULL);
824 while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
828 if (!IsSystemRelationName(NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)))
830 if (((Form_pg_class) GETSTRUCT(tuple))->relhasrules)
833 if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
835 old = MemoryContextSwitchTo((MemoryContext) pmem);
839 relids = palloc(sizeof(Oid) * relalc);
841 else if (relcnt >= relalc)
844 relids = repalloc(relids, sizeof(Oid) * relalc);
846 MemoryContextSwitchTo(old);
847 relids[relcnt] = tuple->t_data->t_oid;
852 heap_close(relationRelation, AccessShareLock);
854 CommitTransactionCommand();
855 for (i = 0; i < relcnt; i++)
857 StartTransactionCommand();
858 if (reindex_relation(relids[i], force))
859 elog(NOTICE, "relation %d was reindexed", relids[i]);
860 CommitTransactionCommand();
862 CommonSpecialPortalClose();
863 StartTransactionCommand();