mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Replace functional-index facility with expressional indexes. Any column
of an index can now be a computed expression instead of a simple variable. Restrictions on expressions are the same as for predicates (only immutable functions, no sub-selects). This fixes problems recently introduced with inlining SQL functions, because the inlining transformation is applied to both expression trees so the planner can still match them up. Along the way, improve efficiency of handling index predicates (both predicates and index expressions are now cached by the relcache) and fix 7.3 oversight that didn't record dependencies of predicate expressions.
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.109 2003/05/14 03:26:01 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.110 2003/05/28 16:03:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -214,7 +214,7 @@ cluster(ClusterStmt *stmt)
|
||||
|
||||
/* Start a new transaction for each relation. */
|
||||
StartTransactionCommand();
|
||||
SetQuerySnapshot(); /* might be needed for functional index */
|
||||
SetQuerySnapshot(); /* might be needed for functions in indexes */
|
||||
cluster_rel(rvtc, true);
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
@@ -320,7 +320,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
|
||||
* seqscan pass over the table to copy the missing rows, but that seems
|
||||
* expensive and tedious.
|
||||
*/
|
||||
if (VARSIZE(&OldIndex->rd_index->indpred) > VARHDRSZ) /* partial? */
|
||||
if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred))
|
||||
elog(ERROR, "CLUSTER: cannot cluster on partial index");
|
||||
if (!OldIndex->rd_am->amindexnulls)
|
||||
{
|
||||
@@ -332,14 +332,24 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
|
||||
* at the first column; multicolumn-capable AMs are *required* to
|
||||
* index nulls in columns after the first.
|
||||
*/
|
||||
if (OidIsValid(OldIndex->rd_index->indproc))
|
||||
elog(ERROR, "CLUSTER: cannot cluster on functional index when index access method does not handle nulls");
|
||||
colno = OldIndex->rd_index->indkey[0];
|
||||
if (colno > 0) /* system columns are non-null */
|
||||
if (colno > 0)
|
||||
{
|
||||
/* ordinary user attribute */
|
||||
if (!OldHeap->rd_att->attrs[colno - 1]->attnotnull)
|
||||
elog(ERROR, "CLUSTER: cannot cluster when index access method does not handle nulls"
|
||||
"\n\tYou may be able to work around this by marking column \"%s\" NOT NULL",
|
||||
NameStr(OldHeap->rd_att->attrs[colno - 1]->attname));
|
||||
}
|
||||
else if (colno < 0)
|
||||
{
|
||||
/* system column --- okay, always non-null */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* index expression, lose... */
|
||||
elog(ERROR, "CLUSTER: cannot cluster on expressional index when index access method does not handle nulls");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -557,43 +567,24 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
|
||||
foreach(indlist, RelationGetIndexList(OldHeap))
|
||||
{
|
||||
Oid indexOID = lfirsto(indlist);
|
||||
HeapTuple indexTuple;
|
||||
HeapTuple classTuple;
|
||||
Form_pg_index indexForm;
|
||||
Form_pg_class classForm;
|
||||
Relation oldIndex;
|
||||
IndexAttrs *attrs;
|
||||
|
||||
indexTuple = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexOID),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indexTuple))
|
||||
elog(ERROR, "Cache lookup failed for index %u", indexOID);
|
||||
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
Assert(indexForm->indexrelid == indexOID);
|
||||
oldIndex = index_open(indexOID);
|
||||
|
||||
attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
|
||||
attrs->indexOID = indexOID;
|
||||
attrs->indexInfo = BuildIndexInfo(indexForm);
|
||||
attrs->indexName = pstrdup(NameStr(oldIndex->rd_rel->relname));
|
||||
attrs->accessMethodOID = oldIndex->rd_rel->relam;
|
||||
attrs->indexInfo = BuildIndexInfo(oldIndex);
|
||||
attrs->classOID = (Oid *)
|
||||
palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
||||
memcpy(attrs->classOID, indexForm->indclass,
|
||||
memcpy(attrs->classOID, oldIndex->rd_index->indclass,
|
||||
sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
|
||||
/* We adjust the isclustered attribute to correct new state */
|
||||
attrs->isclustered = (indexOID == OldIndex);
|
||||
|
||||
/* Name and access method of each index come from pg_class */
|
||||
classTuple = SearchSysCache(RELOID,
|
||||
ObjectIdGetDatum(indexOID),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(classTuple))
|
||||
elog(ERROR, "Cache lookup failed for index %u", indexOID);
|
||||
classForm = (Form_pg_class) GETSTRUCT(classTuple);
|
||||
|
||||
attrs->indexName = pstrdup(NameStr(classForm->relname));
|
||||
attrs->accessMethodOID = classForm->relam;
|
||||
|
||||
ReleaseSysCache(classTuple);
|
||||
ReleaseSysCache(indexTuple);
|
||||
index_close(oldIndex);
|
||||
|
||||
/*
|
||||
* Cons the gathered data into the list. We do not care about
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.99 2003/05/14 03:26:01 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.100 2003/05/28 16:03:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "optimizer/prep.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
@@ -39,19 +40,13 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->funcname != NIL)
|
||||
|
||||
/* non-export function prototypes */
|
||||
static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
|
||||
static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
|
||||
IndexElem *funcIndex,
|
||||
Oid relId,
|
||||
char *accessMethodName, Oid accessMethodId);
|
||||
static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
|
||||
static void CheckPredicate(List *predList);
|
||||
static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
|
||||
List *attList,
|
||||
Oid relId,
|
||||
char *accessMethodName, Oid accessMethodId);
|
||||
static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
static Oid GetIndexOpClass(List *opclass, Oid attrType,
|
||||
char *accessMethodName, Oid accessMethodId);
|
||||
static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
|
||||
|
||||
@@ -59,8 +54,8 @@ static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
|
||||
* DefineIndex
|
||||
* Creates a new index.
|
||||
*
|
||||
* 'attributeList' is a list of IndexElem specifying either a functional
|
||||
* index or a list of attributes to index on.
|
||||
* 'attributeList' is a list of IndexElem specifying columns and expressions
|
||||
* to index on.
|
||||
* 'predicate' is the qual specified in the where clause.
|
||||
* 'rangetable' is needed to interpret the predicate.
|
||||
*/
|
||||
@@ -155,6 +150,16 @@ DefineIndex(RangeVar *heapRelation,
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* If a range table was created then check that only the base rel is
|
||||
* mentioned.
|
||||
*/
|
||||
if (rangetable != NIL)
|
||||
{
|
||||
if (length(rangetable) != 1 || getrelid(1, rangetable) != relationId)
|
||||
elog(ERROR, "index expressions and predicates may refer only to the base relation");
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the partial-index predicate from parsetree form to an
|
||||
* implicit-AND qual expression, for easier evaluation at runtime.
|
||||
@@ -164,14 +169,14 @@ DefineIndex(RangeVar *heapRelation,
|
||||
if (predicate)
|
||||
{
|
||||
cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
|
||||
CheckPredicate(cnfPred, rangetable, relationId);
|
||||
CheckPredicate(cnfPred);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that all of the attributes in a primary key are marked
|
||||
* as not null, otherwise attempt to ALTER TABLE .. SET NOT NULL
|
||||
*/
|
||||
if (primary && !IsFuncIndex(attributeList))
|
||||
if (primary)
|
||||
{
|
||||
List *keys;
|
||||
|
||||
@@ -180,6 +185,9 @@ DefineIndex(RangeVar *heapRelation,
|
||||
IndexElem *key = (IndexElem *) lfirst(keys);
|
||||
HeapTuple atttuple;
|
||||
|
||||
if (!key->name)
|
||||
elog(ERROR, "primary keys cannot be expressions");
|
||||
|
||||
/* System attributes are never null, so no problem */
|
||||
if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids))
|
||||
continue;
|
||||
@@ -216,43 +224,16 @@ DefineIndex(RangeVar *heapRelation,
|
||||
* structure
|
||||
*/
|
||||
indexInfo = makeNode(IndexInfo);
|
||||
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
||||
indexInfo->ii_Expressions = NIL; /* for now */
|
||||
indexInfo->ii_ExpressionsState = NIL;
|
||||
indexInfo->ii_Predicate = cnfPred;
|
||||
indexInfo->ii_PredicateState = NIL;
|
||||
indexInfo->ii_FuncOid = InvalidOid;
|
||||
indexInfo->ii_Unique = unique;
|
||||
|
||||
if (IsFuncIndex(attributeList))
|
||||
{
|
||||
IndexElem *funcIndex = (IndexElem *) lfirst(attributeList);
|
||||
int nargs;
|
||||
|
||||
/* Parser should have given us only one list item, but check */
|
||||
if (numberOfAttributes != 1)
|
||||
elog(ERROR, "Functional index can only have one attribute");
|
||||
|
||||
nargs = length(funcIndex->args);
|
||||
if (nargs > INDEX_MAX_KEYS)
|
||||
elog(ERROR, "Index function can take at most %d arguments",
|
||||
INDEX_MAX_KEYS);
|
||||
|
||||
indexInfo->ii_NumIndexAttrs = 1;
|
||||
indexInfo->ii_NumKeyAttrs = nargs;
|
||||
|
||||
classObjectId = (Oid *) palloc(sizeof(Oid));
|
||||
|
||||
FuncIndexArgs(indexInfo, classObjectId, funcIndex,
|
||||
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
||||
ComputeIndexAttrs(indexInfo, classObjectId, attributeList,
|
||||
relationId, accessMethodName, accessMethodId);
|
||||
}
|
||||
else
|
||||
{
|
||||
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
||||
indexInfo->ii_NumKeyAttrs = numberOfAttributes;
|
||||
|
||||
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
|
||||
|
||||
NormIndexAttrs(indexInfo, classObjectId, attributeList,
|
||||
relationId, accessMethodName, accessMethodId);
|
||||
}
|
||||
|
||||
index_create(relationId, indexRelationName,
|
||||
indexInfo, accessMethodId, classObjectId,
|
||||
@@ -271,8 +252,7 @@ DefineIndex(RangeVar *heapRelation,
|
||||
|
||||
/*
|
||||
* CheckPredicate
|
||||
* Checks that the given list of partial-index predicates refer
|
||||
* (via the given range table) only to the given base relation oid.
|
||||
* Checks that the given list of partial-index predicates is valid.
|
||||
*
|
||||
* This used to also constrain the form of the predicate to forms that
|
||||
* indxpath.c could do something with. However, that seems overly
|
||||
@@ -281,14 +261,9 @@ DefineIndex(RangeVar *heapRelation,
|
||||
* any evaluatable predicate will work. So accept any predicate here
|
||||
* (except ones requiring a plan), and let indxpath.c fend for itself.
|
||||
*/
|
||||
|
||||
static void
|
||||
CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
|
||||
CheckPredicate(List *predList)
|
||||
{
|
||||
if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid)
|
||||
elog(ERROR,
|
||||
"Partial-index predicates may refer only to the base relation");
|
||||
|
||||
/*
|
||||
* We don't currently support generation of an actual query plan for a
|
||||
* predicate, only simple scalar expressions; hence these
|
||||
@@ -301,119 +276,19 @@ CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
|
||||
|
||||
/*
|
||||
* A predicate using mutable functions is probably wrong, for the same
|
||||
* reasons that we don't allow a functional index to use one.
|
||||
* reasons that we don't allow an index expression to use one.
|
||||
*/
|
||||
if (contain_mutable_functions((Node *) predList))
|
||||
elog(ERROR, "Functions in index predicate must be marked IMMUTABLE");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
FuncIndexArgs(IndexInfo *indexInfo,
|
||||
Oid *classOidP,
|
||||
IndexElem *funcIndex,
|
||||
Oid relId,
|
||||
char *accessMethodName,
|
||||
Oid accessMethodId)
|
||||
{
|
||||
Oid argTypes[FUNC_MAX_ARGS];
|
||||
List *arglist;
|
||||
int nargs = 0;
|
||||
int i;
|
||||
FuncDetailCode fdresult;
|
||||
Oid funcid;
|
||||
Oid rettype;
|
||||
bool retset;
|
||||
Oid *true_typeids;
|
||||
|
||||
/*
|
||||
* process the function arguments, which are a list of T_String
|
||||
* (someday ought to allow more general expressions?)
|
||||
*
|
||||
* Note caller already checked that list is not too long.
|
||||
*/
|
||||
MemSet(argTypes, 0, sizeof(argTypes));
|
||||
|
||||
foreach(arglist, funcIndex->args)
|
||||
{
|
||||
char *arg = strVal(lfirst(arglist));
|
||||
HeapTuple tuple;
|
||||
Form_pg_attribute att;
|
||||
|
||||
tuple = SearchSysCacheAttName(relId, arg);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "DefineIndex: column \"%s\" named in key does not exist", arg);
|
||||
att = (Form_pg_attribute) GETSTRUCT(tuple);
|
||||
indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum;
|
||||
argTypes[nargs] = att->atttypid;
|
||||
ReleaseSysCache(tuple);
|
||||
nargs++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the function procedure to get its OID and result type.
|
||||
*
|
||||
* We rely on parse_func.c to find the correct function in the possible
|
||||
* presence of binary-compatible types. However, parse_func may do
|
||||
* too much: it will accept a function that requires run-time coercion
|
||||
* of input types, and the executor is not currently set up to support
|
||||
* that. So, check to make sure that the selected function has
|
||||
* exact-match or binary-compatible input types.
|
||||
*/
|
||||
fdresult = func_get_detail(funcIndex->funcname, funcIndex->args,
|
||||
nargs, argTypes,
|
||||
&funcid, &rettype, &retset,
|
||||
&true_typeids);
|
||||
if (fdresult != FUNCDETAIL_NORMAL)
|
||||
{
|
||||
if (fdresult == FUNCDETAIL_AGGREGATE)
|
||||
elog(ERROR, "DefineIndex: functional index may not use an aggregate function");
|
||||
else if (fdresult == FUNCDETAIL_COERCION)
|
||||
elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
|
||||
"\n\tTry specifying the index opclass you want to use, instead");
|
||||
else
|
||||
func_error("DefineIndex", funcIndex->funcname, nargs, argTypes,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (retset)
|
||||
elog(ERROR, "DefineIndex: cannot index on a function returning a set");
|
||||
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
if (!IsBinaryCoercible(argTypes[i], true_typeids[i]))
|
||||
func_error("DefineIndex", funcIndex->funcname, nargs, true_typeids,
|
||||
"Index function must be binary-compatible with table datatype");
|
||||
}
|
||||
|
||||
/*
|
||||
* Require that the function be marked immutable. Using a mutable
|
||||
* function for a functional index is highly questionable, since if
|
||||
* you aren't going to get the same result for the same data every
|
||||
* time, it's not clear what the index entries mean at all.
|
||||
*/
|
||||
if (func_volatile(funcid) != PROVOLATILE_IMMUTABLE)
|
||||
elog(ERROR, "DefineIndex: index function must be marked IMMUTABLE");
|
||||
|
||||
/* Process opclass, using func return type as default type */
|
||||
|
||||
classOidP[0] = GetAttrOpClass(funcIndex, rettype,
|
||||
accessMethodName, accessMethodId);
|
||||
|
||||
/* OK, return results */
|
||||
|
||||
indexInfo->ii_FuncOid = funcid;
|
||||
/* Need to do the fmgr function lookup now, too */
|
||||
fmgr_info(funcid, &indexInfo->ii_FuncInfo);
|
||||
}
|
||||
|
||||
static void
|
||||
NormIndexAttrs(IndexInfo *indexInfo,
|
||||
Oid *classOidP,
|
||||
List *attList, /* list of IndexElem's */
|
||||
Oid relId,
|
||||
char *accessMethodName,
|
||||
Oid accessMethodId)
|
||||
ComputeIndexAttrs(IndexInfo *indexInfo,
|
||||
Oid *classOidP,
|
||||
List *attList, /* list of IndexElem's */
|
||||
Oid relId,
|
||||
char *accessMethodName,
|
||||
Oid accessMethodId)
|
||||
{
|
||||
List *rest;
|
||||
int attn = 0;
|
||||
@@ -424,31 +299,75 @@ NormIndexAttrs(IndexInfo *indexInfo,
|
||||
foreach(rest, attList)
|
||||
{
|
||||
IndexElem *attribute = (IndexElem *) lfirst(rest);
|
||||
HeapTuple atttuple;
|
||||
Form_pg_attribute attform;
|
||||
Oid atttype;
|
||||
|
||||
if (attribute->name == NULL)
|
||||
elog(ERROR, "missing attribute for define index");
|
||||
if (attribute->name != NULL)
|
||||
{
|
||||
/* Simple index attribute */
|
||||
HeapTuple atttuple;
|
||||
Form_pg_attribute attform;
|
||||
|
||||
atttuple = SearchSysCacheAttName(relId, attribute->name);
|
||||
if (!HeapTupleIsValid(atttuple))
|
||||
elog(ERROR, "DefineIndex: attribute \"%s\" not found",
|
||||
attribute->name);
|
||||
attform = (Form_pg_attribute) GETSTRUCT(atttuple);
|
||||
Assert(attribute->expr == NULL);
|
||||
atttuple = SearchSysCacheAttName(relId, attribute->name);
|
||||
if (!HeapTupleIsValid(atttuple))
|
||||
elog(ERROR, "DefineIndex: attribute \"%s\" not found",
|
||||
attribute->name);
|
||||
attform = (Form_pg_attribute) GETSTRUCT(atttuple);
|
||||
indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
|
||||
atttype = attform->atttypid;
|
||||
ReleaseSysCache(atttuple);
|
||||
}
|
||||
else if (attribute->expr && IsA(attribute->expr, Var))
|
||||
{
|
||||
/* Tricky tricky, he wrote (column) ... treat as simple attr */
|
||||
Var *var = (Var *) attribute->expr;
|
||||
|
||||
indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
|
||||
indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
|
||||
atttype = get_atttype(relId, var->varattno);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Index expression */
|
||||
Assert(attribute->expr != NULL);
|
||||
indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
|
||||
indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
|
||||
attribute->expr);
|
||||
atttype = exprType(attribute->expr);
|
||||
|
||||
classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
|
||||
accessMethodName, accessMethodId);
|
||||
/*
|
||||
* We don't currently support generation of an actual query plan
|
||||
* for an index expression, only simple scalar expressions;
|
||||
* hence these restrictions.
|
||||
*/
|
||||
if (contain_subplans(attribute->expr))
|
||||
elog(ERROR, "Cannot use subselect in index expression");
|
||||
if (contain_agg_clause(attribute->expr))
|
||||
elog(ERROR, "Cannot use aggregate in index expression");
|
||||
|
||||
ReleaseSysCache(atttuple);
|
||||
/*
|
||||
* A expression using mutable functions is probably wrong,
|
||||
* since if you aren't going to get the same result for the same
|
||||
* data every time, it's not clear what the index entries mean at
|
||||
* all.
|
||||
*/
|
||||
if (contain_mutable_functions(attribute->expr))
|
||||
elog(ERROR, "Functions in index expression must be marked IMMUTABLE");
|
||||
}
|
||||
|
||||
classOidP[attn] = GetIndexOpClass(attribute->opclass,
|
||||
atttype,
|
||||
accessMethodName,
|
||||
accessMethodId);
|
||||
attn++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve possibly-defaulted operator class specification
|
||||
*/
|
||||
static Oid
|
||||
GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
char *accessMethodName, Oid accessMethodId)
|
||||
GetIndexOpClass(List *opclass, Oid attrType,
|
||||
char *accessMethodName, Oid accessMethodId)
|
||||
{
|
||||
char *schemaname;
|
||||
char *opcname;
|
||||
@@ -456,7 +375,32 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
Oid opClassId,
|
||||
opInputType;
|
||||
|
||||
if (attribute->opclass == NIL)
|
||||
/*
|
||||
* Release 7.0 removed network_ops, timespan_ops, and
|
||||
* datetime_ops, so we ignore those opclass names
|
||||
* so the default *_ops is used. This can be
|
||||
* removed in some later release. bjm 2000/02/07
|
||||
*
|
||||
* Release 7.1 removes lztext_ops, so suppress that too
|
||||
* for a while. tgl 2000/07/30
|
||||
*
|
||||
* Release 7.2 renames timestamp_ops to timestamptz_ops,
|
||||
* so suppress that too for awhile. I'm starting to
|
||||
* think we need a better approach. tgl 2000/10/01
|
||||
*/
|
||||
if (length(opclass) == 1)
|
||||
{
|
||||
char *claname = strVal(lfirst(opclass));
|
||||
|
||||
if (strcmp(claname, "network_ops") == 0 ||
|
||||
strcmp(claname, "timespan_ops") == 0 ||
|
||||
strcmp(claname, "datetime_ops") == 0 ||
|
||||
strcmp(claname, "lztext_ops") == 0 ||
|
||||
strcmp(claname, "timestamp_ops") == 0)
|
||||
opclass = NIL;
|
||||
}
|
||||
|
||||
if (opclass == NIL)
|
||||
{
|
||||
/* no operator class specified, so find the default */
|
||||
opClassId = GetDefaultOpClass(attrType, accessMethodId);
|
||||
@@ -473,7 +417,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
*/
|
||||
|
||||
/* deconstruct the name list */
|
||||
DeconstructQualifiedName(attribute->opclass, &schemaname, &opcname);
|
||||
DeconstructQualifiedName(opclass, &schemaname, &opcname);
|
||||
|
||||
if (schemaname)
|
||||
{
|
||||
@@ -501,7 +445,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "DefineIndex: operator class \"%s\" not supported by access method \"%s\"",
|
||||
NameListToString(attribute->opclass), accessMethodName);
|
||||
NameListToString(opclass), accessMethodName);
|
||||
|
||||
/*
|
||||
* Verify that the index operator class accepts this datatype. Note
|
||||
@@ -512,7 +456,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
|
||||
|
||||
if (!IsBinaryCoercible(attrType, opInputType))
|
||||
elog(ERROR, "operator class \"%s\" does not accept data type %s",
|
||||
NameListToString(attribute->opclass), format_type_be(attrType));
|
||||
NameListToString(opclass), format_type_be(attrType));
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
@@ -773,7 +717,7 @@ ReindexDatabase(const char *dbname, bool force, bool all)
|
||||
for (i = 0; i < relcnt; i++)
|
||||
{
|
||||
StartTransactionCommand();
|
||||
SetQuerySnapshot(); /* might be needed for functional index */
|
||||
SetQuerySnapshot(); /* might be needed for functions in indexes */
|
||||
if (reindex_relation(relids[i], force))
|
||||
elog(NOTICE, "relation %u was reindexed", relids[i]);
|
||||
CommitTransactionCommand();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.72 2003/04/29 22:13:08 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.73 2003/05/28 16:03:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1103,6 +1103,7 @@ renameatt(Oid myrelid,
|
||||
Relation attrelation;
|
||||
HeapTuple atttup;
|
||||
Form_pg_attribute attform;
|
||||
int attnum;
|
||||
List *indexoidlist;
|
||||
List *indexoidscan;
|
||||
|
||||
@@ -1178,7 +1179,8 @@ renameatt(Oid myrelid,
|
||||
oldattname);
|
||||
attform = (Form_pg_attribute) GETSTRUCT(atttup);
|
||||
|
||||
if (attform->attnum < 0)
|
||||
attnum = attform->attnum;
|
||||
if (attnum < 0)
|
||||
elog(ERROR, "renameatt: system attribute \"%s\" may not be renamed",
|
||||
oldattname);
|
||||
|
||||
@@ -1217,43 +1219,48 @@ renameatt(Oid myrelid,
|
||||
{
|
||||
Oid indexoid = lfirsto(indexoidscan);
|
||||
HeapTuple indextup;
|
||||
Form_pg_index indexform;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* First check to see if index is a functional index. If so, its
|
||||
* column name is a function name and shouldn't be renamed here.
|
||||
* Scan through index columns to see if there's any simple index
|
||||
* entries for this attribute. We ignore expressional entries.
|
||||
*/
|
||||
indextup = SearchSysCache(INDEXRELID,
|
||||
ObjectIdGetDatum(indexoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(indextup))
|
||||
elog(ERROR, "renameatt: can't find index id %u", indexoid);
|
||||
if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc))
|
||||
indexform = (Form_pg_index) GETSTRUCT(indextup);
|
||||
|
||||
for (i = 0; i < indexform->indnatts; i++)
|
||||
{
|
||||
ReleaseSysCache(indextup);
|
||||
continue;
|
||||
if (attnum != indexform->indkey[i])
|
||||
continue;
|
||||
/*
|
||||
* Found one, rename it.
|
||||
*/
|
||||
atttup = SearchSysCacheCopy(ATTNUM,
|
||||
ObjectIdGetDatum(indexoid),
|
||||
Int16GetDatum(i + 1),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(atttup))
|
||||
continue; /* should we raise an error? */
|
||||
/*
|
||||
* Update the (copied) attribute tuple.
|
||||
*/
|
||||
namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
|
||||
newattname);
|
||||
|
||||
simple_heap_update(attrelation, &atttup->t_self, atttup);
|
||||
|
||||
/* keep system catalog indexes current */
|
||||
CatalogUpdateIndexes(attrelation, atttup);
|
||||
|
||||
heap_freetuple(atttup);
|
||||
}
|
||||
|
||||
ReleaseSysCache(indextup);
|
||||
|
||||
/*
|
||||
* Okay, look to see if any column name of the index matches the
|
||||
* old attribute name.
|
||||
*/
|
||||
atttup = SearchSysCacheCopyAttName(indexoid, oldattname);
|
||||
if (!HeapTupleIsValid(atttup))
|
||||
continue; /* Nope, so ignore it */
|
||||
|
||||
/*
|
||||
* Update the (copied) attribute tuple.
|
||||
*/
|
||||
namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
|
||||
newattname);
|
||||
|
||||
simple_heap_update(attrelation, &atttup->t_self, atttup);
|
||||
|
||||
/* keep system catalog indexes current */
|
||||
CatalogUpdateIndexes(attrelation, atttup);
|
||||
|
||||
heap_freetuple(atttup);
|
||||
}
|
||||
|
||||
freeList(indexoidlist);
|
||||
@@ -1986,8 +1993,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, bool recurse,
|
||||
* Loop over each attribute in the primary key and see if it
|
||||
* matches the to-be-altered attribute
|
||||
*/
|
||||
for (i = 0; i < INDEX_MAX_KEYS &&
|
||||
indexStruct->indkey[i] != InvalidAttrNumber; i++)
|
||||
for (i = 0; i < indexStruct->indnatts; i++)
|
||||
{
|
||||
if (indexStruct->indkey[i] == attnum)
|
||||
elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
|
||||
@@ -3185,9 +3191,10 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
|
||||
|
||||
/*
|
||||
* Now build the list of PK attributes from the indkey definition
|
||||
* (we assume a primary key cannot have expressional elements)
|
||||
*/
|
||||
*attnamelist = NIL;
|
||||
for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
|
||||
for (i = 0; i < indexStruct->indnatts; i++)
|
||||
{
|
||||
int pkattno = indexStruct->indkey[i];
|
||||
|
||||
@@ -3241,26 +3248,40 @@ transformFkeyCheckAttrs(Relation pkrel,
|
||||
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
|
||||
/*
|
||||
* Must be unique, not a functional index, and not a partial index
|
||||
* Must have the right number of columns; must be unique and not a
|
||||
* partial index; forget it if there are any expressions, too
|
||||
*/
|
||||
if (indexStruct->indisunique &&
|
||||
indexStruct->indproc == InvalidOid &&
|
||||
VARSIZE(&indexStruct->indpred) <= VARHDRSZ)
|
||||
if (indexStruct->indnatts == numattrs &&
|
||||
indexStruct->indisunique &&
|
||||
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
|
||||
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
|
||||
{
|
||||
for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
|
||||
;
|
||||
if (i == numattrs)
|
||||
/*
|
||||
* The given attnum list may match the index columns in any
|
||||
* order. Check that each list is a subset of the other.
|
||||
*/
|
||||
for (i = 0; i < numattrs; i++)
|
||||
{
|
||||
found = false;
|
||||
for (j = 0; j < numattrs; j++)
|
||||
{
|
||||
if (attnums[i] == indexStruct->indkey[j])
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
/*
|
||||
* The given attnum list may match the index columns in any
|
||||
* order. Check that each list is a subset of the other.
|
||||
*/
|
||||
for (i = 0; i < numattrs; i++)
|
||||
{
|
||||
found = false;
|
||||
for (j = 0; j < numattrs; j++)
|
||||
{
|
||||
if (attnums[i] == indexStruct->indkey[j])
|
||||
if (attnums[j] == indexStruct->indkey[i])
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
@@ -3269,23 +3290,6 @@ transformFkeyCheckAttrs(Relation pkrel,
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
for (i = 0; i < numattrs; i++)
|
||||
{
|
||||
found = false;
|
||||
for (j = 0; j < numattrs; j++)
|
||||
{
|
||||
if (attnums[j] == indexStruct->indkey[i])
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ReleaseSysCache(indexTuple);
|
||||
@@ -4020,12 +4024,12 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
|
||||
|
||||
indexInfo = makeNode(IndexInfo);
|
||||
indexInfo->ii_NumIndexAttrs = 2;
|
||||
indexInfo->ii_NumKeyAttrs = 2;
|
||||
indexInfo->ii_KeyAttrNumbers[0] = 1;
|
||||
indexInfo->ii_KeyAttrNumbers[1] = 2;
|
||||
indexInfo->ii_Expressions = NIL;
|
||||
indexInfo->ii_ExpressionsState = NIL;
|
||||
indexInfo->ii_Predicate = NIL;
|
||||
indexInfo->ii_PredicateState = NIL;
|
||||
indexInfo->ii_FuncOid = InvalidOid;
|
||||
indexInfo->ii_Unique = true;
|
||||
|
||||
classObjectId[0] = OID_BTREE_OPS_OID;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.254 2003/05/27 17:49:45 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.255 2003/05/28 16:03:56 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -304,7 +304,7 @@ vacuum(VacuumStmt *vacstmt)
|
||||
if (vacstmt->vacuum)
|
||||
{
|
||||
StartTransactionCommand();
|
||||
SetQuerySnapshot(); /* might be needed for functional index */
|
||||
SetQuerySnapshot(); /* might be needed for functions in indexes */
|
||||
}
|
||||
else
|
||||
old_context = MemoryContextSwitchTo(anl_context);
|
||||
@@ -728,7 +728,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
|
||||
|
||||
/* Begin a transaction for vacuuming this relation */
|
||||
StartTransactionCommand();
|
||||
SetQuerySnapshot(); /* might be needed for functional index */
|
||||
SetQuerySnapshot(); /* might be needed for functions in indexes */
|
||||
|
||||
/*
|
||||
* Check for user-requested abort. Note we want this to be inside a
|
||||
@@ -3028,7 +3028,10 @@ vac_is_partial_index(Relation indrel)
|
||||
return true;
|
||||
|
||||
/* Otherwise, look to see if there's a partial-index predicate */
|
||||
return (VARSIZE(&indrel->rd_index->indpred) > VARHDRSZ);
|
||||
if (!heap_attisnull(indrel->rd_indextuple, Anum_pg_index_indpred))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user