Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions  Download package

opclasscmds.c
/*-------------------------------------------------------------------------
 *
 * opclasscmds.c
 *
 *      Routines for opclass (and opfamily) manipulation commands
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.64.2.1 2010/07/03 13:53:26 rhaas Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <limits.h>

#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/indexing.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_opfamily.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/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"


/*
 * We use lists of this struct type to keep track of both operators and
 * procedures while building or adding to an opfamily.
 */
00051 typedef struct
{
      Oid               object;                 /* operator or support proc's OID */
      int               number;                 /* strategy or support proc number */
      Oid               lefttype;         /* lefttype */
      Oid               righttype;        /* righttype */
} OpFamilyMember;


static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                         int maxOpNumber, int maxProcNumber,
                         List *items);
static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                          int maxOpNumber, int maxProcNumber,
                          List *items);
static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
static void storeOperators(List *opfamilyname, Oid amoid,
                     Oid opfamilyoid, Oid opclassoid,
                     List *operators, bool isAdd);
static void storeProcedures(List *opfamilyname, Oid amoid,
                        Oid opfamilyoid, Oid opclassoid,
                        List *procedures, bool isAdd);
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                    List *operators);
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                     List *procedures);
static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
                                       Oid newOwnerId);
static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple,
                                          Oid newOwnerId);


/*
 * OpFamilyCacheLookup
 *          Look up an existing opfamily by name.
 *
 * Returns a syscache tuple reference, or NULL if not found.
 */
static HeapTuple
OpFamilyCacheLookup(Oid amID, List *opfamilyname)
{
      char     *schemaname;
      char     *opfname;

      /* deconstruct the name list */
      DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);

      if (schemaname)
      {
            /* Look in specific schema only */
            Oid               namespaceId;

            namespaceId = LookupExplicitNamespace(schemaname);
            return SearchSysCache(OPFAMILYAMNAMENSP,
                                            ObjectIdGetDatum(amID),
                                            PointerGetDatum(opfname),
                                            ObjectIdGetDatum(namespaceId),
                                            0);
      }
      else
      {
            /* Unqualified opfamily name, so search the search path */
            Oid               opfID = OpfamilynameGetOpfid(amID, opfname);

            if (!OidIsValid(opfID))
                  return NULL;
            return SearchSysCache(OPFAMILYOID,
                                            ObjectIdGetDatum(opfID),
                                            0, 0, 0);
      }
}

/*
 * OpClassCacheLookup
 *          Look up an existing opclass by name.
 *
 * Returns a syscache tuple reference, or NULL if not found.
 */
static HeapTuple
OpClassCacheLookup(Oid amID, List *opclassname)
{
      char     *schemaname;
      char     *opcname;

      /* deconstruct the name list */
      DeconstructQualifiedName(opclassname, &schemaname, &opcname);

      if (schemaname)
      {
            /* Look in specific schema only */
            Oid               namespaceId;

            namespaceId = LookupExplicitNamespace(schemaname);
            return SearchSysCache(CLAAMNAMENSP,
                                            ObjectIdGetDatum(amID),
                                            PointerGetDatum(opcname),
                                            ObjectIdGetDatum(namespaceId),
                                            0);
      }
      else
      {
            /* Unqualified opclass name, so search the search path */
            Oid               opcID = OpclassnameGetOpcid(amID, opcname);

            if (!OidIsValid(opcID))
                  return NULL;
            return SearchSysCache(CLAOID,
                                            ObjectIdGetDatum(opcID),
                                            0, 0, 0);
      }
}

/*
 * CreateOpFamily
 *          Internal routine to make the catalog entry for a new operator family.
 *
 * Caller must have done permissions checks etc. already.
 */
static Oid
CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
{
      Oid               opfamilyoid;
      Relation    rel;
      HeapTuple   tup;
      Datum       values[Natts_pg_opfamily];
      bool        nulls[Natts_pg_opfamily];
      NameData    opfName;
      ObjectAddress myself,
                        referenced;

      rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);

      /*
       * Make sure there is no existing opfamily of this name (this is just to
       * give a more friendly error message than "duplicate key").
       */
      if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
                                           ObjectIdGetDatum(amoid),
                                           CStringGetDatum(opfname),
                                           ObjectIdGetDatum(namespaceoid),
                                           0))
            ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_OBJECT),
                         errmsg("operator family \"%s\" for access method \"%s\" already exists",
                                    opfname, amname)));

      /*
       * Okay, let's create the pg_opfamily entry.
       */
      memset(values, 0, sizeof(values));
      memset(nulls, false, sizeof(nulls));

      values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
      namestrcpy(&opfName, opfname);
      values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
      values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
      values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());

      tup = heap_form_tuple(rel->rd_att, values, nulls);

      opfamilyoid = simple_heap_insert(rel, tup);

      CatalogUpdateIndexes(rel, tup);

      heap_freetuple(tup);

      /*
       * Create dependencies for the opfamily proper.  Note: we do not create a
       * dependency link to the AM, because we don't currently support DROP
       * ACCESS METHOD.
       */
      myself.classId = OperatorFamilyRelationId;
      myself.objectId = opfamilyoid;
      myself.objectSubId = 0;

      /* dependency on namespace */
      referenced.classId = NamespaceRelationId;
      referenced.objectId = namespaceoid;
      referenced.objectSubId = 0;
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

      /* dependency on owner */
      recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());

      heap_close(rel, RowExclusiveLock);

      return opfamilyoid;
}

