⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 opclasscmds.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * opclasscmds.c * *	  Routines for opclass manipulation commands * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.38 2005/10/15 02:49:15 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "catalog/dependency.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_am.h"#include "catalog/pg_amop.h"#include "catalog/pg_amproc.h"#include "catalog/pg_namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_operator.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "commands/defrem.h"#include "miscadmin.h"#include "parser/parse_func.h"#include "parser/parse_oper.h"#include "parser/parse_type.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* * We use lists of this struct type to keep track of both operators and * procedures during DefineOpClass. */typedef struct{	Oid			object;			/* operator or support proc's OID */	int			number;			/* strategy or support proc number */	Oid			subtype;		/* subtype */	bool		recheck;		/* oper recheck flag (unused for proc) */} OpClassMember;static Oid	assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid);static Oid	assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid);static void addClassMember(List **list, OpClassMember *member, bool isProc);static void storeOperators(Oid opclassoid, List *operators);static void storeProcedures(Oid opclassoid, List *procedures);/* * DefineOpClass *		Define a new index operator class. */voidDefineOpClass(CreateOpClassStmt *stmt){	char	   *opcname;		/* name of opclass we're creating */	Oid			amoid,			/* our AM's oid */				typeoid,		/* indexable datatype oid */				storageoid,		/* storage datatype oid, if any */				namespaceoid,	/* namespace to create opclass in */				opclassoid;		/* oid of opclass we create */	int			numOperators,	/* amstrategies value */				numProcs;		/* amsupport value */	List	   *operators;		/* OpClassMember list for operators */	List	   *procedures;		/* OpClassMember list for support procs */	ListCell   *l;	Relation	rel;	HeapTuple	tup;	Datum		values[Natts_pg_opclass];	char		nulls[Natts_pg_opclass];	AclResult	aclresult;	NameData	opcName;	int			i;	ObjectAddress myself,				referenced;	/* Convert list of names to a name and namespace */	namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,													 &opcname);	/* Check we have creation rights in target namespace */	aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);	if (aclresult != ACLCHECK_OK)		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,					   get_namespace_name(namespaceoid));	/* Get necessary info about access method */	tup = SearchSysCache(AMNAME,						 CStringGetDatum(stmt->amname),						 0, 0, 0);	if (!HeapTupleIsValid(tup))		ereport(ERROR,				(errcode(ERRCODE_UNDEFINED_OBJECT),				 errmsg("access method \"%s\" does not exist",						stmt->amname)));	amoid = HeapTupleGetOid(tup);	numOperators = ((Form_pg_am) GETSTRUCT(tup))->amstrategies;	numProcs = ((Form_pg_am) GETSTRUCT(tup))->amsupport;	/* XXX Should we make any privilege check against the AM? */	ReleaseSysCache(tup);	/*	 * Currently, we require superuser privileges to create an opclass. This	 * seems necessary because we have no way to validate that the offered set	 * of operators and functions are consistent with the AM's expectations.	 * It would be nice to provide such a check someday, if it can be done	 * without solving the halting problem :-(	 */	if (!superuser())		ereport(ERROR,				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),				 errmsg("must be superuser to create an operator class")));	/* Look up the datatype */	typeoid = typenameTypeId(stmt->datatype);#ifdef NOT_USED	/* XXX this is unnecessary given the superuser check above */	/* Check we have ownership of the datatype */	if (!pg_type_ownercheck(typeoid, GetUserId()))		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,					   format_type_be(typeoid));#endif	operators = NIL;	procedures = NIL;	/* Storage datatype is optional */	storageoid = InvalidOid;	/*	 * Scan the "items" list to obtain additional info.	 */	foreach(l, stmt->items)	{		CreateOpClassItem *item = lfirst(l);		Oid			operOid;		Oid			funcOid;		OpClassMember *member;		AclResult	aclresult;		Assert(IsA(item, CreateOpClassItem));		switch (item->itemtype)		{			case OPCLASS_ITEM_OPERATOR:				if (item->number <= 0 || item->number > numOperators)					ereport(ERROR,							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),							 errmsg("invalid operator number %d,"									" must be between 1 and %d",									item->number, numOperators)));				if (item->args != NIL)				{					TypeName   *typeName1 = (TypeName *) linitial(item->args);					TypeName   *typeName2 = (TypeName *) lsecond(item->args);					operOid = LookupOperNameTypeNames(item->name,													  typeName1,													  typeName2,													  false);				}				else				{					/* Default to binary op on input datatype */					operOid = LookupOperName(item->name, typeoid, typeoid,											 false);				}				/* Caller must have execute permission on operators */				funcOid = get_opcode(operOid);				aclresult = pg_proc_aclcheck(funcOid, GetUserId(),											 ACL_EXECUTE);				if (aclresult != ACLCHECK_OK)					aclcheck_error(aclresult, ACL_KIND_PROC,								   get_func_name(funcOid));				/* Save the info */				member = (OpClassMember *) palloc0(sizeof(OpClassMember));				member->object = operOid;				member->number = item->number;				member->subtype = assignOperSubtype(amoid, typeoid, operOid);				member->recheck = item->recheck;				addClassMember(&operators, member, false);				break;			case OPCLASS_ITEM_FUNCTION:				if (item->number <= 0 || item->number > numProcs)					ereport(ERROR,							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),							 errmsg("invalid procedure number %d,"									" must be between 1 and %d",									item->number, numProcs)));				funcOid = LookupFuncNameTypeNames(item->name, item->args,												  false);				/* Caller must have execute permission on functions */				aclresult = pg_proc_aclcheck(funcOid, GetUserId(),											 ACL_EXECUTE);				if (aclresult != ACLCHECK_OK)					aclcheck_error(aclresult, ACL_KIND_PROC,								   get_func_name(funcOid));				/* Save the info */				member = (OpClassMember *) palloc0(sizeof(OpClassMember));				member->object = funcOid;				member->number = item->number;				member->subtype = assignProcSubtype(amoid, typeoid, funcOid);				addClassMember(&procedures, member, true);				break;			case OPCLASS_ITEM_STORAGETYPE:				if (OidIsValid(storageoid))					ereport(ERROR,							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						   errmsg("storage type specified more than once")));				storageoid = typenameTypeId(item->storedtype);				break;			default:				elog(ERROR, "unrecognized item type: %d", item->itemtype);				break;		}	}	/*	 * If storagetype is specified, make sure it's legal.	 */	if (OidIsValid(storageoid))	{		/* Just drop the spec if same as column datatype */		if (storageoid == typeoid)			storageoid = InvalidOid;		else		{			/*			 * Currently, only GiST allows storagetype different from			 * datatype.  This hardcoded test should be eliminated in favor of			 * adding another boolean column to pg_am ...			 */			if (amoid != GIST_AM_OID)				ereport(ERROR,						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),						 errmsg("storage type may not be different from data type for access method \"%s\"",								stmt->amname)));		}	}	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);	/*	 * Make sure there is no existing opclass of this name (this is just to	 * give a more friendly error message than "duplicate key").	 */	if (SearchSysCacheExists(CLAAMNAMENSP,							 ObjectIdGetDatum(amoid),							 CStringGetDatum(opcname),							 ObjectIdGetDatum(namespaceoid),							 0))		ereport(ERROR,				(errcode(ERRCODE_DUPLICATE_OBJECT),				 errmsg("operator class \"%s\" for access method \"%s\" already exists",						opcname, stmt->amname)));	/*	 * If we are creating a default opclass, check there isn't one already.	 * (Note we do not restrict this test to visible opclasses; this ensures	 * that typcache.c can find unique solutions to its questions.)	 */	if (stmt->isDefault)	{		ScanKeyData skey[1];		SysScanDesc scan;		ScanKeyInit(&skey[0],					Anum_pg_opclass_opcamid,					BTEqualStrategyNumber, F_OIDEQ,					ObjectIdGetDatum(amoid));		scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,								  SnapshotNow, 1, skey);		while (HeapTupleIsValid(tup = systable_getnext(scan)))		{			Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);			if (opclass->opcintype == typeoid && opclass->opcdefault)				ereport(ERROR,						(errcode(ERRCODE_DUPLICATE_OBJECT),						 errmsg("could not make operator class \"%s\" be default for type %s",								opcname,								TypeNameToString(stmt->datatype)),				   errdetail("Operator class \"%s\" already is the default.",							 NameStr(opclass->opcname))));		}		systable_endscan(scan);	}	/*	 * Okay, let's create the pg_opclass entry.	 */	for (i = 0; i < Natts_pg_opclass; ++i)	{		nulls[i] = ' ';		values[i] = (Datum) NULL;		/* redundant, but safe */	}	i = 0;	values[i++] = ObjectIdGetDatum(amoid);		/* opcamid */	namestrcpy(&opcName, opcname);	values[i++] = NameGetDatum(&opcName);		/* opcname */	values[i++] = ObjectIdGetDatum(namespaceoid);		/* opcnamespace */	values[i++] = ObjectIdGetDatum(GetUserId());		/* opcowner */	values[i++] = ObjectIdGetDatum(typeoid);	/* opcintype */	values[i++] = BoolGetDatum(stmt->isDefault);		/* opcdefault */	values[i++] = ObjectIdGetDatum(storageoid); /* opckeytype */	tup = heap_formtuple(rel->rd_att, values, nulls);	opclassoid = simple_heap_insert(rel, tup);	CatalogUpdateIndexes(rel, tup);	heap_freetuple(tup);	/*	 * Now add tuples to pg_amop and pg_amproc tying in the operators and	 * functions.	 */	storeOperators(opclassoid, operators);	storeProcedures(opclassoid, procedures);	/*	 * Create dependencies.  Note: we do not create a dependency link to the	 * AM, because we don't currently support DROP ACCESS METHOD.	 */	myself.classId = OperatorClassRelationId;	myself.objectId = opclassoid;	myself.objectSubId = 0;	/* dependency on namespace */	referenced.classId = NamespaceRelationId;	referenced.objectId = namespaceoid;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	/* dependency on indexed datatype */	referenced.classId = TypeRelationId;	referenced.objectId = typeoid;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	/* dependency on storage datatype */	if (OidIsValid(storageoid))	{		referenced.classId = TypeRelationId;		referenced.objectId = storageoid;		referenced.objectSubId = 0;		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	}	/* dependencies on operators */	foreach(l, operators)	{		OpClassMember *op = (OpClassMember *) lfirst(l);		referenced.classId = OperatorRelationId;		referenced.objectId = op->object;		referenced.objectSubId = 0;		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	}	/* dependencies on procedures */	foreach(l, procedures)	{		OpClassMember *proc = (OpClassMember *) lfirst(l);		referenced.classId = ProcedureRelationId;		referenced.objectId = proc->object;		referenced.objectSubId = 0;		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	}	/* dependency on owner */	recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());	heap_close(rel, RowExclusiveLock);}/* * Determine the subtype to assign to an operator, and do any validity * checking we can manage * * Currently this is done using hardwired rules; we don't let the user * specify it directly. */static OidassignOperSubtype(Oid amoid, Oid typeoid, Oid operOid){	Oid			subtype;	Operator	optup;	Form_pg_operator opform;	/* Subtypes are currently only supported by btree, others use 0 */	if (amoid != BTREE_AM_OID)		return InvalidOid;	optup = SearchSysCache(OPEROID,						   ObjectIdGetDatum(operOid),						   0, 0, 0);	if (optup == NULL)		elog(ERROR, "cache lookup failed for operator %u", operOid);	opform = (Form_pg_operator) GETSTRUCT(optup);	/*	 * btree operators must be binary ops returning boolean, and the left-side	 * input type must match the operator class' input type.	 */	if (opform->oprkind != 'b')		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),				 errmsg("btree operators must be binary")));	if (opform->oprresult != BOOLOID)		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),				 errmsg("btree operators must return boolean")));	if (opform->oprleft != typeoid)		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),			  errmsg("btree operators must have index type as left input")));	/*	 * The subtype is "default" (0) if oprright matches the operator class,	 * otherwise it is oprright.	 */	if (opform->oprright == typeoid)		subtype = InvalidOid;	else		subtype = opform->oprright;	ReleaseSysCache(optup);	return subtype;}/* * Determine the subtype to assign to a support procedure, and do any validity * checking we can manage * * Currently this is done using hardwired rules; we don't let the user * specify it directly. */static OidassignProcSubtype(Oid amoid, Oid typeoid, Oid procOid){	Oid			subtype;	HeapTuple	proctup;	Form_pg_proc procform;	/* Subtypes are currently only supported by btree, others use 0 */	if (amoid != BTREE_AM_OID)		return InvalidOid;	proctup = SearchSysCache(PROCOID,							 ObjectIdGetDatum(procOid),							 0, 0, 0);	if (proctup == NULL)		elog(ERROR, "cache lookup failed for function %u", procOid);	procform = (Form_pg_proc) GETSTRUCT(proctup);	/*	 * btree support procs must be 2-arg procs returning int4, and the first	 * input type must match the operator class' input type.	 */	if (procform->pronargs != 2)		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),				 errmsg("btree procedures must have two arguments")));	if (procform->prorettype != INT4OID)		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),				 errmsg("btree procedures must return integer")));	if (procform->proargtypes.values[0] != typeoid)		ereport(ERROR,				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),			errmsg("btree procedures must have index type as first input")));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -