indexcmds.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 777 行 · 第 1/2 页
C
777 行
/*------------------------------------------------------------------------- * * indexcmds.c * POSTGRES define and remove index code. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.114 2003/10/02 06:34:03 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/catalog.h"#include "catalog/catname.h"#include "catalog/dependency.h"#include "catalog/heap.h"#include "catalog/index.h"#include "catalog/namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_proc.h"#include "commands/dbcommands.h"#include "commands/defrem.h"#include "commands/tablecmds.h"#include "executor/executor.h"#include "miscadmin.h"#include "optimizer/clauses.h"#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"#include "utils/lsyscache.h"#include "utils/syscache.h"/* non-export function prototypes */static void CheckPredicate(List *predList);static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, List *attList, Oid relId, char *accessMethodName, Oid accessMethodId);static Oid GetIndexOpClass(List *opclass, Oid attrType, char *accessMethodName, Oid accessMethodId);static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);/* * DefineIndex * Creates a new index. * * '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. */voidDefineIndex(RangeVar *heapRelation, char *indexRelationName, char *accessMethodName, List *attributeList, bool unique, bool primary, bool isconstraint, Expr *predicate, List *rangetable){ Oid *classObjectId; Oid accessMethodId; Oid relationId; Oid namespaceId; Relation rel; HeapTuple tuple; Form_pg_am accessMethodForm; IndexInfo *indexInfo; int numberOfAttributes; List *cnfPred = NIL; /* * count attributes in index */ numberOfAttributes = length(attributeList); if (numberOfAttributes <= 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("must specify at least one column"))); if (numberOfAttributes > INDEX_MAX_KEYS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("cannot use more than %d columns in an index", INDEX_MAX_KEYS))); /* * Open heap relation, acquire a suitable lock on it, remember its OID */ rel = heap_openrv(heapRelation, ShareLock); /* Note: during bootstrap may see uncataloged relation */ if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_UNCATALOGED) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", heapRelation->relname))); relationId = RelationGetRelid(rel); namespaceId = RelationGetNamespace(rel); heap_close(rel, NoLock); /* * Verify we (still) have CREATE rights in the rel's namespace. * (Presumably we did when the rel was created, but maybe not * anymore.) Skip check if bootstrapping, since permissions machinery * may not be working yet. */ if (!IsBootstrapProcessingMode()) { AclResult aclresult; aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); } /* * look up the access method, verify it can handle the requested * features */ tuple = SearchSysCache(AMNAME, PointerGetDatum(accessMethodName), 0, 0, 0); if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("access method \"%s\" does not exist", accessMethodName))); accessMethodId = HeapTupleGetOid(tuple); accessMethodForm = (Form_pg_am) GETSTRUCT(tuple); if (unique && !accessMethodForm->amcanunique) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"%s\" does not support unique indexes", accessMethodName))); if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"%s\" does not support multicolumn indexes", accessMethodName))); 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) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("index expressions and predicates may refer only to the table being indexed"))); } /* * Convert the partial-index predicate from parsetree form to an * implicit-AND qual expression, for easier evaluation at runtime. * While we are at it, we reduce it to a canonical (CNF or DNF) form * to simplify the task of proving implications. */ if (predicate) { cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true); 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) { List *keys; foreach(keys, attributeList) { IndexElem *key = (IndexElem *) lfirst(keys); HeapTuple atttuple; if (!key->name) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("primary keys cannot be expressions"))); /* System attributes are never null, so no problem */ if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) continue; atttuple = SearchSysCacheAttName(relationId, key->name); if (HeapTupleIsValid(atttuple)) { if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull) { /* * Try to make it NOT NULL. * * XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade * to child tables? Currently, since the PRIMARY KEY * itself doesn't cascade, we don't cascade the * notnull constraint either; but this is pretty * debatable. */ AlterTableAlterColumnSetNotNull(relationId, false, key->name); } ReleaseSysCache(atttuple); } else { /* This shouldn't happen if parser did its job ... */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" named in key does not exist", key->name))); } } } /* * Prepare arguments for index_create, primarily an IndexInfo * 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_Unique = unique; classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); ComputeIndexAttrs(indexInfo, classObjectId, attributeList, relationId, accessMethodName, accessMethodId); index_create(relationId, indexRelationName, indexInfo, accessMethodId, classObjectId, primary, isconstraint, allowSystemTableMods); /* * We update the relation's pg_class tuple even if it already has * relhasindex = true. This is needed to cause a shared-cache-inval * message to be sent for the pg_class tuple, which will cause other * backends to flush their relcache entries and in particular their * cached lists of the indexes for this relation. */ setRelhasindex(relationId, true, primary, InvalidOid);}/* * CheckPredicate * 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 * restrictive. One useful application of partial indexes is to apply * a UNIQUE constraint across a subset of a table, and in that scenario * any evaluatable predicate will work. So accept any predicate here * (except ones requiring a plan), and let indxpath.c fend for itself. */static voidCheckPredicate(List *predList){ /* * We don't currently support generation of an actual query plan for a * predicate, only simple scalar expressions; hence these * restrictions. */ if (contain_subplans((Node *) predList)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in index predicate"))); if (contain_agg_clause((Node *) predList)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate in index predicate"))); /* * A predicate using mutable functions is probably wrong, for the same * reasons that we don't allow an index expression to use one. */ if (contain_mutable_functions((Node *) predList)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("functions in index predicate must be marked IMMUTABLE")));}static voidComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, List *attList, /* list of IndexElem's */ Oid relId, char *accessMethodName, Oid accessMethodId){ List *rest; int attn = 0; /* * process attributeList */ foreach(rest, attList) { IndexElem *attribute = (IndexElem *) lfirst(rest); Oid atttype; if (attribute->name != NULL) { /* Simple index attribute */ HeapTuple atttuple; Form_pg_attribute attform; Assert(attribute->expr == NULL); atttuple = SearchSysCacheAttName(relId, attribute->name); if (!HeapTupleIsValid(atttuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" does not exist", 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] = 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); /* * 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)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot use subquery in index expression"))); if (contain_agg_clause(attribute->expr)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in index expression"))); /* * 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)) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("functions in index expression must be marked IMMUTABLE"))); } classOidP[attn] = GetIndexOpClass(attribute->opclass, atttype, accessMethodName,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?