/*
 * 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 */
                        opfamilyoid,      /* oid of containing opfamily */
                        opclassoid;       /* oid of opclass we create */
      int               maxOpNumber,      /* amstrategies value */
                        maxProcNumber;    /* amsupport value */
      bool        amstorage;        /* amstorage flag */
      List     *operators;          /* OpFamilyMember list for operators */
      List     *procedures;         /* OpFamilyMember list for support procs */
      ListCell   *l;
      Relation    rel;
      HeapTuple   tup;
      Form_pg_am  pg_am;
      Datum       values[Natts_pg_opclass];
      bool        nulls[Natts_pg_opclass];
      AclResult   aclresult;
      NameData    opcName;
      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);
      pg_am = (Form_pg_am) GETSTRUCT(tup);
      maxOpNumber = pg_am->amstrategies;
      /* if amstrategies is zero, just enforce that op numbers fit in int16 */
      if (maxOpNumber <= 0)
            maxOpNumber = SHRT_MAX;
      maxProcNumber = pg_am->amsupport;
      amstorage = pg_am->amstorage;

      /* XXX Should we make any privilege check against the AM? */

      ReleaseSysCache(tup);

      /*
       * The question of appropriate permissions for CREATE OPERATOR CLASS is
       * interesting.  Creating an opclass is tantamount to granting public
       * execute access on the functions involved, since the index machinery
       * generally does not check access permission before using the functions.
       * A minimum expectation therefore is that the caller have execute
       * privilege with grant option.  Since we don't have a way to make the
       * opclass go away if the grant option is revoked, we choose instead to
       * require ownership of the functions.    It's also not entirely clear what
       * permissions should be required on the datatype, but ownership seems
       * like a safe choice.
       *
       * 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 :-(
       *
       * XXX re-enable NOT_USED code sections below if you remove this test.
       */
      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("must be superuser to create an operator class")));

      /* Look up the datatype */
      typeoid = typenameTypeId(NULL, stmt->datatype, NULL);

#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

      /*
       * Look up the containing operator family, or create one if FAMILY option
       * was omitted and there's not a match already.
       */
      if (stmt->opfamilyname)
      {
            tup = OpFamilyCacheLookup(amoid, stmt->opfamilyname);
            if (!HeapTupleIsValid(tup))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                 NameListToString(stmt->opfamilyname), stmt->amname)));
            opfamilyoid = HeapTupleGetOid(tup);

            /*
             * XXX given the superuser check above, there's no need for an
             * ownership check here
             */
            ReleaseSysCache(tup);
      }
      else
      {
            /* Lookup existing family of same name and namespace */
            tup = SearchSysCache(OPFAMILYAMNAMENSP,
                                           ObjectIdGetDatum(amoid),
                                           PointerGetDatum(opcname),
                                           ObjectIdGetDatum(namespaceoid),
                                           0);
            if (HeapTupleIsValid(tup))
            {
                  opfamilyoid = HeapTupleGetOid(tup);

                  /*
                   * XXX given the superuser check above, there's no need for an
                   * ownership check here
                   */
                  ReleaseSysCache(tup);
            }
            else
            {
                  /*
                   * Create it ... again no need for more permissions ...
                   */
                  opfamilyoid = CreateOpFamily(stmt->amname, opcname,
                                                             namespaceoid, amoid);
            }
      }

      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;
            OpFamilyMember *member;

            Assert(IsA(item, CreateOpClassItem));
            switch (item->itemtype)
            {
                  case OPCLASS_ITEM_OPERATOR:
                        if (item->number <= 0 || item->number > maxOpNumber)
                              ereport(ERROR,
                                          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                           errmsg("invalid operator number %d,"
                                                      " must be between 1 and %d",
                                                      item->number, maxOpNumber)));
                        if (item->args != NIL)
                        {
                              TypeName   *typeName1 = (TypeName *) linitial(item->args);
                              TypeName   *typeName2 = (TypeName *) lsecond(item->args);

                              operOid = LookupOperNameTypeNames(NULL, item->name,
                                                                                typeName1, typeName2,
                                                                                false, -1);
                        }
                        else
                        {
                              /* Default to binary op on input datatype */
                              operOid = LookupOperName(NULL, item->name,
                                                                   typeoid, typeoid,
                                                                   false, -1);
                        }

