Skip to content
Snippets Groups Projects
Select Git revision
  • benchmark-tools
  • postgres-lambda
  • master default
  • REL9_4_25
  • REL9_5_20
  • REL9_6_16
  • REL_10_11
  • REL_11_6
  • REL_12_1
  • REL_12_0
  • REL_12_RC1
  • REL_12_BETA4
  • REL9_4_24
  • REL9_5_19
  • REL9_6_15
  • REL_10_10
  • REL_11_5
  • REL_12_BETA3
  • REL9_4_23
  • REL9_5_18
  • REL9_6_14
  • REL_10_9
  • REL_11_4
23 results

opclasscmds.c

Blame
  • opclasscmds.c 26.70 KiB
    /*-------------------------------------------------------------------------
     *
     * 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.36 2005/08/22 17:38:20 tgl 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.
     */
    void
    DefineOpClass(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 Oid
    assignOperSubtype(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 Oid
    assignProcSubtype(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")));
    
    	/*
    	 * The subtype is "default" (0) if second input type matches the
    	 * operator class, otherwise it is the second input type.
    	 */
    	if (procform->proargtypes.values[1] == typeoid)
    		subtype = InvalidOid;
    	else
    		subtype = procform->proargtypes.values[1];
    	ReleaseSysCache(proctup);
    	return subtype;
    }
    
    /*
     * Add a new class member to the appropriate list, after checking for
     * duplicated strategy or proc number.
     */
    static void
    addClassMember(List **list, OpClassMember *member, bool isProc)
    {
    	ListCell   *l;
    
    	foreach(l, *list)
    	{
    		OpClassMember *old = (OpClassMember *) lfirst(l);
    
    		if (old->number == member->number &&
    			old->subtype == member->subtype)
    		{
    			if (isProc)
    				ereport(ERROR,
    						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    					 errmsg("procedure number %d appears more than once",
    							member->number)));
    			else
    				ereport(ERROR,
    						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    					  errmsg("operator number %d appears more than once",
    							 member->number)));
    		}
    	}
    	*list = lappend(*list, member);
    }
    
    /*
     * Dump the operators to pg_amop
     */
    static void
    storeOperators(Oid opclassoid, List *operators)
    {
    	Relation	rel;
    	Datum		values[Natts_pg_amop];
    	char		nulls[Natts_pg_amop];
    	HeapTuple	tup;
    	ListCell   *l;
    	int			i;
    
    	rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
    
    	foreach(l, operators)
    	{
    		OpClassMember *op = (OpClassMember *) lfirst(l);
    
    		for (i = 0; i < Natts_pg_amop; ++i)
    		{
    			nulls[i] = ' ';
    			values[i] = (Datum) NULL;
    		}
    
    		i = 0;
    		values[i++] = ObjectIdGetDatum(opclassoid);		/* amopclaid */
    		values[i++] = ObjectIdGetDatum(op->subtype);	/* amopsubtype */
    		values[i++] = Int16GetDatum(op->number);		/* amopstrategy */
    		values[i++] = BoolGetDatum(op->recheck);		/* amopreqcheck */
    		values[i++] = ObjectIdGetDatum(op->object);		/* amopopr */
    
    		tup = heap_formtuple(rel->rd_att, values, nulls);
    
    		simple_heap_insert(rel, tup);
    
    		CatalogUpdateIndexes(rel, tup);
    
    		heap_freetuple(tup);
    	}
    
    	heap_close(rel, RowExclusiveLock);
    }
    
    /*
     * Dump the procedures (support routines) to pg_amproc
     */
    static void
    storeProcedures(Oid opclassoid, List *procedures)
    {
    	Relation	rel;
    	Datum		values[Natts_pg_amproc];
    	char		nulls[Natts_pg_amproc];
    	HeapTuple	tup;
    	ListCell   *l;
    	int			i;
    
    	rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
    
    	foreach(l, procedures)
    	{
    		OpClassMember *proc = (OpClassMember *) lfirst(l);
    
    		for (i = 0; i < Natts_pg_amproc; ++i)
    		{
    			nulls[i] = ' ';
    			values[i] = (Datum) NULL;
    		}
    
    		i = 0;
    		values[i++] = ObjectIdGetDatum(opclassoid);		/* amopclaid */
    		values[i++] = ObjectIdGetDatum(proc->subtype);	/* amprocsubtype */
    		values[i++] = Int16GetDatum(proc->number);		/* amprocnum */
    		values[i++] = ObjectIdGetDatum(proc->object);	/* amproc */
    
    		tup = heap_formtuple(rel->rd_att, values, nulls);
    
    		simple_heap_insert(rel, tup);
    
    		CatalogUpdateIndexes(rel, tup);
    
    		heap_freetuple(tup);
    	}
    
    	heap_close(rel, RowExclusiveLock);
    }
    
    
    /*
     * RemoveOpClass
     *		Deletes an opclass.
     */
    void
    RemoveOpClass(RemoveOpClassStmt *stmt)
    {
    	Oid			amID,
    				opcID;
    	char	   *schemaname;
    	char	   *opcname;
    	HeapTuple	tuple;
    	ObjectAddress object;
    
    	/*
    	 * Get the access method's OID.
    	 */
    	amID = GetSysCacheOid(AMNAME,
    						  CStringGetDatum(stmt->amname),
    						  0, 0, 0);
    	if (!OidIsValid(amID))
    		ereport(ERROR,
    				(errcode(ERRCODE_UNDEFINED_OBJECT),
    				 errmsg("access method \"%s\" does not exist",
    						stmt->amname)));
    
    	/*
    	 * Look up the opclass.
    	 */
    
    	/* deconstruct the name list */
    	DeconstructQualifiedName(stmt->opclassname, &schemaname, &opcname);
    
    	if (schemaname)
    	{
    		/* Look in specific schema only */
    		Oid			namespaceId;
    
    		namespaceId = LookupExplicitNamespace(schemaname);
    		tuple = SearchSysCache(CLAAMNAMENSP,
    							   ObjectIdGetDatum(amID),
    							   PointerGetDatum(opcname),
    							   ObjectIdGetDatum(namespaceId),
    							   0);
    	}
    	else
    	{
    		/* Unqualified opclass name, so search the search path */
    		opcID = OpclassnameGetOpcid(amID, opcname);
    		if (!OidIsValid(opcID))
    			ereport(ERROR,
    					(errcode(ERRCODE_UNDEFINED_OBJECT),
    					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
    							opcname, stmt->amname)));
    		tuple = SearchSysCache(CLAOID,
    							   ObjectIdGetDatum(opcID),
    							   0, 0, 0);
    	}
    
    	if (!HeapTupleIsValid(tuple))
    		ereport(ERROR,
    				(errcode(ERRCODE_UNDEFINED_OBJECT),
    				 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
    					NameListToString(stmt->opclassname), stmt->amname)));
    
    	opcID = HeapTupleGetOid(tuple);
    
    	/* Permission check: must own opclass or its namespace */
    	if (!pg_opclass_ownercheck(opcID, GetUserId()) &&
    		!pg_namespace_ownercheck(((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace,
    								 GetUserId()))
    		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
    					   NameListToString(stmt->opclassname));
    
    	ReleaseSysCache(tuple);
    
    	/*
    	 * Do the deletion
    	 */
    	object.classId = OperatorClassRelationId;
    	object.objectId = opcID;
    	object.objectSubId = 0;
    
    	performDeletion(&object, stmt->behavior);
    }
    
    /*
     * Guts of opclass deletion.
     */
    void
    RemoveOpClassById(Oid opclassOid)
    {
    	Relation	rel;
    	HeapTuple	tup;
    	ScanKeyData skey[1];
    	SysScanDesc scan;
    
    	/*
    	 * First remove the pg_opclass entry itself.
    	 */
    	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
    
    	tup = SearchSysCache(CLAOID,
    						 ObjectIdGetDatum(opclassOid),
    						 0, 0, 0);
    	if (!HeapTupleIsValid(tup)) /* should not happen */
    		elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
    
    	simple_heap_delete(rel, &tup->t_self);
    
    	ReleaseSysCache(tup);
    
    	heap_close(rel, RowExclusiveLock);
    
    	/*
    	 * Remove associated entries in pg_amop.
    	 */
    	ScanKeyInit(&skey[0],
    				Anum_pg_amop_amopclaid,
    				BTEqualStrategyNumber, F_OIDEQ,
    				ObjectIdGetDatum(opclassOid));
    
    	rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
    
    	scan = systable_beginscan(rel, AccessMethodStrategyIndexId, true,
    							  SnapshotNow, 1, skey);
    
    	while (HeapTupleIsValid(tup = systable_getnext(scan)))
    		simple_heap_delete(rel, &tup->t_self);
    
    	systable_endscan(scan);
    	heap_close(rel, RowExclusiveLock);
    
    	/*
    	 * Remove associated entries in pg_amproc.
    	 */
    	ScanKeyInit(&skey[0],
    				Anum_pg_amproc_amopclaid,
    				BTEqualStrategyNumber, F_OIDEQ,
    				ObjectIdGetDatum(opclassOid));
    
    	rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
    
    	scan = systable_beginscan(rel, AccessMethodProcedureIndexId, true,
    							  SnapshotNow, 1, skey);
    
    	while (HeapTupleIsValid(tup = systable_getnext(scan)))
    		simple_heap_delete(rel, &tup->t_self);
    
    	systable_endscan(scan);
    	heap_close(rel, RowExclusiveLock);
    }
    
    
    /*
     * Rename opclass
     */
    void
    RenameOpClass(List *name, const char *access_method, const char *newname)
    {
    	Oid			opcOid;
    	Oid			amOid;
    	Oid			namespaceOid;
    	char	   *schemaname;
    	char	   *opcname;
    	HeapTuple	tup;
    	Relation	rel;
    	AclResult	aclresult;
    
    	amOid = GetSysCacheOid(AMNAME,
    						   CStringGetDatum(access_method),
    						   0, 0, 0);
    	if (!OidIsValid(amOid))
    		ereport(ERROR,
    				(errcode(ERRCODE_UNDEFINED_OBJECT),
    				 errmsg("access method \"%s\" does not exist",
    						access_method)));
    
    	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
    
    	/*
    	 * Look up the opclass
    	 */
    	DeconstructQualifiedName(name, &schemaname, &opcname);
    
    	if (schemaname)
    	{
    		namespaceOid = LookupExplicitNamespace(schemaname);
    
    		tup = SearchSysCacheCopy(CLAAMNAMENSP,
    								 ObjectIdGetDatum(amOid),
    								 PointerGetDatum(opcname),
    								 ObjectIdGetDatum(namespaceOid),
    								 0);
    		if (!HeapTupleIsValid(tup))
    			ereport(ERROR,
    					(errcode(ERRCODE_UNDEFINED_OBJECT),
    					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
    							opcname, access_method)));
    
    		opcOid = HeapTupleGetOid(tup);
    	}
    	else
    	{
    		opcOid = OpclassnameGetOpcid(amOid, opcname);
    		if (!OidIsValid(opcOid))
    			ereport(ERROR,
    					(errcode(ERRCODE_UNDEFINED_OBJECT),
    					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
    							opcname, access_method)));
    
    		tup = SearchSysCacheCopy(CLAOID,
    								 ObjectIdGetDatum(opcOid),
    								 0, 0, 0);
    		if (!HeapTupleIsValid(tup))		/* should not happen */
    			elog(ERROR, "cache lookup failed for opclass %u", opcOid);
    
    		namespaceOid = ((Form_pg_opclass) GETSTRUCT(tup))->opcnamespace;
    	}
    
    	/* make sure the new name doesn't exist */
    	if (SearchSysCacheExists(CLAAMNAMENSP,
    							 ObjectIdGetDatum(amOid),
    							 CStringGetDatum(newname),
    							 ObjectIdGetDatum(namespaceOid),
    							 0))
    	{
    		ereport(ERROR,
    				(errcode(ERRCODE_DUPLICATE_OBJECT),
    				 errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
    						newname, access_method,
    						get_namespace_name(namespaceOid))));
    	}
    
    	/* must be owner */
    	if (!pg_opclass_ownercheck(opcOid, GetUserId()))
    		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
    					   NameListToString(name));
    
    	/* must have CREATE privilege on namespace */
    	aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
    	if (aclresult != ACLCHECK_OK)
    		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
    					   get_namespace_name(namespaceOid));
    
    	/* rename */
    	namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname);
    	simple_heap_update(rel, &tup->t_self, tup);
    	CatalogUpdateIndexes(rel, tup);
    
    	heap_close(rel, NoLock);
    	heap_freetuple(tup);
    }
    
    /*
     * Change opclass owner
     */
    void
    AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
    {
    	Oid			opcOid;
    	Oid			amOid;
    	Oid			namespaceOid;
    	char	   *schemaname;
    	char	   *opcname;
    	HeapTuple	tup;
    	Relation	rel;
    	AclResult	aclresult;
    	Form_pg_opclass opcForm;
    
    	amOid = GetSysCacheOid(AMNAME,
    						   CStringGetDatum(access_method),
    						   0, 0, 0);
    	if (!OidIsValid(amOid))
    		ereport(ERROR,
    				(errcode(ERRCODE_UNDEFINED_OBJECT),
    				 errmsg("access method \"%s\" does not exist",
    						access_method)));
    
    	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
    
    	/*
    	 * Look up the opclass
    	 */
    	DeconstructQualifiedName(name, &schemaname, &opcname);
    
    	if (schemaname)
    	{
    		namespaceOid = LookupExplicitNamespace(schemaname);
    
    		tup = SearchSysCacheCopy(CLAAMNAMENSP,
    								 ObjectIdGetDatum(amOid),
    								 PointerGetDatum(opcname),
    								 ObjectIdGetDatum(namespaceOid),
    								 0);
    		if (!HeapTupleIsValid(tup))
    			ereport(ERROR,
    					(errcode(ERRCODE_UNDEFINED_OBJECT),
    					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
    							opcname, access_method)));
    
    	}
    	else
    	{
    		opcOid = OpclassnameGetOpcid(amOid, opcname);
    		if (!OidIsValid(opcOid))
    			ereport(ERROR,
    					(errcode(ERRCODE_UNDEFINED_OBJECT),
    					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
    							opcname, access_method)));
    
    		tup = SearchSysCacheCopy(CLAOID,
    								 ObjectIdGetDatum(opcOid),
    								 0, 0, 0);
    		if (!HeapTupleIsValid(tup))		/* should not happen */
    			elog(ERROR, "cache lookup failed for opclass %u", opcOid);
    		namespaceOid = ((Form_pg_opclass) GETSTRUCT(tup))->opcnamespace;
    	}
    	opcForm = (Form_pg_opclass) GETSTRUCT(tup);
    
    	/*
    	 * If the new owner is the same as the existing owner, consider the
    	 * command to have succeeded.  This is for dump restoration purposes.
    	 */
    	if (opcForm->opcowner != newOwnerId)
    	{
    		/* Superusers can always do it */
    		if (!superuser())
    		{
    			/* Otherwise, must be owner of the existing object */
    			if (!pg_opclass_ownercheck(HeapTupleGetOid(tup),GetUserId()))
    				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
    							   NameListToString(name));
    
    			/* Must be able to become new owner */
    			check_is_member_of_role(GetUserId(), newOwnerId);
    
    			/* New owner must have CREATE privilege on namespace */
    			aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
    											  ACL_CREATE);
    			if (aclresult != ACLCHECK_OK)
    				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
    							   get_namespace_name(namespaceOid));
    		}
    
    		/*
    		 * Modify the owner --- okay to scribble on tup because it's a
    		 * copy
    		 */
    		opcForm->opcowner = newOwnerId;
    
    		simple_heap_update(rel, &tup->t_self, tup);
    
    		CatalogUpdateIndexes(rel, tup);
    
    		/* Update owner dependency reference */
    		changeDependencyOnOwner(OperatorClassRelationId, amOid, newOwnerId);
    	}
    
    	heap_close(rel, NoLock);
    	heap_freetuple(tup);
    }