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 + -
显示快捷键?