#ifdef NOT_USED
                        /* XXX this is unnecessary given the superuser check above */
                        /* Caller must own operator and its underlying function */
                        if (!pg_oper_ownercheck(operOid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
                                                   get_opname(operOid));
                        funcOid = get_opcode(operOid);
                        if (!pg_proc_ownercheck(funcOid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
                                                   get_func_name(funcOid));
#endif

                        /* Save the info */
                        member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
                        member->object = operOid;
                        member->number = item->number;
                        assignOperTypes(member, amoid, typeoid);
                        addFamilyMember(&operators, member, false);
                        break;
                  case OPCLASS_ITEM_FUNCTION:
                        if (item->number <= 0 || item->number > maxProcNumber)
                              ereport(ERROR,
                                          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                           errmsg("invalid procedure number %d,"
                                                      " must be between 1 and %d",
                                                      item->number, maxProcNumber)));
                        funcOid = LookupFuncNameTypeNames(item->name, item->args,
                                                                          false);
#ifdef NOT_USED
                        /* XXX this is unnecessary given the superuser check above */
                        /* Caller must own function */
                        if (!pg_proc_ownercheck(funcOid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
                                                   get_func_name(funcOid));
#endif

                        /* Save the info */
                        member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
                        member->object = funcOid;
                        member->number = item->number;

                        /* allow overriding of the function's actual arg types */
                        if (item->class_args)
                              processTypesSpec(item->class_args,
                                                       &member->lefttype, &member->righttype);

                        assignProcTypes(member, amoid, typeoid);
                        addFamilyMember(&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(NULL, item->storedtype, NULL);

#ifdef NOT_USED
                        /* XXX this is unnecessary given the superuser check above */
                        /* Check we have ownership of the datatype */
                        if (!pg_type_ownercheck(storageoid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
                                                   format_type_be(storageoid));
#endif
                        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 if (!amstorage)
                  ereport(ERROR,
                              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                               errmsg("storage type cannot 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_opcmethod,
                              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.
       */
      memset(values, 0, sizeof(values));
      memset(nulls, false, sizeof(nulls));

      values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
      namestrcpy(&opcName, opcname);
      values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
      values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
      values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
      values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
      values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
      values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(stmt->isDefault);
      values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);

      tup = heap_form_tuple(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.  Dependencies on them are inserted, too.
       */
      storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
                           opclassoid, operators, false);
      storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
                              opclassoid, procedures, false);

      /*
       * Create dependencies for the opclass proper.  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 opfamily */
      referenced.classId = OperatorFamilyRelationId;
      referenced.objectId = opfamilyoid;
      referenced.objectSubId = 0;
      recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);

      /* 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);
      }

      /* dependency on owner */
      recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());

      heap_close(rel, RowExclusiveLock);
}


/*
 * DefineOpFamily
 *          Define a new index operator family.
 */
void
DefineOpFamily(CreateOpFamilyStmt *stmt)
{
      char     *opfname;            /* name of opfamily we're creating */
      Oid               amoid,                  /* our AM's oid */
                        namespaceoid,     /* namespace to create opfamily in */
                        opfamilyoid;      /* oid of opfamily we create */
      Relation    rel;
      HeapTuple   tup;
      Datum       values[Natts_pg_opfamily];
      bool        nulls[Natts_pg_opfamily];
      AclResult   aclresult;
      NameData    opfName;
      ObjectAddress myself,
                        referenced;

      /* Convert list of names to a name and namespace */
      namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
                                                                               &opfname);

      /* 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);

      /* XXX Should we make any privilege check against the AM? */

      ReleaseSysCache(tup);

      /*
       * Currently, we require superuser privileges to create an opfamily. See
       * comments in DefineOpClass.
       *
       * XXX re-enable NOT_USED code sections below if you remove this test.
       */
      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("must be superuser to create an operator family")));

      rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);

      /*
       * Make sure there is no existing opfamily of this name (this is just to
       * give a more friendly error message than "duplicate key").
       */
      if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
                                           ObjectIdGetDatum(amoid),
                                           CStringGetDatum(opfname),
                                           ObjectIdGetDatum(namespaceoid),
                                           0))
            ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_OBJECT),
                         errmsg("operator family \"%s\" for access method \"%s\" already exists",
                                    opfname, stmt->amname)));

      /*
       * Okay, let's create the pg_opfamily entry.
       */
      memset(values, 0, sizeof(values));
      memset(nulls, false, sizeof(nulls));

      values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
      namestrcpy(&opfName, opfname);
      values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
      values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
      values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());

      tup = heap_form_tuple(rel->rd_att, values, nulls);

      opfamilyoid = simple_heap_insert(rel, tup);

      CatalogUpdateIndexes(rel, tup);

      heap_freetuple(tup);

      /*
       * Create dependencies for the opfamily proper.  Note: we do not create a
       * dependency link to the AM, because we don't currently support DROP
       * ACCESS METHOD.
       */
      myself.classId = OperatorFamilyRelationId;
      myself.objectId = opfamilyoid;
      myself.objectSubId = 0;

      /* dependency on namespace */
      referenced.classId = NamespaceRelationId;
      referenced.objectId = namespaceoid;
      referenced.objectSubId = 0;
      recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

      /* dependency on owner */
      recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());

      heap_close(rel, RowExclusiveLock);
}


/*
 * AlterOpFamily
 *          Add or remove operators/procedures within an existing operator family.
 *
 * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP.  Some
 * other commands called ALTER OPERATOR FAMILY exist, but go through
 * different code paths.
 */
void
AlterOpFamily(AlterOpFamilyStmt *stmt)
{
      Oid               amoid,                  /* our AM's oid */
                        opfamilyoid;      /* oid of opfamily */
      int               maxOpNumber,      /* amstrategies value */
                        maxProcNumber;    /* amsupport value */
      HeapTuple   tup;
      Form_pg_am  pg_am;

      /* 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);
      pg_am = (Form_pg_am) GETSTRUCT(tup);
      maxOpNumber = pg_am->amstrategies;
      /* if amstrategies is zero, just enforce that op numbers fit in int16 */
      if (maxOpNumber <= 0)
            maxOpNumber = SHRT_MAX;
      maxProcNumber = pg_am->amsupport;

      /* XXX Should we make any privilege check against the AM? */

      ReleaseSysCache(tup);

      /* Look up the opfamily */
      tup = OpFamilyCacheLookup(amoid, stmt->opfamilyname);
      if (!HeapTupleIsValid(tup))
            ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                         errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                    NameListToString(stmt->opfamilyname), stmt->amname)));
      opfamilyoid = HeapTupleGetOid(tup);
      ReleaseSysCache(tup);

      /*
       * Currently, we require superuser privileges to alter an opfamily.
       *
       * XXX re-enable NOT_USED code sections below if you remove this test.
       */
      if (!superuser())
            ereport(ERROR,
                        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                         errmsg("must be superuser to alter an operator family")));

      /*
       * ADD and DROP cases need separate code from here on down.
       */
      if (stmt->isDrop)
            AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
                                      maxOpNumber, maxProcNumber,
                                      stmt->items);
      else
            AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
                                     maxOpNumber, maxProcNumber,
                                     stmt->items);
}

/*
 * ADD part of ALTER OP FAMILY
 */
