Tom Lane 6eb8d255d2 Allow CREATE FUNCTION's WITH clause to be used for all language types,
not just C, so that ISCACHABLE attribute can be specified for user-defined
functions.  Get rid of ParamString node type, which wasn't actually being
generated by gram.y anymore, even though define.c thought that was what
it was getting.  Clean up minor bug in dfmgr.c (premature heap_close).
1999-10-02 21:33:33 +00:00

569 lines
14 KiB
C

/*-------------------------------------------------------------------------
*
* defind.c
* POSTGRES define, extend and remove index code.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.12 1999/10/02 21:33:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/pg_index.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->args!=NULL)
/* non-export function prototypes */
static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
static void CheckPredExpr(Node *predicate, List *rangeTable,
Oid baseRelOid);
static void
CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
static void FuncIndexArgs(IndexElem *funcIndex, AttrNumber *attNumP,
Oid *argTypes, Oid *opOidP, Oid relId);
static void NormIndexAttrs(List *attList, AttrNumber *attNumP,
Oid *opOidP, Oid relId);
static char *GetDefaultOpClass(Oid atttypid);
/*
* DefineIndex
* Creates a new index.
*
* 'attributeList' is a list of IndexElem specifying either a functional
* index or a list of attributes to index on.
* 'parameterList' is a list of DefElem specified in the with clause.
* 'predicate' is the qual specified in the where clause.
* 'rangetable' is for the predicate
*
* Exceptions:
* XXX
*/
void
DefineIndex(char *heapRelationName,
char *indexRelationName,
char *accessMethodName,
List *attributeList,
List *parameterList,
bool unique,
bool primary,
Expr *predicate,
List *rangetable)
{
Oid *classObjectId;
Oid accessMethodId;
Oid relationId;
int numberOfAttributes;
AttrNumber *attributeNumberA;
HeapTuple tuple;
uint16 parameterCount = 0;
Datum *parameterA = NULL;
FuncIndexInfo fInfo;
List *cnfPred = NULL;
bool lossy = FALSE;
List *pl;
/*
* Handle attributes
*/
numberOfAttributes = length(attributeList);
if (numberOfAttributes <= 0)
elog(ERROR, "DefineIndex: must specify at least one attribute");
/*
* compute heap relation id
*/
if ((relationId = RelnameFindRelid(heapRelationName)) == InvalidOid)
{
elog(ERROR, "DefineIndex: %s relation not found",
heapRelationName);
}
if (unique && strcmp(accessMethodName, "btree") != 0)
elog(ERROR, "DefineIndex: unique indices are only available with the btree access method");
if (numberOfAttributes > 1 && strcmp(accessMethodName, "btree") != 0)
elog(ERROR, "DefineIndex: multi-column indices are only available with the btree access method");
/*
* compute access method id
*/
tuple = SearchSysCacheTuple(AMNAME,
PointerGetDatum(accessMethodName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "DefineIndex: %s access method not found",
accessMethodName);
}
accessMethodId = tuple->t_data->t_oid;
/*
* WITH clause reinstated to handle lossy indices. -- JMH, 7/22/96
*/
foreach(pl, parameterList)
{
DefElem *param = (DefElem *) lfirst(pl);
if (!strcasecmp(param->defname, "islossy"))
lossy = TRUE;
else
elog(NOTICE, "Unrecognized index attribute '%s' ignored",
param->defname);
}
/*
* Convert the partial-index predicate from parsetree form to plan
* form, so it can be readily evaluated during index creation. Note:
* "predicate" comes in as a list containing (1) the predicate itself
* (a where_clause), and (2) a corresponding range table.
*
* [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
*/
if (predicate != NULL && rangetable != NIL)
{
cnfPred = cnfify((Expr *) copyObject(predicate), true);
fix_opids((Node *) cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
if (IsFuncIndex(attributeList))
{
IndexElem *funcIndex = lfirst(attributeList);
int nargs;
nargs = length(funcIndex->args);
if (nargs > INDEX_MAX_KEYS)
{
elog(ERROR,
"Too many args to function, limit of %d", INDEX_MAX_KEYS);
}
FIsetnArgs(&fInfo, nargs);
strcpy(FIgetname(&fInfo), funcIndex->name);
attributeNumberA = (AttrNumber *) palloc(nargs * sizeof attributeNumberA[0]);
classObjectId = (Oid *) palloc(sizeof classObjectId[0]);
FuncIndexArgs(funcIndex, attributeNumberA,
&(FIgetArg(&fInfo, 0)),
classObjectId, relationId);
index_create(heapRelationName,
indexRelationName,
&fInfo, NULL, accessMethodId,
numberOfAttributes, attributeNumberA,
classObjectId, parameterCount, parameterA, (Node *) cnfPred,
lossy, unique, primary);
}
else
{
attributeNumberA = (AttrNumber *) palloc(numberOfAttributes *
sizeof attributeNumberA[0]);
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]);
NormIndexAttrs(attributeList, attributeNumberA,
classObjectId, relationId);
index_create(heapRelationName, indexRelationName, NULL,
attributeList,
accessMethodId, numberOfAttributes, attributeNumberA,
classObjectId, parameterCount, parameterA, (Node *) cnfPred,
lossy, unique, primary);
}
}
/*
* ExtendIndex
* Extends a partial index.
*
* Exceptions:
* XXX
*/
void
ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
{
Oid *classObjectId;
Oid accessMethodId;
Oid indexId,
relationId;
Oid indproc;
int numberOfAttributes;
AttrNumber *attributeNumberA;
HeapTuple tuple;
FuncIndexInfo fInfo;
FuncIndexInfo *funcInfo = NULL;
Form_pg_index index;
Node *oldPred = NULL;
List *cnfPred = NULL;
PredInfo *predInfo;
Relation heapRelation;
Relation indexRelation;
int i;
/*
* compute index relation id and access method id
*/
tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(indexRelationName),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "ExtendIndex: %s index not found",
indexRelationName);
}
indexId = tuple->t_data->t_oid;
accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
/*
* find pg_index tuple
*/
tuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(indexId),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "ExtendIndex: %s is not an index",
indexRelationName);
}
/*
* Extract info from the pg_index tuple
*/
index = (Form_pg_index) GETSTRUCT(tuple);
Assert(index->indexrelid == indexId);
relationId = index->indrelid;
indproc = index->indproc;
for (i = 0; i < INDEX_MAX_KEYS; i++)
if (index->indkey[i] == InvalidAttrNumber)
break;
numberOfAttributes = i;
if (VARSIZE(&index->indpred) != 0)
{
char *predString;
predString = fmgr(F_TEXTOUT, &index->indpred);
oldPred = stringToNode(predString);
pfree(predString);
}
if (oldPred == NULL)
elog(ERROR, "ExtendIndex: %s is not a partial index",
indexRelationName);
/*
* Convert the extension predicate from parsetree form to plan form,
* so it can be readily evaluated during index creation. Note:
* "predicate" comes in as a list containing (1) the predicate itself
* (a where_clause), and (2) a corresponding range table.
*/
if (rangetable != NIL)
{
cnfPred = cnfify((Expr *) copyObject(predicate), true);
fix_opids((Node *) cnfPred);
CheckPredicate(cnfPred, rangetable, relationId);
}
/* make predInfo list to pass to index_build */
predInfo = (PredInfo *) palloc(sizeof(PredInfo));
predInfo->pred = (Node *) cnfPred;
predInfo->oldPred = oldPred;
attributeNumberA = (AttrNumber *) palloc(numberOfAttributes *
sizeof attributeNumberA[0]);
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof classObjectId[0]);
for (i = 0; i < numberOfAttributes; i++)
{
attributeNumberA[i] = index->indkey[i];
classObjectId[i] = index->indclass[i];
}
if (indproc != InvalidOid)
{
funcInfo = &fInfo;
/* FIgetnArgs(funcInfo) = numberOfAttributes; */
FIsetnArgs(funcInfo, numberOfAttributes);
tuple = SearchSysCacheTuple(PROOID,
ObjectIdGetDatum(indproc),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "ExtendIndex: index procedure not found");
namecpy(&(funcInfo->funcName),
&(((Form_pg_proc) GETSTRUCT(tuple))->proname));
FIsetProcOid(funcInfo, tuple->t_data->t_oid);
}
heapRelation = heap_open(relationId, ShareLock);
indexRelation = index_open(indexId);
InitIndexStrategy(numberOfAttributes, indexRelation, accessMethodId);
index_build(heapRelation, indexRelation, numberOfAttributes,
attributeNumberA, 0, NULL, funcInfo, predInfo);
/* heap and index rels are closed as a side-effect of index_build */
}
/*
* CheckPredicate
* Checks that the given list of partial-index predicates refer
* (via the given range table) only to the given base relation oid,
* and that they're in a form the planner can handle, i.e.,
* boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
* has to be on the left).
*/
static void
CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
{
List *item;
foreach(item, predList)
CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
}
static void
CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
{
List *clauses = NIL,
*clause;
if (is_opclause(predicate))
{
CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
return;
}
else if (or_clause(predicate) || and_clause(predicate))
clauses = ((Expr *) predicate)->args;
else
elog(ERROR, "Unsupported partial-index predicate expression type");
foreach(clause, clauses)
CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
}
static void
CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
{
Var *pred_var;
Const *pred_const;
pred_var = (Var *) get_leftop(predicate);
pred_const = (Const *) get_rightop(predicate);
if (!IsA(predicate->oper, Oper) ||
!IsA(pred_var, Var) ||
!IsA(pred_const, Const))
elog(ERROR, "Unsupported partial-index predicate clause type");
if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
elog(ERROR,
"Partial-index predicates may refer only to the base relation");
}
static void
FuncIndexArgs(IndexElem *funcIndex,
AttrNumber *attNumP,
Oid *argTypes,
Oid *opOidP,
Oid relId)
{
List *rest;
HeapTuple tuple;
Form_pg_attribute att;
tuple = SearchSysCacheTuple(CLANAME,
PointerGetDatum(funcIndex->class),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "DefineIndex: %s class not found",
funcIndex->class);
}
*opOidP = tuple->t_data->t_oid;
MemSet(argTypes, 0, 8 * sizeof(Oid));
/*
* process the function arguments
*/
for (rest = funcIndex->args; rest != NIL; rest = lnext(rest))
{
char *arg;
arg = strVal(lfirst(rest));
tuple = SearchSysCacheTuple(ATTNAME,
ObjectIdGetDatum(relId),
PointerGetDatum(arg), 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR,
"DefineIndex: attribute \"%s\" not found",
arg);
}
att = (Form_pg_attribute) GETSTRUCT(tuple);
*attNumP++ = att->attnum;
*argTypes++ = att->atttypid;
}
}
static void
NormIndexAttrs(List *attList, /* list of IndexElem's */
AttrNumber *attNumP,
Oid *classOidP,
Oid relId)
{
List *rest;
HeapTuple atttuple,
tuple;
/*
* process attributeList
*/
for (rest = attList; rest != NIL; rest = lnext(rest))
{
IndexElem *attribute;
Form_pg_attribute attform;
attribute = lfirst(rest);
if (attribute->name == NULL)
elog(ERROR, "missing attribute for define index");
atttuple = SearchSysCacheTupleCopy(ATTNAME,
ObjectIdGetDatum(relId),
PointerGetDatum(attribute->name),
0, 0);
if (!HeapTupleIsValid(atttuple))
{
elog(ERROR,
"DefineIndex: attribute \"%s\" not found",
attribute->name);
}
attform = (Form_pg_attribute) GETSTRUCT(atttuple);
*attNumP++ = attform->attnum;
/* we want the type so we can set the proper alignment, etc. */
if (attribute->typename == NULL)
{
tuple = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(attform->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "create index: type for attribute '%s' undefined",
attribute->name);
/* we just set the type name because that is all we need */
attribute->typename = makeNode(TypeName);
attribute->typename->name = nameout(&((Form_pg_type) GETSTRUCT(tuple))->typname);
/* we all need the typmod for the char and varchar types. */
attribute->typename->typmod = attform->atttypmod;
}
if (attribute->class == NULL)
{
/* no operator class specified, so find the default */
attribute->class = GetDefaultOpClass(attform->atttypid);
if (attribute->class == NULL)
{
elog(ERROR,
"Can't find a default operator class for type %u.",
attform->atttypid);
}
}
tuple = SearchSysCacheTuple(CLANAME,
PointerGetDatum(attribute->class),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
{
elog(ERROR, "DefineIndex: %s class not found",
attribute->class);
}
*classOidP++ = tuple->t_data->t_oid;
pfree(atttuple);
}
}
static char *
GetDefaultOpClass(Oid atttypid)
{
HeapTuple tuple;
tuple = SearchSysCacheTuple(CLADEFTYPE,
ObjectIdGetDatum(atttypid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
return 0;
return nameout(&(((Form_pg_opclass) GETSTRUCT(tuple))->opcname));
}
/*
* RemoveIndex
* Deletes an index.
*
* Exceptions:
* BadArg if name is invalid.
* "WARN" if index nonexistent.
* ...
*/
void
RemoveIndex(char *name)
{
HeapTuple tuple;
tuple = SearchSysCacheTuple(RELNAME,
PointerGetDatum(name),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "index \"%s\" nonexistent", name);
if (((Form_pg_class) GETSTRUCT(tuple))->relkind != RELKIND_INDEX)
{
elog(ERROR, "relation \"%s\" is of type \"%c\"",
name,
((Form_pg_class) GETSTRUCT(tuple))->relkind);
}
index_destroy(tuple->t_data->t_oid);
}