static void
AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                         int maxOpNumber, int maxProcNumber,
                         List *items)
{
      List     *operators;          /* OpFamilyMember list for operators */
      List     *procedures;         /* OpFamilyMember list for support procs */
      ListCell   *l;

      operators = NIL;
      procedures = NIL;

      /*
       * Scan the "items" list to obtain additional info.
       */
      foreach(l, items)
      {
            CreateOpClassItem *item = lfirst(l);
            Oid               operOid;
            Oid               funcOid;
            OpFamilyMember *member;

            Assert(IsA(item, CreateOpClassItem));
            switch (item->itemtype)
            {
                  case OPCLASS_ITEM_OPERATOR:
                        if (item->number <= 0 || item->number > maxOpNumber)
                              ereport(ERROR,
                                          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                           errmsg("invalid operator number %d,"
                                                      " must be between 1 and %d",
                                                      item->number, maxOpNumber)));
                        if (item->args != NIL)
                        {
                              TypeName   *typeName1 = (TypeName *) linitial(item->args);
                              TypeName   *typeName2 = (TypeName *) lsecond(item->args);

                              operOid = LookupOperNameTypeNames(NULL, item->name,
                                                                                typeName1, typeName2,
                                                                                false, -1);
                        }
                        else
                        {
                              ereport(ERROR,
                                          (errcode(ERRCODE_SYNTAX_ERROR),
                                           errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
                              operOid = InvalidOid;         /* keep compiler quiet */
                        }

#ifdef NOT_USED
                        /* XXX this is unnecessary given the superuser check above */
                        /* Caller must own operator and its underlying function */
                        if (!pg_oper_ownercheck(operOid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
                                                   get_opname(operOid));
                        funcOid = get_opcode(operOid);
                        if (!pg_proc_ownercheck(funcOid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
                                                   get_func_name(funcOid));
#endif

                        /* Save the info */
                        member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
                        member->object = operOid;
                        member->number = item->number;
                        assignOperTypes(member, amoid, InvalidOid);
                        addFamilyMember(&operators, member, false);
                        break;
                  case OPCLASS_ITEM_FUNCTION:
                        if (item->number <= 0 || item->number > maxProcNumber)
                              ereport(ERROR,
                                          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                           errmsg("invalid procedure number %d,"
                                                      " must be between 1 and %d",
                                                      item->number, maxProcNumber)));
                        funcOid = LookupFuncNameTypeNames(item->name, item->args,
                                                                          false);
#ifdef NOT_USED
                        /* XXX this is unnecessary given the superuser check above */
                        /* Caller must own function */
                        if (!pg_proc_ownercheck(funcOid, GetUserId()))
                              aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
                                                   get_func_name(funcOid));
#endif

                        /* Save the info */
                        member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
                        member->object = funcOid;
                        member->number = item->number;

                        /* allow overriding of the function's actual arg types */
                        if (item->class_args)
                              processTypesSpec(item->class_args,
                                                       &member->lefttype, &member->righttype);

                        assignProcTypes(member, amoid, InvalidOid);
                        addFamilyMember(&procedures, member, true);
                        break;
                  case OPCLASS_ITEM_STORAGETYPE:
                        ereport(ERROR,
                                    (errcode(ERRCODE_SYNTAX_ERROR),
                                     errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
                        break;
                  default:
                        elog(ERROR, "unrecognized item type: %d", item->itemtype);
                        break;
            }
      }

      /*
       * Add tuples to pg_amop and pg_amproc tying in the operators and
       * functions.  Dependencies on them are inserted, too.
       */
      storeOperators(opfamilyname, amoid, opfamilyoid,
                           InvalidOid, operators, true);
      storeProcedures(opfamilyname, amoid, opfamilyoid,
                              InvalidOid, procedures, true);
}

/*
 * DROP part of ALTER OP FAMILY
 */
static void
AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                          int maxOpNumber, int maxProcNumber,
                          List *items)
{
      List     *operators;          /* OpFamilyMember list for operators */
      List     *procedures;         /* OpFamilyMember list for support procs */
      ListCell   *l;

      operators = NIL;
      procedures = NIL;

      /*
       * Scan the "items" list to obtain additional info.
       */
      foreach(l, items)
      {
            CreateOpClassItem *item = lfirst(l);
            Oid               lefttype,
                              righttype;
            OpFamilyMember *member;

            Assert(IsA(item, CreateOpClassItem));
            switch (item->itemtype)
            {
                  case OPCLASS_ITEM_OPERATOR:
                        if (item->number <= 0 || item->number > maxOpNumber)
                              ereport(ERROR,
                                          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                           errmsg("invalid operator number %d,"
                                                      " must be between 1 and %d",
                                                      item->number, maxOpNumber)));
                        processTypesSpec(item->args, &lefttype, &righttype);
                        /* Save the info */
                        member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
                        member->number = item->number;
                        member->lefttype = lefttype;
                        member->righttype = righttype;
                        addFamilyMember(&operators, member, false);
                        break;
                  case OPCLASS_ITEM_FUNCTION:
                        if (item->number <= 0 || item->number > maxProcNumber)
                              ereport(ERROR,
                                          (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                           errmsg("invalid procedure number %d,"
                                                      " must be between 1 and %d",
                                                      item->number, maxProcNumber)));
                        processTypesSpec(item->args, &lefttype, &righttype);
                        /* Save the info */
                        member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
                        member->number = item->number;
                        member->lefttype = lefttype;
                        member->righttype = righttype;
                        addFamilyMember(&procedures, member, true);
                        break;
                  case OPCLASS_ITEM_STORAGETYPE:
                        /* grammar prevents this from appearing */
                  default:
                        elog(ERROR, "unrecognized item type: %d", item->itemtype);
                        break;
            }
      }

      /*
       * Remove tuples from pg_amop and pg_amproc.
       */
      dropOperators(opfamilyname, amoid, opfamilyoid, operators);
      dropProcedures(opfamilyname, amoid, opfamilyoid, procedures);
}


/*
 * Deal with explicit arg types used in ALTER ADD/DROP
 */
static void
processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
{
      TypeName   *typeName;

      Assert(args != NIL);

      typeName = (TypeName *) linitial(args);
      *lefttype = typenameTypeId(NULL, typeName, NULL);

      if (list_length(args) > 1)
      {
            typeName = (TypeName *) lsecond(args);
            *righttype = typenameTypeId(NULL, typeName, NULL);
      }
      else
            *righttype = *lefttype;

      if (list_length(args) > 2)
            ereport(ERROR,
                        (errcode(ERRCODE_SYNTAX_ERROR),
                         errmsg("one or two argument types must be specified")));
}


/*
 * Determine the lefttype/righttype to assign to an operator,
 * and do any validity checking we can manage.
 */
static void
assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
{
      Operator    optup;
      Form_pg_operator opform;

      /* Fetch the operator definition */
      optup = SearchSysCache(OPEROID,
                                       ObjectIdGetDatum(member->object),
                                       0, 0, 0);
      if (optup == NULL)
            elog(ERROR, "cache lookup failed for operator %u", member->object);
      opform = (Form_pg_operator) GETSTRUCT(optup);

      /*
       * Opfamily operators must be binary ops returning boolean.
       */
      if (opform->oprkind != 'b')
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                         errmsg("index operators must be binary")));
      if (opform->oprresult != BOOLOID)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                         errmsg("index operators must return boolean")));

      /*
       * If lefttype/righttype isn't specified, use the operator's input types
       */
      if (!OidIsValid(member->lefttype))
            member->lefttype = opform->oprleft;
      if (!OidIsValid(member->righttype))
            member->righttype = opform->oprright;

      ReleaseSysCache(optup);
}

/*
 * Determine the lefttype/righttype to assign to a support procedure,
 * and do any validity checking we can manage.
 */
static void
assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
{
      HeapTuple   proctup;
      Form_pg_proc procform;

      /* Fetch the procedure definition */
      proctup = SearchSysCache(PROCOID,
                                           ObjectIdGetDatum(member->object),
                                           0, 0, 0);
      if (proctup == NULL)
            elog(ERROR, "cache lookup failed for function %u", member->object);
      procform = (Form_pg_proc) GETSTRUCT(proctup);

      /*
       * btree support procs must be 2-arg procs returning int4; hash support
       * procs must be 1-arg procs returning int4; otherwise we don't know.
       */
      if (amoid == BTREE_AM_OID)
      {
            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 lefttype/righttype isn't specified, use the proc's input types
             */
            if (!OidIsValid(member->lefttype))
                  member->lefttype = procform->proargtypes.values[0];
            if (!OidIsValid(member->righttype))
                  member->righttype = procform->proargtypes.values[1];
      }
      else if (amoid == HASH_AM_OID)
      {
            if (procform->pronargs != 1)
                  ereport(ERROR,
                              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                               errmsg("hash procedures must have one argument")));
            if (procform->prorettype != INT4OID)
                  ereport(ERROR,
                              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                               errmsg("hash procedures must return integer")));

            /*
             * If lefttype/righttype isn't specified, use the proc's input type
             */
            if (!OidIsValid(member->lefttype))
                  member->lefttype = procform->proargtypes.values[0];
            if (!OidIsValid(member->righttype))
                  member->righttype = procform->proargtypes.values[0];
      }
      else
      {
            /*
             * The default for GiST and GIN in CREATE OPERATOR CLASS is to use the
             * class' opcintype as lefttype and righttype.  In CREATE or ALTER
             * OPERATOR FAMILY, opcintype isn't available, so make the user
             * specify the types.
             */
            if (!OidIsValid(member->lefttype))
                  member->lefttype = typeoid;
            if (!OidIsValid(member->righttype))
                  member->righttype = typeoid;
            if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
                  ereport(ERROR,
                              (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                               errmsg("associated data types must be specified for index support procedure")));
      }

      ReleaseSysCache(proctup);
}

/*
 * Add a new family member to the appropriate list, after checking for
 * duplicated strategy or proc number.
 */
static void
addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
{
      ListCell   *l;

      foreach(l, *list)
      {
            OpFamilyMember *old = (OpFamilyMember *) lfirst(l);

            if (old->number == member->number &&
                  old->lefttype == member->lefttype &&
                  old->righttype == member->righttype)
            {
                  if (isProc)
                        ereport(ERROR,
                                    (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                     errmsg("procedure number %d for (%s,%s) appears more than once",
                                                member->number,
                                                format_type_be(member->lefttype),
                                                format_type_be(member->righttype))));
                  else
                        ereport(ERROR,
                                    (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                                     errmsg("operator number %d for (%s,%s) appears more than once",
                                                member->number,
                                                format_type_be(member->lefttype),
                                                format_type_be(member->righttype))));
            }
      }
      *list = lappend(*list, member);
}

/*
 * Dump the operators to pg_amop
 *
 * We also make dependency entries in pg_depend for the opfamily entries.
 * If opclassoid is valid then make an INTERNAL dependency on that opclass,
 * else make an AUTO dependency on the opfamily.
 */
static void
storeOperators(List *opfamilyname, Oid amoid,
                     Oid opfamilyoid, Oid opclassoid,
                     List *operators, bool isAdd)
{
      Relation    rel;
      Datum       values[Natts_pg_amop];
      bool        nulls[Natts_pg_amop];
      HeapTuple   tup;
      Oid               entryoid;
      ObjectAddress myself,
                        referenced;
      ListCell   *l;

      rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);

      foreach(l, operators)
      {
            OpFamilyMember *op = (OpFamilyMember *) lfirst(l);

            /*
             * If adding to an existing family, check for conflict with an
             * existing pg_amop entry (just to give a nicer error message)
             */
            if (isAdd &&
                  SearchSysCacheExists(AMOPSTRATEGY,
                                                 ObjectIdGetDatum(opfamilyoid),
                                                 ObjectIdGetDatum(op->lefttype),
                                                 ObjectIdGetDatum(op->righttype),
                                                 Int16GetDatum(op->number)))
                  ereport(ERROR,
                              (errcode(ERRCODE_DUPLICATE_OBJECT),
                               errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
                                          op->number,
                                          format_type_be(op->lefttype),
                                          format_type_be(op->righttype),
                                          NameListToString(opfamilyname))));

            /* Create the pg_amop entry */
            memset(values, 0, sizeof(values));
            memset(nulls, false, sizeof(nulls));

            values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
            values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
            values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
            values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
            values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
            values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);

            tup = heap_form_tuple(rel->rd_att, values, nulls);

            entryoid = simple_heap_insert(rel, tup);

            CatalogUpdateIndexes(rel, tup);

            heap_freetuple(tup);

            /* Make its dependencies */
            myself.classId = AccessMethodOperatorRelationId;
            myself.objectId = entryoid;
            myself.objectSubId = 0;

            referenced.classId = OperatorRelationId;
            referenced.objectId = op->object;
            referenced.objectSubId = 0;

            if (OidIsValid(opclassoid))
            {
                  /* if contained in an opclass, use a NORMAL dep on operator */
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

                  /* ... and an INTERNAL dep on the opclass */
                  referenced.classId = OperatorClassRelationId;
                  referenced.objectId = opclassoid;
                  referenced.objectSubId = 0;
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
            }
            else
            {
                  /* if "loose" in the opfamily, use a AUTO dep on operator */
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);

                  /* ... and an AUTO dep on the opfamily */
                  referenced.classId = OperatorFamilyRelationId;
                  referenced.objectId = opfamilyoid;
                  referenced.objectSubId = 0;
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
            }
      }

      heap_close(rel, RowExclusiveLock);
}

/*
 * Dump the procedures (support routines) to pg_amproc
 *
 * We also make dependency entries in pg_depend for the opfamily entries.
 * If opclassoid is valid then make an INTERNAL dependency on that opclass,
 * else make an AUTO dependency on the opfamily.
 */
static void
storeProcedures(List *opfamilyname, Oid amoid,
                        Oid opfamilyoid, Oid opclassoid,
                        List *procedures, bool isAdd)
{
      Relation    rel;
      Datum       values[Natts_pg_amproc];
      bool        nulls[Natts_pg_amproc];
      HeapTuple   tup;
      Oid               entryoid;
      ObjectAddress myself,
                        referenced;
      ListCell   *l;

      rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);

      foreach(l, procedures)
      {
            OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);

            /*
             * If adding to an existing family, check for conflict with an
             * existing pg_amproc entry (just to give a nicer error message)
             */
            if (isAdd &&
                  SearchSysCacheExists(AMPROCNUM,
                                                 ObjectIdGetDatum(opfamilyoid),
                                                 ObjectIdGetDatum(proc->lefttype),
                                                 ObjectIdGetDatum(proc->righttype),
                                                 Int16GetDatum(proc->number)))
                  ereport(ERROR,
                              (errcode(ERRCODE_DUPLICATE_OBJECT),
                               errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
                                          proc->number,
                                          format_type_be(proc->lefttype),
                                          format_type_be(proc->righttype),
                                          NameListToString(opfamilyname))));

            /* Create the pg_amproc entry */
            memset(values, 0, sizeof(values));
            memset(nulls, false, sizeof(nulls));

            values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
            values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
            values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
            values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
            values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);

            tup = heap_form_tuple(rel->rd_att, values, nulls);

            entryoid = simple_heap_insert(rel, tup);

            CatalogUpdateIndexes(rel, tup);

            heap_freetuple(tup);

            /* Make its dependencies */
            myself.classId = AccessMethodProcedureRelationId;
            myself.objectId = entryoid;
            myself.objectSubId = 0;

            referenced.classId = ProcedureRelationId;
            referenced.objectId = proc->object;
            referenced.objectSubId = 0;

            if (OidIsValid(opclassoid))
            {
                  /* if contained in an opclass, use a NORMAL dep on procedure */
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

                  /* ... and an INTERNAL dep on the opclass */
                  referenced.classId = OperatorClassRelationId;
                  referenced.objectId = opclassoid;
                  referenced.objectSubId = 0;
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
            }
            else
            {
                  /* if "loose" in the opfamily, use a AUTO dep on procedure */
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);

                  /* ... and an AUTO dep on the opfamily */
                  referenced.classId = OperatorFamilyRelationId;
                  referenced.objectId = opfamilyoid;
                  referenced.objectSubId = 0;
                  recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
            }
      }

      heap_close(rel, RowExclusiveLock);
}


/*
 * Remove operator entries from an opfamily.
 *
 * Note: this is only allowed for "loose" members of an opfamily, hence
 * behavior is always RESTRICT.
 */
static void
dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                    List *operators)
{
      ListCell   *l;

      foreach(l, operators)
      {
            OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
            Oid               amopid;
            ObjectAddress object;

            amopid = GetSysCacheOid(AMOPSTRATEGY,
                                                ObjectIdGetDatum(opfamilyoid),
                                                ObjectIdGetDatum(op->lefttype),
                                                ObjectIdGetDatum(op->righttype),
                                                Int16GetDatum(op->number));
            if (!OidIsValid(amopid))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
                                          op->number,
                                          format_type_be(op->lefttype),
                                          format_type_be(op->righttype),
                                          NameListToString(opfamilyname))));

            object.classId = AccessMethodOperatorRelationId;
            object.objectId = amopid;
            object.objectSubId = 0;

            performDeletion(&object, DROP_RESTRICT);
      }
}

/*
 * Remove procedure entries from an opfamily.
 *
 * Note: this is only allowed for "loose" members of an opfamily, hence
 * behavior is always RESTRICT.
 */
static void
dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
                     List *procedures)
{
      ListCell   *l;

      foreach(l, procedures)
      {
            OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
            Oid               amprocid;
            ObjectAddress object;

            amprocid = GetSysCacheOid(AMPROCNUM,
                                                  ObjectIdGetDatum(opfamilyoid),
                                                  ObjectIdGetDatum(op->lefttype),
                                                  ObjectIdGetDatum(op->righttype),
                                                  Int16GetDatum(op->number));
            if (!OidIsValid(amprocid))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
                                          op->number,
                                          format_type_be(op->lefttype),
                                          format_type_be(op->righttype),
                                          NameListToString(opfamilyname))));

            object.classId = AccessMethodProcedureRelationId;
            object.objectId = amprocid;
            object.objectSubId = 0;

            performDeletion(&object, DROP_RESTRICT);
      }
}


/*
 * RemoveOpClass
 *          Deletes an opclass.
 */
void
RemoveOpClass(RemoveOpClassStmt *stmt)
{
      Oid               amID,
                        opcID;
      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.
       */
      tuple = OpClassCacheLookup(amID, stmt->opclassname);
      if (!HeapTupleIsValid(tuple))
      {
            if (!stmt->missing_ok)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator class \"%s\" does not exist for access method \"%s\"",
                                    NameListToString(stmt->opclassname), stmt->amname)));
            else
                  ereport(NOTICE,
                              (errmsg("operator class \"%s\" does not exist for access method \"%s\"",
                                    NameListToString(stmt->opclassname), stmt->amname)));
            return;
      }

      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);
}

/*
 * RemoveOpFamily
 *          Deletes an opfamily.
 */
void
RemoveOpFamily(RemoveOpFamilyStmt *stmt)
{
      Oid               amID,
                        opfID;
      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 opfamily.
       */
      tuple = OpFamilyCacheLookup(amID, stmt->opfamilyname);
      if (!HeapTupleIsValid(tuple))
      {
            if (!stmt->missing_ok)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                 NameListToString(stmt->opfamilyname), stmt->amname)));
            else
                  ereport(NOTICE,
                              (errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                 NameListToString(stmt->opfamilyname), stmt->amname)));
            return;
      }

      opfID = HeapTupleGetOid(tuple);

      /* Permission check: must own opfamily or its namespace */
      if (!pg_opfamily_ownercheck(opfID, GetUserId()) &&
            !pg_namespace_ownercheck(((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace,
                                                 GetUserId()))
            aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
                                 NameListToString(stmt->opfamilyname));

      ReleaseSysCache(tuple);

      /*
       * Do the deletion
       */
      object.classId = OperatorFamilyRelationId;
      object.objectId = opfID;
      object.objectSubId = 0;

      performDeletion(&object, stmt->behavior);
}


/*
 * Deletion subroutines for use by dependency.c.
 */
void
RemoveOpFamilyById(Oid opfamilyOid)
{
      Relation    rel;
      HeapTuple   tup;

      rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);

      tup = SearchSysCache(OPFAMILYOID,
                                     ObjectIdGetDatum(opfamilyOid),
                                     0, 0, 0);
      if (!HeapTupleIsValid(tup)) /* should not happen */
            elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);

      simple_heap_delete(rel, &tup->t_self);

      ReleaseSysCache(tup);

      heap_close(rel, RowExclusiveLock);
}

void
RemoveOpClassById(Oid opclassOid)
{
      Relation    rel;
      HeapTuple   tup;

      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);
}

void
RemoveAmOpEntryById(Oid entryOid)
{
      Relation    rel;
      HeapTuple   tup;
      ScanKeyData skey[1];
      SysScanDesc scan;

      ScanKeyInit(&skey[0],
                        ObjectIdAttributeNumber,
                        BTEqualStrategyNumber, F_OIDEQ,
                        ObjectIdGetDatum(entryOid));

      rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);

      scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
                                            SnapshotNow, 1, skey);

      /* we expect exactly one match */
      tup = systable_getnext(scan);
      if (!HeapTupleIsValid(tup))
            elog(ERROR, "could not find tuple for amop entry %u", entryOid);

      simple_heap_delete(rel, &tup->t_self);

      systable_endscan(scan);
      heap_close(rel, RowExclusiveLock);
}

void
RemoveAmProcEntryById(Oid entryOid)
{
      Relation    rel;
      HeapTuple   tup;
      ScanKeyData skey[1];
      SysScanDesc scan;

      ScanKeyInit(&skey[0],
                        ObjectIdAttributeNumber,
                        BTEqualStrategyNumber, F_OIDEQ,
                        ObjectIdGetDatum(entryOid));

      rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);

      scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
                                            SnapshotNow, 1, skey);

      /* we expect exactly one match */
      tup = systable_getnext(scan);
      if (!HeapTupleIsValid(tup))
            elog(ERROR, "could not find tuple for amproc entry %u", entryOid);

      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);
}

/*
 * Rename opfamily
 */
void
RenameOpFamily(List *name, const char *access_method, const char *newname)
{
      Oid               opfOid;
      Oid               amOid;
      Oid               namespaceOid;
      char     *schemaname;
      char     *opfname;
      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(OperatorFamilyRelationId, RowExclusiveLock);

      /*
       * Look up the opfamily
       */
      DeconstructQualifiedName(name, &schemaname, &opfname);

      if (schemaname)
      {
            namespaceOid = LookupExplicitNamespace(schemaname);

            tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP,
                                                 ObjectIdGetDatum(amOid),
                                                 PointerGetDatum(opfname),
                                                 ObjectIdGetDatum(namespaceOid),
                                                 0);
            if (!HeapTupleIsValid(tup))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                          opfname, access_method)));

            opfOid = HeapTupleGetOid(tup);
      }
      else
      {
            opfOid = OpfamilynameGetOpfid(amOid, opfname);
            if (!OidIsValid(opfOid))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                          opfname, access_method)));

            tup = SearchSysCacheCopy(OPFAMILYOID,
                                                 ObjectIdGetDatum(opfOid),
                                                 0, 0, 0);
            if (!HeapTupleIsValid(tup))         /* should not happen */
                  elog(ERROR, "cache lookup failed for opfamily %u", opfOid);

            namespaceOid = ((Form_pg_opfamily) GETSTRUCT(tup))->opfnamespace;
      }

      /* make sure the new name doesn't exist */
      if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
                                           ObjectIdGetDatum(amOid),
                                           CStringGetDatum(newname),
                                           ObjectIdGetDatum(namespaceOid),
                                           0))
      {
            ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_OBJECT),
                         errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
                                    newname, access_method,
                                    get_namespace_name(namespaceOid))));
      }

      /* must be owner */
      if (!pg_opfamily_ownercheck(opfOid, GetUserId()))
            aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
                                 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_opfamily) GETSTRUCT(tup))->opfname), newname);
      simple_heap_update(rel, &tup->t_self, tup);
      CatalogUpdateIndexes(rel, tup);

      heap_close(rel, NoLock);
      heap_freetuple(tup);
}

/*
 * Change opclass owner by name
 */
void
AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
{
      Oid               amOid;
      Relation    rel;
      HeapTuple   tup;
      char     *opcname;
      char     *schemaname;

      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)
      {
            Oid               namespaceOid;

            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
      {
            Oid               opcOid;

            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);
      }

      AlterOpClassOwner_internal(rel, tup, newOwnerId);

      heap_freetuple(tup);
      heap_close(rel, NoLock);
}

/*
 * Change operator class owner, specified by OID
 */
void
AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId)
{
      HeapTuple       tup;
      Relation        rel;

      rel = heap_open(OperatorClassRelationId, RowExclusiveLock);

      tup = SearchSysCacheCopy(CLAOID, ObjectIdGetDatum(opclassOid), 0, 0, 0);
      if (!HeapTupleIsValid(tup))
            elog(ERROR, "cache lookup failed for opclass %u", opclassOid);

      AlterOpClassOwner_internal(rel, tup, newOwnerId);

      heap_freetuple(tup);
      heap_close(rel, NoLock);
}

/*
 * The first parameter is pg_opclass, opened and suitably locked.  The second
 * parameter is a copy of the tuple from pg_opclass we want to modify.
 */
static void
AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
      Oid               namespaceOid;
      AclResult   aclresult;
      Form_pg_opclass opcForm;

      Assert(tup->t_tableOid == OperatorClassRelationId);
      Assert(RelationGetRelid(rel) == OperatorClassRelationId);

      opcForm = (Form_pg_opclass) GETSTRUCT(tup);

      namespaceOid = opcForm->opcnamespace;

      /*
       * 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,
                                             NameStr(opcForm->opcname));

                  /* 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, HeapTupleGetOid(tup),
                                                newOwnerId);
      }
}

/*
 * Change opfamily owner by name
 */
void
AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId)
{
      Oid               amOid;
      Relation    rel;
      HeapTuple   tup;
      char     *opfname;
      char     *schemaname;

      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(OperatorFamilyRelationId, RowExclusiveLock);

      /*
       * Look up the opfamily
       */
      DeconstructQualifiedName(name, &schemaname, &opfname);

      if (schemaname)
      {
            Oid               namespaceOid;

            namespaceOid = LookupExplicitNamespace(schemaname);

            tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP,
                                                 ObjectIdGetDatum(amOid),
                                                 PointerGetDatum(opfname),
                                                 ObjectIdGetDatum(namespaceOid),
                                                 0);
            if (!HeapTupleIsValid(tup))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                          opfname, access_method)));
      }
      else
      {
            Oid               opfOid;

            opfOid = OpfamilynameGetOpfid(amOid, opfname);
            if (!OidIsValid(opfOid))
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_OBJECT),
                               errmsg("operator family \"%s\" does not exist for access method \"%s\"",
                                          opfname, access_method)));

            tup = SearchSysCacheCopy(OPFAMILYOID,
                                                 ObjectIdGetDatum(opfOid),
                                                 0, 0, 0);
            if (!HeapTupleIsValid(tup))         /* should not happen */
                  elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
      }

      AlterOpFamilyOwner_internal(rel, tup, newOwnerId);

      heap_freetuple(tup);
      heap_close(rel, NoLock);
}

/*
 * Change operator family owner, specified by OID
 */
void
AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId)
{
      HeapTuple       tup;
      Relation        rel;

      rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);

      tup = SearchSysCacheCopy(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid),
                                           0, 0, 0);
      if (!HeapTupleIsValid(tup))
            elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);

      AlterOpFamilyOwner_internal(rel, tup, newOwnerId);

      heap_freetuple(tup);
      heap_close(rel, NoLock);
}

/*
 * The first parameter is pg_opfamily, opened and suitably locked.      The second
 * parameter is a copy of the tuple from pg_opfamily we want to modify.
 */
static void
AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
      Oid               namespaceOid;
      AclResult   aclresult;
      Form_pg_opfamily opfForm;

      Assert(tup->t_tableOid == OperatorFamilyRelationId);
      Assert(RelationGetRelid(rel) == OperatorFamilyRelationId);

      opfForm = (Form_pg_opfamily) GETSTRUCT(tup);

      namespaceOid = opfForm->opfnamespace;

      /*
       * If the new owner is the same as the existing owner, consider the
       * command to have succeeded.  This is for dump restoration purposes.
       */
      if (opfForm->opfowner != newOwnerId)
      {
            /* Superusers can always do it */
            if (!superuser())
            {
                  /* Otherwise, must be owner of the existing object */
                  if (!pg_opfamily_ownercheck(HeapTupleGetOid(tup), GetUserId()))
                        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
                                             NameStr(opfForm->opfname));

                  /* 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
             */
            opfForm->opfowner = newOwnerId;

            simple_heap_update(rel, &tup->t_self, tup);

            CatalogUpdateIndexes(rel, tup);

            /* Update owner dependency reference */
            changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup),
                                                newOwnerId);
      }
}

Generated by  Doxygen 1.6.0   Back to index