LCOV - code coverage report
Current view: top level - src/backend/commands - opclasscmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 556 623 89.2 %
Date: 2026-02-07 07:17:36 Functions: 21 21 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * opclasscmds.c
       4             :  *
       5             :  *    Routines for opclass (and opfamily) manipulation commands
       6             :  *
       7             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/commands/opclasscmds.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : #include "postgres.h"
      17             : 
      18             : #include <limits.h>
      19             : 
      20             : #include "access/genam.h"
      21             : #include "access/hash.h"
      22             : #include "access/htup_details.h"
      23             : #include "access/nbtree.h"
      24             : #include "access/table.h"
      25             : #include "catalog/catalog.h"
      26             : #include "catalog/dependency.h"
      27             : #include "catalog/indexing.h"
      28             : #include "catalog/objectaccess.h"
      29             : #include "catalog/pg_am.h"
      30             : #include "catalog/pg_amop.h"
      31             : #include "catalog/pg_amproc.h"
      32             : #include "catalog/pg_namespace.h"
      33             : #include "catalog/pg_opclass.h"
      34             : #include "catalog/pg_operator.h"
      35             : #include "catalog/pg_opfamily.h"
      36             : #include "catalog/pg_proc.h"
      37             : #include "catalog/pg_type.h"
      38             : #include "commands/defrem.h"
      39             : #include "commands/event_trigger.h"
      40             : #include "miscadmin.h"
      41             : #include "parser/parse_func.h"
      42             : #include "parser/parse_oper.h"
      43             : #include "parser/parse_type.h"
      44             : #include "utils/acl.h"
      45             : #include "utils/builtins.h"
      46             : #include "utils/fmgroids.h"
      47             : #include "utils/lsyscache.h"
      48             : #include "utils/rel.h"
      49             : #include "utils/syscache.h"
      50             : 
      51             : static void AlterOpFamilyAdd(AlterOpFamilyStmt *stmt,
      52             :                              Oid amoid, Oid opfamilyoid,
      53             :                              int maxOpNumber, int maxProcNumber,
      54             :                              int optsProcNumber, List *items);
      55             : static void AlterOpFamilyDrop(AlterOpFamilyStmt *stmt,
      56             :                               Oid amoid, Oid opfamilyoid,
      57             :                               int maxOpNumber, int maxProcNumber,
      58             :                               List *items);
      59             : static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
      60             : static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
      61             : static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
      62             :                             int opclassOptsProcNum);
      63             : static void addFamilyMember(List **list, OpFamilyMember *member);
      64             : static void storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
      65             :                            List *operators, bool isAdd);
      66             : static void storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
      67             :                             List *procedures, bool isAdd);
      68             : static bool typeDepNeeded(Oid typid, OpFamilyMember *member);
      69             : static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
      70             :                           List *operators);
      71             : static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
      72             :                            List *procedures);
      73             : 
      74             : /*
      75             :  * OpFamilyCacheLookup
      76             :  *      Look up an existing opfamily by name.
      77             :  *
      78             :  * Returns a syscache tuple reference, or NULL if not found.
      79             :  */
      80             : static HeapTuple
      81         888 : OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
      82             : {
      83             :     char       *schemaname;
      84             :     char       *opfname;
      85             :     HeapTuple   htup;
      86             : 
      87             :     /* deconstruct the name list */
      88         888 :     DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
      89             : 
      90         888 :     if (schemaname)
      91             :     {
      92             :         /* Look in specific schema only */
      93             :         Oid         namespaceId;
      94             : 
      95         146 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
      96         140 :         if (!OidIsValid(namespaceId))
      97           6 :             htup = NULL;
      98             :         else
      99         134 :             htup = SearchSysCache3(OPFAMILYAMNAMENSP,
     100             :                                    ObjectIdGetDatum(amID),
     101             :                                    PointerGetDatum(opfname),
     102             :                                    ObjectIdGetDatum(namespaceId));
     103             :     }
     104             :     else
     105             :     {
     106             :         /* Unqualified opfamily name, so search the search path */
     107         742 :         Oid         opfID = OpfamilynameGetOpfid(amID, opfname);
     108             : 
     109         742 :         if (!OidIsValid(opfID))
     110          12 :             htup = NULL;
     111             :         else
     112         730 :             htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
     113             :     }
     114             : 
     115         882 :     if (!HeapTupleIsValid(htup) && !missing_ok)
     116             :     {
     117             :         HeapTuple   amtup;
     118             : 
     119           6 :         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
     120           6 :         if (!HeapTupleIsValid(amtup))
     121           0 :             elog(ERROR, "cache lookup failed for access method %u", amID);
     122           6 :         ereport(ERROR,
     123             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     124             :                  errmsg("operator family \"%s\" does not exist for access method \"%s\"",
     125             :                         NameListToString(opfamilyname),
     126             :                         NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
     127             :     }
     128             : 
     129         876 :     return htup;
     130             : }
     131             : 
     132             : /*
     133             :  * get_opfamily_oid
     134             :  *    find an opfamily OID by possibly qualified name
     135             :  *
     136             :  * If not found, returns InvalidOid if missing_ok, else throws error.
     137             :  */
     138             : Oid
     139         888 : get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
     140             : {
     141             :     HeapTuple   htup;
     142             :     Form_pg_opfamily opfamform;
     143             :     Oid         opfID;
     144             : 
     145         888 :     htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
     146         876 :     if (!HeapTupleIsValid(htup))
     147          12 :         return InvalidOid;
     148         864 :     opfamform = (Form_pg_opfamily) GETSTRUCT(htup);
     149         864 :     opfID = opfamform->oid;
     150         864 :     ReleaseSysCache(htup);
     151             : 
     152         864 :     return opfID;
     153             : }
     154             : 
     155             : /*
     156             :  * OpClassCacheLookup
     157             :  *      Look up an existing opclass by name.
     158             :  *
     159             :  * Returns a syscache tuple reference, or NULL if not found.
     160             :  */
     161             : static HeapTuple
     162         214 : OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
     163             : {
     164             :     char       *schemaname;
     165             :     char       *opcname;
     166             :     HeapTuple   htup;
     167             : 
     168             :     /* deconstruct the name list */
     169         214 :     DeconstructQualifiedName(opclassname, &schemaname, &opcname);
     170             : 
     171         214 :     if (schemaname)
     172             :     {
     173             :         /* Look in specific schema only */
     174             :         Oid         namespaceId;
     175             : 
     176          26 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
     177          26 :         if (!OidIsValid(namespaceId))
     178           6 :             htup = NULL;
     179             :         else
     180          20 :             htup = SearchSysCache3(CLAAMNAMENSP,
     181             :                                    ObjectIdGetDatum(amID),
     182             :                                    PointerGetDatum(opcname),
     183             :                                    ObjectIdGetDatum(namespaceId));
     184             :     }
     185             :     else
     186             :     {
     187             :         /* Unqualified opclass name, so search the search path */
     188         188 :         Oid         opcID = OpclassnameGetOpcid(amID, opcname);
     189             : 
     190         188 :         if (!OidIsValid(opcID))
     191          12 :             htup = NULL;
     192             :         else
     193         176 :             htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
     194             :     }
     195             : 
     196         214 :     if (!HeapTupleIsValid(htup) && !missing_ok)
     197             :     {
     198             :         HeapTuple   amtup;
     199             : 
     200           6 :         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
     201           6 :         if (!HeapTupleIsValid(amtup))
     202           0 :             elog(ERROR, "cache lookup failed for access method %u", amID);
     203           6 :         ereport(ERROR,
     204             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     205             :                  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
     206             :                         NameListToString(opclassname),
     207             :                         NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
     208             :     }
     209             : 
     210         208 :     return htup;
     211             : }
     212             : 
     213             : /*
     214             :  * get_opclass_oid
     215             :  *    find an opclass OID by possibly qualified name
     216             :  *
     217             :  * If not found, returns InvalidOid if missing_ok, else throws error.
     218             :  */
     219             : Oid
     220         214 : get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
     221             : {
     222             :     HeapTuple   htup;
     223             :     Form_pg_opclass opcform;
     224             :     Oid         opcID;
     225             : 
     226         214 :     htup = OpClassCacheLookup(amID, opclassname, missing_ok);
     227         208 :     if (!HeapTupleIsValid(htup))
     228          12 :         return InvalidOid;
     229         196 :     opcform = (Form_pg_opclass) GETSTRUCT(htup);
     230         196 :     opcID = opcform->oid;
     231         196 :     ReleaseSysCache(htup);
     232             : 
     233         196 :     return opcID;
     234             : }
     235             : 
     236             : /*
     237             :  * CreateOpFamily
     238             :  *      Internal routine to make the catalog entry for a new operator family.
     239             :  *
     240             :  * Caller must have done permissions checks etc. already.
     241             :  */
     242             : static ObjectAddress
     243         648 : CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
     244             :                Oid namespaceoid, Oid amoid)
     245             : {
     246             :     Oid         opfamilyoid;
     247             :     Relation    rel;
     248             :     HeapTuple   tup;
     249             :     Datum       values[Natts_pg_opfamily];
     250             :     bool        nulls[Natts_pg_opfamily];
     251             :     NameData    opfName;
     252             :     ObjectAddress myself,
     253             :                 referenced;
     254             : 
     255         648 :     rel = table_open(OperatorFamilyRelationId, RowExclusiveLock);
     256             : 
     257             :     /*
     258             :      * Make sure there is no existing opfamily of this name (this is just to
     259             :      * give a more friendly error message than "duplicate key").
     260             :      */
     261         648 :     if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
     262             :                               ObjectIdGetDatum(amoid),
     263             :                               CStringGetDatum(opfname),
     264             :                               ObjectIdGetDatum(namespaceoid)))
     265           0 :         ereport(ERROR,
     266             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     267             :                  errmsg("operator family \"%s\" for access method \"%s\" already exists",
     268             :                         opfname, stmt->amname)));
     269             : 
     270             :     /*
     271             :      * Okay, let's create the pg_opfamily entry.
     272             :      */
     273         648 :     memset(values, 0, sizeof(values));
     274         648 :     memset(nulls, false, sizeof(nulls));
     275             : 
     276         648 :     opfamilyoid = GetNewOidWithIndex(rel, OpfamilyOidIndexId,
     277             :                                      Anum_pg_opfamily_oid);
     278         648 :     values[Anum_pg_opfamily_oid - 1] = ObjectIdGetDatum(opfamilyoid);
     279         648 :     values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
     280         648 :     namestrcpy(&opfName, opfname);
     281         648 :     values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
     282         648 :     values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     283         648 :     values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
     284             : 
     285         648 :     tup = heap_form_tuple(rel->rd_att, values, nulls);
     286             : 
     287         648 :     CatalogTupleInsert(rel, tup);
     288             : 
     289         648 :     heap_freetuple(tup);
     290             : 
     291             :     /*
     292             :      * Create dependencies for the opfamily proper.
     293             :      */
     294         648 :     myself.classId = OperatorFamilyRelationId;
     295         648 :     myself.objectId = opfamilyoid;
     296         648 :     myself.objectSubId = 0;
     297             : 
     298             :     /* dependency on access method */
     299         648 :     referenced.classId = AccessMethodRelationId;
     300         648 :     referenced.objectId = amoid;
     301         648 :     referenced.objectSubId = 0;
     302         648 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     303             : 
     304             :     /* dependency on namespace */
     305         648 :     referenced.classId = NamespaceRelationId;
     306         648 :     referenced.objectId = namespaceoid;
     307         648 :     referenced.objectSubId = 0;
     308         648 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     309             : 
     310             :     /* dependency on owner */
     311         648 :     recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
     312             : 
     313             :     /* dependency on extension */
     314         648 :     recordDependencyOnCurrentExtension(&myself, false);
     315             : 
     316             :     /* Report the new operator family to possibly interested event triggers */
     317         648 :     EventTriggerCollectSimpleCommand(myself, InvalidObjectAddress,
     318             :                                      (Node *) stmt);
     319             : 
     320             :     /* Post creation hook for new operator family */
     321         648 :     InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
     322             : 
     323         648 :     table_close(rel, RowExclusiveLock);
     324             : 
     325         648 :     return myself;
     326             : }
     327             : 
     328             : /*
     329             :  * DefineOpClass
     330             :  *      Define a new index operator class.
     331             :  */
     332             : ObjectAddress
     333         556 : DefineOpClass(CreateOpClassStmt *stmt)
     334             : {
     335             :     char       *opcname;        /* name of opclass we're creating */
     336             :     Oid         amoid,          /* our AM's oid */
     337             :                 typeoid,        /* indexable datatype oid */
     338             :                 storageoid,     /* storage datatype oid, if any */
     339             :                 namespaceoid,   /* namespace to create opclass in */
     340             :                 opfamilyoid,    /* oid of containing opfamily */
     341             :                 opclassoid;     /* oid of opclass we create */
     342             :     int         maxOpNumber,    /* amstrategies value */
     343             :                 optsProcNumber, /* amoptsprocnum value */
     344             :                 maxProcNumber;  /* amsupport value */
     345             :     bool        amstorage;      /* amstorage flag */
     346         556 :     bool        isDefault = stmt->isDefault;
     347             :     List       *operators;      /* OpFamilyMember list for operators */
     348             :     List       *procedures;     /* OpFamilyMember list for support procs */
     349             :     ListCell   *l;
     350             :     Relation    rel;
     351             :     HeapTuple   tup;
     352             :     Form_pg_am  amform;
     353             :     const IndexAmRoutine *amroutine;
     354             :     Datum       values[Natts_pg_opclass];
     355             :     bool        nulls[Natts_pg_opclass];
     356             :     AclResult   aclresult;
     357             :     NameData    opcName;
     358             :     ObjectAddress myself,
     359             :                 referenced;
     360             : 
     361             :     /* Convert list of names to a name and namespace */
     362         556 :     namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
     363             :                                                      &opcname);
     364             : 
     365             :     /* Check we have creation rights in target namespace */
     366         556 :     aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
     367         556 :     if (aclresult != ACLCHECK_OK)
     368           0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
     369           0 :                        get_namespace_name(namespaceoid));
     370             : 
     371             :     /* Get necessary info about access method */
     372         556 :     tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
     373         556 :     if (!HeapTupleIsValid(tup))
     374           0 :         ereport(ERROR,
     375             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     376             :                  errmsg("access method \"%s\" does not exist",
     377             :                         stmt->amname)));
     378             : 
     379         556 :     amform = (Form_pg_am) GETSTRUCT(tup);
     380         556 :     amoid = amform->oid;
     381         556 :     amroutine = GetIndexAmRoutineByAmId(amoid, false);
     382         556 :     ReleaseSysCache(tup);
     383             : 
     384         556 :     maxOpNumber = amroutine->amstrategies;
     385             :     /* if amstrategies is zero, just enforce that op numbers fit in int16 */
     386         556 :     if (maxOpNumber <= 0)
     387         340 :         maxOpNumber = SHRT_MAX;
     388         556 :     maxProcNumber = amroutine->amsupport;
     389         556 :     optsProcNumber = amroutine->amoptsprocnum;
     390         556 :     amstorage = amroutine->amstorage;
     391             : 
     392             :     /* XXX Should we make any privilege check against the AM? */
     393             : 
     394             :     /*
     395             :      * The question of appropriate permissions for CREATE OPERATOR CLASS is
     396             :      * interesting.  Creating an opclass is tantamount to granting public
     397             :      * execute access on the functions involved, since the index machinery
     398             :      * generally does not check access permission before using the functions.
     399             :      * A minimum expectation therefore is that the caller have execute
     400             :      * privilege with grant option.  Since we don't have a way to make the
     401             :      * opclass go away if the grant option is revoked, we choose instead to
     402             :      * require ownership of the functions.  It's also not entirely clear what
     403             :      * permissions should be required on the datatype, but ownership seems
     404             :      * like a safe choice.
     405             :      *
     406             :      * Currently, we require superuser privileges to create an opclass. This
     407             :      * seems necessary because we have no way to validate that the offered set
     408             :      * of operators and functions are consistent with the AM's expectations.
     409             :      * It would be nice to provide such a check someday, if it can be done
     410             :      * without solving the halting problem :-(
     411             :      *
     412             :      * XXX re-enable NOT_USED code sections below if you remove this test.
     413             :      */
     414         556 :     if (!superuser())
     415           0 :         ereport(ERROR,
     416             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     417             :                  errmsg("must be superuser to create an operator class")));
     418             : 
     419             :     /* Look up the datatype */
     420         556 :     typeoid = typenameTypeId(NULL, stmt->datatype);
     421             : 
     422             : #ifdef NOT_USED
     423             :     /* XXX this is unnecessary given the superuser check above */
     424             :     /* Check we have ownership of the datatype */
     425             :     if (!object_ownercheck(TypeRelationId, typeoid, GetUserId()))
     426             :         aclcheck_error_type(ACLCHECK_NOT_OWNER, typeoid);
     427             : #endif
     428             : 
     429             :     /*
     430             :      * Look up the containing operator family, or create one if FAMILY option
     431             :      * was omitted and there's not a match already.
     432             :      */
     433         556 :     if (stmt->opfamilyname)
     434             :     {
     435          44 :         opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
     436             :     }
     437             :     else
     438             :     {
     439             :         /* Lookup existing family of same name and namespace */
     440         512 :         tup = SearchSysCache3(OPFAMILYAMNAMENSP,
     441             :                               ObjectIdGetDatum(amoid),
     442             :                               PointerGetDatum(opcname),
     443             :                               ObjectIdGetDatum(namespaceoid));
     444         512 :         if (HeapTupleIsValid(tup))
     445             :         {
     446          12 :             opfamilyoid = ((Form_pg_opfamily) GETSTRUCT(tup))->oid;
     447             : 
     448             :             /*
     449             :              * XXX given the superuser check above, there's no need for an
     450             :              * ownership check here
     451             :              */
     452          12 :             ReleaseSysCache(tup);
     453             :         }
     454             :         else
     455             :         {
     456             :             CreateOpFamilyStmt *opfstmt;
     457             :             ObjectAddress tmpAddr;
     458             : 
     459         500 :             opfstmt = makeNode(CreateOpFamilyStmt);
     460         500 :             opfstmt->opfamilyname = stmt->opclassname;
     461         500 :             opfstmt->amname = stmt->amname;
     462             : 
     463             :             /*
     464             :              * Create it ... again no need for more permissions ...
     465             :              */
     466         500 :             tmpAddr = CreateOpFamily(opfstmt, opcname, namespaceoid, amoid);
     467         500 :             opfamilyoid = tmpAddr.objectId;
     468             :         }
     469             :     }
     470             : 
     471         556 :     operators = NIL;
     472         556 :     procedures = NIL;
     473             : 
     474             :     /* Storage datatype is optional */
     475         556 :     storageoid = InvalidOid;
     476             : 
     477             :     /*
     478             :      * Scan the "items" list to obtain additional info.
     479             :      */
     480        6234 :     foreach(l, stmt->items)
     481             :     {
     482        5678 :         CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
     483             :         Oid         operOid;
     484             :         Oid         funcOid;
     485             :         Oid         sortfamilyOid;
     486             :         OpFamilyMember *member;
     487             : 
     488        5678 :         switch (item->itemtype)
     489             :         {
     490        2554 :             case OPCLASS_ITEM_OPERATOR:
     491        2554 :                 if (item->number <= 0 || item->number > maxOpNumber)
     492           0 :                     ereport(ERROR,
     493             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     494             :                              errmsg("invalid operator number %d,"
     495             :                                     " must be between 1 and %d",
     496             :                                     item->number, maxOpNumber)));
     497        2554 :                 if (item->name->objargs != NIL)
     498         478 :                     operOid = LookupOperWithArgs(item->name, false);
     499             :                 else
     500             :                 {
     501             :                     /* Default to binary op on input datatype */
     502        2076 :                     operOid = LookupOperName(NULL, item->name->objname,
     503             :                                              typeoid, typeoid,
     504             :                                              false, -1);
     505             :                 }
     506             : 
     507        2554 :                 if (item->order_family)
     508          96 :                     sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
     509             :                                                      item->order_family,
     510             :                                                      false);
     511             :                 else
     512        2458 :                     sortfamilyOid = InvalidOid;
     513             : 
     514             : #ifdef NOT_USED
     515             :                 /* XXX this is unnecessary given the superuser check above */
     516             :                 /* Caller must own operator and its underlying function */
     517             :                 if (!object_ownercheck(OperatorRelationId, operOid, GetUserId()))
     518             :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
     519             :                                    get_opname(operOid));
     520             :                 funcOid = get_opcode(operOid);
     521             :                 if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
     522             :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
     523             :                                    get_func_name(funcOid));
     524             : #endif
     525             : 
     526             :                 /* Save the info */
     527        2554 :                 member = palloc0_object(OpFamilyMember);
     528        2554 :                 member->is_func = false;
     529        2554 :                 member->object = operOid;
     530        2554 :                 member->number = item->number;
     531        2554 :                 member->sortfamily = sortfamilyOid;
     532        2554 :                 assignOperTypes(member, amoid, typeoid);
     533        2554 :                 addFamilyMember(&operators, member);
     534        2554 :                 break;
     535        2770 :             case OPCLASS_ITEM_FUNCTION:
     536        2770 :                 if (item->number <= 0 || item->number > maxProcNumber)
     537           0 :                     ereport(ERROR,
     538             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     539             :                              errmsg("invalid function number %d,"
     540             :                                     " must be between 1 and %d",
     541             :                                     item->number, maxProcNumber)));
     542        2770 :                 funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
     543             : #ifdef NOT_USED
     544             :                 /* XXX this is unnecessary given the superuser check above */
     545             :                 /* Caller must own function */
     546             :                 if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
     547             :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
     548             :                                    get_func_name(funcOid));
     549             : #endif
     550             :                 /* Save the info */
     551        2770 :                 member = palloc0_object(OpFamilyMember);
     552        2770 :                 member->is_func = true;
     553        2770 :                 member->object = funcOid;
     554        2770 :                 member->number = item->number;
     555             : 
     556             :                 /* allow overriding of the function's actual arg types */
     557        2770 :                 if (item->class_args)
     558         156 :                     processTypesSpec(item->class_args,
     559             :                                      &member->lefttype, &member->righttype);
     560             : 
     561        2770 :                 assignProcTypes(member, amoid, typeoid, optsProcNumber);
     562        2770 :                 addFamilyMember(&procedures, member);
     563        2770 :                 break;
     564         354 :             case OPCLASS_ITEM_STORAGETYPE:
     565         354 :                 if (OidIsValid(storageoid))
     566           0 :                     ereport(ERROR,
     567             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     568             :                              errmsg("storage type specified more than once")));
     569         354 :                 storageoid = typenameTypeId(NULL, item->storedtype);
     570             : 
     571             : #ifdef NOT_USED
     572             :                 /* XXX this is unnecessary given the superuser check above */
     573             :                 /* Check we have ownership of the datatype */
     574             :                 if (!object_ownercheck(TypeRelationId, storageoid, GetUserId()))
     575             :                     aclcheck_error_type(ACLCHECK_NOT_OWNER, storageoid);
     576             : #endif
     577         354 :                 break;
     578           0 :             default:
     579           0 :                 elog(ERROR, "unrecognized item type: %d", item->itemtype);
     580             :                 break;
     581             :         }
     582             :     }
     583             : 
     584             :     /*
     585             :      * If storagetype is specified, make sure it's legal.
     586             :      */
     587         556 :     if (OidIsValid(storageoid))
     588             :     {
     589             :         /* Just drop the spec if same as column datatype */
     590         354 :         if (storageoid == typeoid)
     591         152 :             storageoid = InvalidOid;
     592         202 :         else if (!amstorage)
     593           0 :             ereport(ERROR,
     594             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     595             :                      errmsg("storage type cannot be different from data type for access method \"%s\"",
     596             :                             stmt->amname)));
     597             :     }
     598             : 
     599         556 :     rel = table_open(OperatorClassRelationId, RowExclusiveLock);
     600             : 
     601             :     /*
     602             :      * Make sure there is no existing opclass of this name (this is just to
     603             :      * give a more friendly error message than "duplicate key").
     604             :      */
     605         556 :     if (SearchSysCacheExists3(CLAAMNAMENSP,
     606             :                               ObjectIdGetDatum(amoid),
     607             :                               CStringGetDatum(opcname),
     608             :                               ObjectIdGetDatum(namespaceoid)))
     609           0 :         ereport(ERROR,
     610             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     611             :                  errmsg("operator class \"%s\" for access method \"%s\" already exists",
     612             :                         opcname, stmt->amname)));
     613             : 
     614             :     /*
     615             :      * HACK: if we're trying to create btree_gist's gist_inet_ops or
     616             :      * gist_cidr_ops during a binary upgrade, avoid failure in the next stanza
     617             :      * by silently making the new opclass non-default.  Without this kluge, we
     618             :      * would fail to upgrade databases containing pre-1.9 versions of
     619             :      * contrib/btree_gist.  We can remove it sometime in the far future when
     620             :      * we don't expect any such databases to exist.  (The result of this hack
     621             :      * is that the installed version of btree_gist will approximate btree_gist
     622             :      * 1.9, how closely depending on whether it's 1.8 or something older.
     623             :      * ALTER EXTENSION UPDATE can be used to bring it up to real 1.9.)
     624             :      */
     625         556 :     if (isDefault && IsBinaryUpgrade)
     626             :     {
     627           0 :         if (amoid == GIST_AM_OID &&
     628           0 :             ((typeoid == INETOID && strcmp(opcname, "gist_inet_ops") == 0) ||
     629           0 :              (typeoid == CIDROID && strcmp(opcname, "gist_cidr_ops") == 0)))
     630           0 :             isDefault = false;
     631             :     }
     632             : 
     633             :     /*
     634             :      * If we are creating a default opclass, check there isn't one already.
     635             :      * (Note we do not restrict this test to visible opclasses; this ensures
     636             :      * that typcache.c can find unique solutions to its questions.)
     637             :      */
     638         556 :     if (isDefault)
     639             :     {
     640             :         ScanKeyData skey[1];
     641             :         SysScanDesc scan;
     642             : 
     643         426 :         ScanKeyInit(&skey[0],
     644             :                     Anum_pg_opclass_opcmethod,
     645             :                     BTEqualStrategyNumber, F_OIDEQ,
     646             :                     ObjectIdGetDatum(amoid));
     647             : 
     648         426 :         scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
     649             :                                   NULL, 1, skey);
     650             : 
     651       10378 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     652             :         {
     653        9952 :             Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
     654             : 
     655        9952 :             if (opclass->opcintype == typeoid && opclass->opcdefault)
     656           0 :                 ereport(ERROR,
     657             :                         (errcode(ERRCODE_DUPLICATE_OBJECT),
     658             :                          errmsg("could not make operator class \"%s\" be default for type %s",
     659             :                                 opcname,
     660             :                                 TypeNameToString(stmt->datatype)),
     661             :                          errdetail("Operator class \"%s\" already is the default.",
     662             :                                    NameStr(opclass->opcname))));
     663             :         }
     664             : 
     665         426 :         systable_endscan(scan);
     666             :     }
     667             : 
     668             :     /*
     669             :      * Okay, let's create the pg_opclass entry.
     670             :      */
     671         556 :     memset(values, 0, sizeof(values));
     672         556 :     memset(nulls, false, sizeof(nulls));
     673             : 
     674         556 :     opclassoid = GetNewOidWithIndex(rel, OpclassOidIndexId,
     675             :                                     Anum_pg_opclass_oid);
     676         556 :     values[Anum_pg_opclass_oid - 1] = ObjectIdGetDatum(opclassoid);
     677         556 :     values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
     678         556 :     namestrcpy(&opcName, opcname);
     679         556 :     values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
     680         556 :     values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     681         556 :     values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
     682         556 :     values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
     683         556 :     values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
     684         556 :     values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(isDefault);
     685         556 :     values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
     686             : 
     687         556 :     tup = heap_form_tuple(rel->rd_att, values, nulls);
     688             : 
     689         556 :     CatalogTupleInsert(rel, tup);
     690             : 
     691         556 :     heap_freetuple(tup);
     692             : 
     693             :     /*
     694             :      * Now that we have the opclass OID, set up default dependency info for
     695             :      * the pg_amop and pg_amproc entries.  Historically, CREATE OPERATOR CLASS
     696             :      * has created hard dependencies on the opclass, so that's what we use.
     697             :      */
     698        3110 :     foreach(l, operators)
     699             :     {
     700        2554 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
     701             : 
     702        2554 :         op->ref_is_hard = true;
     703        2554 :         op->ref_is_family = false;
     704        2554 :         op->refobjid = opclassoid;
     705             :     }
     706        3326 :     foreach(l, procedures)
     707             :     {
     708        2770 :         OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
     709             : 
     710        2770 :         proc->ref_is_hard = true;
     711        2770 :         proc->ref_is_family = false;
     712        2770 :         proc->refobjid = opclassoid;
     713             :     }
     714             : 
     715             :     /*
     716             :      * Let the index AM editorialize on the dependency choices.  It could also
     717             :      * do further validation on the operators and functions, if it likes.
     718             :      */
     719         556 :     if (amroutine->amadjustmembers)
     720         546 :         amroutine->amadjustmembers(opfamilyoid,
     721             :                                    opclassoid,
     722             :                                    operators,
     723             :                                    procedures);
     724             : 
     725             :     /*
     726             :      * Now add tuples to pg_amop and pg_amproc tying in the operators and
     727             :      * functions.  Dependencies on them are inserted, too.
     728             :      */
     729         556 :     storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
     730             :                    operators, false);
     731         556 :     storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
     732             :                     procedures, false);
     733             : 
     734             :     /* let event triggers know what happened */
     735         556 :     EventTriggerCollectCreateOpClass(stmt, opclassoid, operators, procedures);
     736             : 
     737             :     /*
     738             :      * Create dependencies for the opclass proper.  Note: we do not need a
     739             :      * dependency link to the AM, because that exists through the opfamily.
     740             :      */
     741         556 :     myself.classId = OperatorClassRelationId;
     742         556 :     myself.objectId = opclassoid;
     743         556 :     myself.objectSubId = 0;
     744             : 
     745             :     /* dependency on namespace */
     746         556 :     referenced.classId = NamespaceRelationId;
     747         556 :     referenced.objectId = namespaceoid;
     748         556 :     referenced.objectSubId = 0;
     749         556 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     750             : 
     751             :     /* dependency on opfamily */
     752         556 :     referenced.classId = OperatorFamilyRelationId;
     753         556 :     referenced.objectId = opfamilyoid;
     754         556 :     referenced.objectSubId = 0;
     755         556 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     756             : 
     757             :     /* dependency on indexed datatype */
     758         556 :     referenced.classId = TypeRelationId;
     759         556 :     referenced.objectId = typeoid;
     760         556 :     referenced.objectSubId = 0;
     761         556 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     762             : 
     763             :     /* dependency on storage datatype */
     764         556 :     if (OidIsValid(storageoid))
     765             :     {
     766         202 :         referenced.classId = TypeRelationId;
     767         202 :         referenced.objectId = storageoid;
     768         202 :         referenced.objectSubId = 0;
     769         202 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     770             :     }
     771             : 
     772             :     /* dependency on owner */
     773         556 :     recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
     774             : 
     775             :     /* dependency on extension */
     776         556 :     recordDependencyOnCurrentExtension(&myself, false);
     777             : 
     778             :     /* Post creation hook for new operator class */
     779         556 :     InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
     780             : 
     781         556 :     table_close(rel, RowExclusiveLock);
     782             : 
     783         556 :     return myself;
     784             : }
     785             : 
     786             : 
     787             : /*
     788             :  * DefineOpFamily
     789             :  *      Define a new index operator family.
     790             :  */
     791             : ObjectAddress
     792         148 : DefineOpFamily(CreateOpFamilyStmt *stmt)
     793             : {
     794             :     char       *opfname;        /* name of opfamily we're creating */
     795             :     Oid         amoid,          /* our AM's oid */
     796             :                 namespaceoid;   /* namespace to create opfamily in */
     797             :     AclResult   aclresult;
     798             : 
     799             :     /* Convert list of names to a name and namespace */
     800         148 :     namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
     801             :                                                      &opfname);
     802             : 
     803             :     /* Check we have creation rights in target namespace */
     804         148 :     aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
     805         148 :     if (aclresult != ACLCHECK_OK)
     806           0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
     807           0 :                        get_namespace_name(namespaceoid));
     808             : 
     809             :     /* Get access method OID, throwing an error if it doesn't exist. */
     810         148 :     amoid = get_index_am_oid(stmt->amname, false);
     811             : 
     812             :     /* XXX Should we make any privilege check against the AM? */
     813             : 
     814             :     /*
     815             :      * Currently, we require superuser privileges to create an opfamily. See
     816             :      * comments in DefineOpClass.
     817             :      */
     818         148 :     if (!superuser())
     819           0 :         ereport(ERROR,
     820             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     821             :                  errmsg("must be superuser to create an operator family")));
     822             : 
     823             :     /* Insert pg_opfamily catalog entry */
     824         148 :     return CreateOpFamily(stmt, opfname, namespaceoid, amoid);
     825             : }
     826             : 
     827             : 
     828             : /*
     829             :  * AlterOpFamily
     830             :  *      Add or remove operators/procedures within an existing operator family.
     831             :  *
     832             :  * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP.  Some
     833             :  * other commands called ALTER OPERATOR FAMILY exist, but go through
     834             :  * different code paths.
     835             :  */
     836             : Oid
     837         458 : AlterOpFamily(AlterOpFamilyStmt *stmt)
     838             : {
     839             :     Oid         amoid,          /* our AM's oid */
     840             :                 opfamilyoid;    /* oid of opfamily */
     841             :     int         maxOpNumber,    /* amstrategies value */
     842             :                 optsProcNumber, /* amoptsprocnum value */
     843             :                 maxProcNumber;  /* amsupport value */
     844             :     HeapTuple   tup;
     845             :     Form_pg_am  amform;
     846             :     const IndexAmRoutine *amroutine;
     847             : 
     848             :     /* Get necessary info about access method */
     849         458 :     tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
     850         458 :     if (!HeapTupleIsValid(tup))
     851           6 :         ereport(ERROR,
     852             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     853             :                  errmsg("access method \"%s\" does not exist",
     854             :                         stmt->amname)));
     855             : 
     856         452 :     amform = (Form_pg_am) GETSTRUCT(tup);
     857         452 :     amoid = amform->oid;
     858         452 :     amroutine = GetIndexAmRoutineByAmId(amoid, false);
     859         452 :     ReleaseSysCache(tup);
     860             : 
     861         452 :     maxOpNumber = amroutine->amstrategies;
     862             :     /* if amstrategies is zero, just enforce that op numbers fit in int16 */
     863         452 :     if (maxOpNumber <= 0)
     864         140 :         maxOpNumber = SHRT_MAX;
     865         452 :     maxProcNumber = amroutine->amsupport;
     866         452 :     optsProcNumber = amroutine->amoptsprocnum;
     867             : 
     868             :     /* XXX Should we make any privilege check against the AM? */
     869             : 
     870             :     /* Look up the opfamily */
     871         452 :     opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
     872             : 
     873             :     /*
     874             :      * Currently, we require superuser privileges to alter an opfamily.
     875             :      *
     876             :      * XXX re-enable NOT_USED code sections below if you remove this test.
     877             :      */
     878         446 :     if (!superuser())
     879           6 :         ereport(ERROR,
     880             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     881             :                  errmsg("must be superuser to alter an operator family")));
     882             : 
     883             :     /*
     884             :      * ADD and DROP cases need separate code from here on down.
     885             :      */
     886         440 :     if (stmt->isDrop)
     887          64 :         AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
     888             :                           maxOpNumber, maxProcNumber, stmt->items);
     889             :     else
     890         376 :         AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
     891             :                          maxOpNumber, maxProcNumber, optsProcNumber,
     892             :                          stmt->items);
     893             : 
     894         296 :     return opfamilyoid;
     895             : }
     896             : 
     897             : /*
     898             :  * ADD part of ALTER OP FAMILY
     899             :  */
     900             : static void
     901         376 : AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
     902             :                  int maxOpNumber, int maxProcNumber, int optsProcNumber,
     903             :                  List *items)
     904             : {
     905         376 :     const IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
     906             :     List       *operators;      /* OpFamilyMember list for operators */
     907             :     List       *procedures;     /* OpFamilyMember list for support procs */
     908             :     ListCell   *l;
     909             : 
     910         376 :     operators = NIL;
     911         376 :     procedures = NIL;
     912             : 
     913             :     /*
     914             :      * Scan the "items" list to obtain additional info.
     915             :      */
     916        1360 :     foreach(l, items)
     917             :     {
     918        1098 :         CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
     919             :         Oid         operOid;
     920             :         Oid         funcOid;
     921             :         Oid         sortfamilyOid;
     922             :         OpFamilyMember *member;
     923             : 
     924        1098 :         switch (item->itemtype)
     925             :         {
     926         864 :             case OPCLASS_ITEM_OPERATOR:
     927         864 :                 if (item->number <= 0 || item->number > maxOpNumber)
     928          12 :                     ereport(ERROR,
     929             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     930             :                              errmsg("invalid operator number %d,"
     931             :                                     " must be between 1 and %d",
     932             :                                     item->number, maxOpNumber)));
     933         852 :                 if (item->name->objargs != NIL)
     934         846 :                     operOid = LookupOperWithArgs(item->name, false);
     935             :                 else
     936             :                 {
     937           6 :                     ereport(ERROR,
     938             :                             (errcode(ERRCODE_SYNTAX_ERROR),
     939             :                              errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
     940             :                     operOid = InvalidOid;   /* keep compiler quiet */
     941             :                 }
     942             : 
     943         846 :                 if (item->order_family)
     944          24 :                     sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
     945             :                                                      item->order_family,
     946             :                                                      false);
     947             :                 else
     948         822 :                     sortfamilyOid = InvalidOid;
     949             : 
     950             : #ifdef NOT_USED
     951             :                 /* XXX this is unnecessary given the superuser check above */
     952             :                 /* Caller must own operator and its underlying function */
     953             :                 if (!object_ownercheck(OperatorRelationId, operOid, GetUserId()))
     954             :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_OPERATOR,
     955             :                                    get_opname(operOid));
     956             :                 funcOid = get_opcode(operOid);
     957             :                 if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
     958             :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
     959             :                                    get_func_name(funcOid));
     960             : #endif
     961             : 
     962             :                 /* Save the info */
     963         846 :                 member = palloc0_object(OpFamilyMember);
     964         846 :                 member->is_func = false;
     965         846 :                 member->object = operOid;
     966         846 :                 member->number = item->number;
     967         846 :                 member->sortfamily = sortfamilyOid;
     968             :                 /* We can set up dependency fields immediately */
     969             :                 /* Historically, ALTER ADD has created soft dependencies */
     970         846 :                 member->ref_is_hard = false;
     971         846 :                 member->ref_is_family = true;
     972         846 :                 member->refobjid = opfamilyoid;
     973         846 :                 assignOperTypes(member, amoid, InvalidOid);
     974         840 :                 addFamilyMember(&operators, member);
     975         834 :                 break;
     976         228 :             case OPCLASS_ITEM_FUNCTION:
     977         228 :                 if (item->number <= 0 || item->number > maxProcNumber)
     978          12 :                     ereport(ERROR,
     979             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     980             :                              errmsg("invalid function number %d,"
     981             :                                     " must be between 1 and %d",
     982             :                                     item->number, maxProcNumber)));
     983         216 :                 funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
     984             : #ifdef NOT_USED
     985             :                 /* XXX this is unnecessary given the superuser check above */
     986             :                 /* Caller must own function */
     987             :                 if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
     988             :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
     989             :                                    get_func_name(funcOid));
     990             : #endif
     991             : 
     992             :                 /* Save the info */
     993         210 :                 member = palloc0_object(OpFamilyMember);
     994         210 :                 member->is_func = true;
     995         210 :                 member->object = funcOid;
     996         210 :                 member->number = item->number;
     997             :                 /* We can set up dependency fields immediately */
     998             :                 /* Historically, ALTER ADD has created soft dependencies */
     999         210 :                 member->ref_is_hard = false;
    1000         210 :                 member->ref_is_family = true;
    1001         210 :                 member->refobjid = opfamilyoid;
    1002             : 
    1003             :                 /* allow overriding of the function's actual arg types */
    1004         210 :                 if (item->class_args)
    1005          74 :                     processTypesSpec(item->class_args,
    1006             :                                      &member->lefttype, &member->righttype);
    1007             : 
    1008         210 :                 assignProcTypes(member, amoid, InvalidOid, optsProcNumber);
    1009         156 :                 addFamilyMember(&procedures, member);
    1010         150 :                 break;
    1011           6 :             case OPCLASS_ITEM_STORAGETYPE:
    1012           6 :                 ereport(ERROR,
    1013             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    1014             :                          errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
    1015             :                 break;
    1016           0 :             default:
    1017           0 :                 elog(ERROR, "unrecognized item type: %d", item->itemtype);
    1018             :                 break;
    1019             :         }
    1020             :     }
    1021             : 
    1022             :     /*
    1023             :      * Let the index AM editorialize on the dependency choices.  It could also
    1024             :      * do further validation on the operators and functions, if it likes.
    1025             :      */
    1026         262 :     if (amroutine->amadjustmembers)
    1027         262 :         amroutine->amadjustmembers(opfamilyoid,
    1028             :                                    InvalidOid,  /* no specific opclass */
    1029             :                                    operators,
    1030             :                                    procedures);
    1031             : 
    1032             :     /*
    1033             :      * Add tuples to pg_amop and pg_amproc tying in the operators and
    1034             :      * functions.  Dependencies on them are inserted, too.
    1035             :      */
    1036         262 :     storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
    1037             :                    operators, true);
    1038         250 :     storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
    1039             :                     procedures, true);
    1040             : 
    1041             :     /* make information available to event triggers */
    1042         250 :     EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
    1043             :                                   operators, procedures);
    1044         250 : }
    1045             : 
    1046             : /*
    1047             :  * DROP part of ALTER OP FAMILY
    1048             :  */
    1049             : static void
    1050          64 : AlterOpFamilyDrop(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
    1051             :                   int maxOpNumber, int maxProcNumber, List *items)
    1052             : {
    1053             :     List       *operators;      /* OpFamilyMember list for operators */
    1054             :     List       *procedures;     /* OpFamilyMember list for support procs */
    1055             :     ListCell   *l;
    1056             : 
    1057          64 :     operators = NIL;
    1058          64 :     procedures = NIL;
    1059             : 
    1060             :     /*
    1061             :      * Scan the "items" list to obtain additional info.
    1062             :      */
    1063         152 :     foreach(l, items)
    1064             :     {
    1065          94 :         CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
    1066             :         Oid         lefttype,
    1067             :                     righttype;
    1068             :         OpFamilyMember *member;
    1069             : 
    1070          94 :         switch (item->itemtype)
    1071             :         {
    1072          56 :             case OPCLASS_ITEM_OPERATOR:
    1073          56 :                 if (item->number <= 0 || item->number > maxOpNumber)
    1074           0 :                     ereport(ERROR,
    1075             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1076             :                              errmsg("invalid operator number %d,"
    1077             :                                     " must be between 1 and %d",
    1078             :                                     item->number, maxOpNumber)));
    1079          56 :                 processTypesSpec(item->class_args, &lefttype, &righttype);
    1080             :                 /* Save the info */
    1081          50 :                 member = palloc0_object(OpFamilyMember);
    1082          50 :                 member->is_func = false;
    1083          50 :                 member->number = item->number;
    1084          50 :                 member->lefttype = lefttype;
    1085          50 :                 member->righttype = righttype;
    1086          50 :                 addFamilyMember(&operators, member);
    1087          50 :                 break;
    1088          38 :             case OPCLASS_ITEM_FUNCTION:
    1089          38 :                 if (item->number <= 0 || item->number > maxProcNumber)
    1090           0 :                     ereport(ERROR,
    1091             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1092             :                              errmsg("invalid function number %d,"
    1093             :                                     " must be between 1 and %d",
    1094             :                                     item->number, maxProcNumber)));
    1095          38 :                 processTypesSpec(item->class_args, &lefttype, &righttype);
    1096             :                 /* Save the info */
    1097          38 :                 member = palloc0_object(OpFamilyMember);
    1098          38 :                 member->is_func = true;
    1099          38 :                 member->number = item->number;
    1100          38 :                 member->lefttype = lefttype;
    1101          38 :                 member->righttype = righttype;
    1102          38 :                 addFamilyMember(&procedures, member);
    1103          38 :                 break;
    1104           0 :             case OPCLASS_ITEM_STORAGETYPE:
    1105             :                 /* grammar prevents this from appearing */
    1106             :             default:
    1107           0 :                 elog(ERROR, "unrecognized item type: %d", item->itemtype);
    1108             :                 break;
    1109             :         }
    1110             :     }
    1111             : 
    1112             :     /*
    1113             :      * Remove tuples from pg_amop and pg_amproc.
    1114             :      */
    1115          58 :     dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
    1116          52 :     dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
    1117             : 
    1118             :     /* make information available to event triggers */
    1119          46 :     EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
    1120             :                                   operators, procedures);
    1121          46 : }
    1122             : 
    1123             : 
    1124             : /*
    1125             :  * Deal with explicit arg types used in ALTER ADD/DROP
    1126             :  */
    1127             : static void
    1128         324 : processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
    1129             : {
    1130             :     TypeName   *typeName;
    1131             : 
    1132             :     Assert(args != NIL);
    1133             : 
    1134         324 :     typeName = (TypeName *) linitial(args);
    1135         324 :     *lefttype = typenameTypeId(NULL, typeName);
    1136             : 
    1137         324 :     if (list_length(args) > 1)
    1138             :     {
    1139         264 :         typeName = (TypeName *) lsecond(args);
    1140         264 :         *righttype = typenameTypeId(NULL, typeName);
    1141             :     }
    1142             :     else
    1143          60 :         *righttype = *lefttype;
    1144             : 
    1145         324 :     if (list_length(args) > 2)
    1146           6 :         ereport(ERROR,
    1147             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    1148             :                  errmsg("one or two argument types must be specified")));
    1149         318 : }
    1150             : 
    1151             : 
    1152             : /*
    1153             :  * Determine the lefttype/righttype to assign to an operator,
    1154             :  * and do any validity checking we can manage.
    1155             :  */
    1156             : static void
    1157        3400 : assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
    1158             : {
    1159             :     Operator    optup;
    1160             :     Form_pg_operator opform;
    1161             : 
    1162             :     /* Fetch the operator definition */
    1163        3400 :     optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
    1164        3400 :     if (!HeapTupleIsValid(optup))
    1165           0 :         elog(ERROR, "cache lookup failed for operator %u", member->object);
    1166        3400 :     opform = (Form_pg_operator) GETSTRUCT(optup);
    1167             : 
    1168             :     /*
    1169             :      * Opfamily operators must be binary.
    1170             :      */
    1171        3400 :     if (opform->oprkind != 'b')
    1172           0 :         ereport(ERROR,
    1173             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1174             :                  errmsg("index operators must be binary")));
    1175             : 
    1176        3400 :     if (OidIsValid(member->sortfamily))
    1177             :     {
    1178             :         /*
    1179             :          * Ordering op, check index supports that.  (We could perhaps also
    1180             :          * check that the operator returns a type supported by the sortfamily,
    1181             :          * but that seems more trouble than it's worth here.  If it does not,
    1182             :          * the operator will never be matchable to any ORDER BY clause, but no
    1183             :          * worse consequences can ensue.  Also, trying to check that would
    1184             :          * create an ordering hazard during dump/reload: it's possible that
    1185             :          * the family has been created but not yet populated with the required
    1186             :          * operators.)
    1187             :          */
    1188         120 :         if (!GetIndexAmRoutineByAmId(amoid, false)->amcanorderbyop)
    1189           6 :             ereport(ERROR,
    1190             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1191             :                      errmsg("access method \"%s\" does not support ordering operators",
    1192             :                             get_am_name(amoid))));
    1193             :     }
    1194             :     else
    1195             :     {
    1196             :         /*
    1197             :          * Search operators must return boolean.
    1198             :          */
    1199        3280 :         if (opform->oprresult != BOOLOID)
    1200           0 :             ereport(ERROR,
    1201             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1202             :                      errmsg("index search operators must return boolean")));
    1203             :     }
    1204             : 
    1205             :     /*
    1206             :      * If lefttype/righttype isn't specified, use the operator's input types
    1207             :      */
    1208        3394 :     if (!OidIsValid(member->lefttype))
    1209        3394 :         member->lefttype = opform->oprleft;
    1210        3394 :     if (!OidIsValid(member->righttype))
    1211        3394 :         member->righttype = opform->oprright;
    1212             : 
    1213        3394 :     ReleaseSysCache(optup);
    1214        3394 : }
    1215             : 
    1216             : /*
    1217             :  * Determine the lefttype/righttype to assign to a support procedure,
    1218             :  * and do any validity checking we can manage.
    1219             :  */
    1220             : static void
    1221        2980 : assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid,
    1222             :                 int opclassOptsProcNum)
    1223             : {
    1224             :     HeapTuple   proctup;
    1225             :     Form_pg_proc procform;
    1226             : 
    1227             :     /* Fetch the procedure definition */
    1228        2980 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
    1229        2980 :     if (!HeapTupleIsValid(proctup))
    1230           0 :         elog(ERROR, "cache lookup failed for function %u", member->object);
    1231        2980 :     procform = (Form_pg_proc) GETSTRUCT(proctup);
    1232             : 
    1233             :     /* Check the signature of the opclass options parsing function */
    1234        2980 :     if (member->number == opclassOptsProcNum)
    1235             :     {
    1236          46 :         if (OidIsValid(typeoid))
    1237             :         {
    1238           0 :             if ((OidIsValid(member->lefttype) && member->lefttype != typeoid) ||
    1239           0 :                 (OidIsValid(member->righttype) && member->righttype != typeoid))
    1240           0 :                 ereport(ERROR,
    1241             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1242             :                          errmsg("associated data types for operator class options parsing functions must match opclass input type")));
    1243             :         }
    1244             :         else
    1245             :         {
    1246          46 :             if (member->lefttype != member->righttype)
    1247           6 :                 ereport(ERROR,
    1248             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1249             :                          errmsg("left and right associated data types for operator class options parsing functions must match")));
    1250             :         }
    1251             : 
    1252          40 :         if (procform->prorettype != VOIDOID ||
    1253          34 :             procform->pronargs != 1 ||
    1254          34 :             procform->proargtypes.values[0] != INTERNALOID)
    1255           6 :             ereport(ERROR,
    1256             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1257             :                      errmsg("invalid operator class options parsing function"),
    1258             :                      errhint("Valid signature of operator class options parsing function is %s.",
    1259             :                              "(internal) RETURNS void")));
    1260             :     }
    1261             : 
    1262             :     /*
    1263             :      * Ordering comparison procs must be 2-arg procs returning int4.  Ordering
    1264             :      * sortsupport procs must take internal and return void.  Ordering
    1265             :      * in_range procs must be 5-arg procs returning bool.  Ordering equalimage
    1266             :      * procs must take 1 arg and return bool.  Hashing support proc 1 must be
    1267             :      * a 1-arg proc returning int4, while proc 2 must be a 2-arg proc
    1268             :      * returning int8. Otherwise we don't know.
    1269             :      */
    1270        2934 :     else if (GetIndexAmRoutineByAmId(amoid, false)->amcanorder)
    1271             :     {
    1272         202 :         if (member->number == BTORDER_PROC)
    1273             :         {
    1274         182 :             if (procform->pronargs != 2)
    1275           6 :                 ereport(ERROR,
    1276             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1277             :                          errmsg("ordering comparison functions must have two arguments")));
    1278         176 :             if (procform->prorettype != INT4OID)
    1279           6 :                 ereport(ERROR,
    1280             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1281             :                          errmsg("ordering comparison functions must return integer")));
    1282             : 
    1283             :             /*
    1284             :              * If lefttype/righttype isn't specified, use the proc's input
    1285             :              * types
    1286             :              */
    1287         170 :             if (!OidIsValid(member->lefttype))
    1288         168 :                 member->lefttype = procform->proargtypes.values[0];
    1289         170 :             if (!OidIsValid(member->righttype))
    1290         168 :                 member->righttype = procform->proargtypes.values[1];
    1291             :         }
    1292          20 :         else if (member->number == BTSORTSUPPORT_PROC)
    1293             :         {
    1294           4 :             if (procform->pronargs != 1 ||
    1295           4 :                 procform->proargtypes.values[0] != INTERNALOID)
    1296           0 :                 ereport(ERROR,
    1297             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1298             :                          errmsg("ordering sort support functions must accept type \"internal\"")));
    1299           4 :             if (procform->prorettype != VOIDOID)
    1300           0 :                 ereport(ERROR,
    1301             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1302             :                          errmsg("ordering sort support functions must return void")));
    1303             : 
    1304             :             /*
    1305             :              * Can't infer lefttype/righttype from proc, so use default rule
    1306             :              */
    1307             :         }
    1308          16 :         else if (member->number == BTINRANGE_PROC)
    1309             :         {
    1310           0 :             if (procform->pronargs != 5)
    1311           0 :                 ereport(ERROR,
    1312             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1313             :                          errmsg("ordering in_range functions must have five arguments")));
    1314           0 :             if (procform->prorettype != BOOLOID)
    1315           0 :                 ereport(ERROR,
    1316             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1317             :                          errmsg("ordering in_range functions must return boolean")));
    1318             : 
    1319             :             /*
    1320             :              * If lefttype/righttype isn't specified, use the proc's input
    1321             :              * types (we look at the test-value and offset arguments)
    1322             :              */
    1323           0 :             if (!OidIsValid(member->lefttype))
    1324           0 :                 member->lefttype = procform->proargtypes.values[0];
    1325           0 :             if (!OidIsValid(member->righttype))
    1326           0 :                 member->righttype = procform->proargtypes.values[2];
    1327             :         }
    1328          16 :         else if (member->number == BTEQUALIMAGE_PROC)
    1329             :         {
    1330          10 :             if (procform->pronargs != 1)
    1331           0 :                 ereport(ERROR,
    1332             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1333             :                          errmsg("ordering equal image functions must have one argument")));
    1334          10 :             if (procform->prorettype != BOOLOID)
    1335           0 :                 ereport(ERROR,
    1336             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1337             :                          errmsg("ordering equal image functions must return boolean")));
    1338             : 
    1339             :             /*
    1340             :              * pg_amproc functions are indexed by (lefttype, righttype), but
    1341             :              * an equalimage function can only be called at CREATE INDEX time.
    1342             :              * The same opclass opcintype OID is always used for lefttype and
    1343             :              * righttype.  Providing a cross-type routine isn't sensible.
    1344             :              * Reject cross-type ALTER OPERATOR FAMILY ...  ADD FUNCTION 4
    1345             :              * statements here.
    1346             :              */
    1347          10 :             if (member->lefttype != member->righttype)
    1348           6 :                 ereport(ERROR,
    1349             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1350             :                          errmsg("ordering equal image functions must not be cross-type")));
    1351             :         }
    1352           6 :         else if (member->number == BTSKIPSUPPORT_PROC)
    1353             :         {
    1354           6 :             if (procform->pronargs != 1 ||
    1355           6 :                 procform->proargtypes.values[0] != INTERNALOID)
    1356           0 :                 ereport(ERROR,
    1357             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1358             :                          errmsg("btree skip support functions must accept type \"internal\"")));
    1359           6 :             if (procform->prorettype != VOIDOID)
    1360           0 :                 ereport(ERROR,
    1361             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1362             :                          errmsg("btree skip support functions must return void")));
    1363             : 
    1364             :             /*
    1365             :              * pg_amproc functions are indexed by (lefttype, righttype), but a
    1366             :              * skip support function doesn't make sense in cross-type
    1367             :              * scenarios.  The same opclass opcintype OID is always used for
    1368             :              * lefttype and righttype.  Providing a cross-type routine isn't
    1369             :              * sensible.  Reject cross-type ALTER OPERATOR FAMILY ...  ADD
    1370             :              * FUNCTION 6 statements here.
    1371             :              */
    1372           6 :             if (member->lefttype != member->righttype)
    1373           6 :                 ereport(ERROR,
    1374             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1375             :                          errmsg("btree skip support functions must not be cross-type")));
    1376             :         }
    1377             :     }
    1378        2732 :     else if (GetIndexAmRoutineByAmId(amoid, false)->amcanhash)
    1379             :     {
    1380         120 :         if (member->number == HASHSTANDARD_PROC)
    1381             :         {
    1382          62 :             if (procform->pronargs != 1)
    1383           6 :                 ereport(ERROR,
    1384             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1385             :                          errmsg("hash function 1 must have one argument")));
    1386          56 :             if (procform->prorettype != INT4OID)
    1387           6 :                 ereport(ERROR,
    1388             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1389             :                          errmsg("hash function 1 must return integer")));
    1390             :         }
    1391          58 :         else if (member->number == HASHEXTENDED_PROC)
    1392             :         {
    1393          58 :             if (procform->pronargs != 2)
    1394           0 :                 ereport(ERROR,
    1395             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1396             :                          errmsg("hash function 2 must have two arguments")));
    1397          58 :             if (procform->prorettype != INT8OID)
    1398           0 :                 ereport(ERROR,
    1399             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1400             :                          errmsg("hash function 2 must return bigint")));
    1401             :         }
    1402             : 
    1403             :         /*
    1404             :          * If lefttype/righttype isn't specified, use the proc's input type
    1405             :          */
    1406         108 :         if (!OidIsValid(member->lefttype))
    1407         102 :             member->lefttype = procform->proargtypes.values[0];
    1408         108 :         if (!OidIsValid(member->righttype))
    1409         102 :             member->righttype = procform->proargtypes.values[0];
    1410             :     }
    1411             : 
    1412             :     /*
    1413             :      * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
    1414             :      * lefttype and righttype.  In CREATE or ALTER OPERATOR FAMILY, opcintype
    1415             :      * isn't available, so make the user specify the types.
    1416             :      */
    1417        2932 :     if (!OidIsValid(member->lefttype))
    1418        2456 :         member->lefttype = typeoid;
    1419        2932 :     if (!OidIsValid(member->righttype))
    1420        2456 :         member->righttype = typeoid;
    1421             : 
    1422        2932 :     if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
    1423           6 :         ereport(ERROR,
    1424             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1425             :                  errmsg("associated data types must be specified for index support function")));
    1426             : 
    1427        2926 :     ReleaseSysCache(proctup);
    1428        2926 : }
    1429             : 
    1430             : /*
    1431             :  * Add a new family member to the appropriate list, after checking for
    1432             :  * duplicated strategy or proc number.
    1433             :  */
    1434             : static void
    1435        6408 : addFamilyMember(List **list, OpFamilyMember *member)
    1436             : {
    1437             :     ListCell   *l;
    1438             : 
    1439       26162 :     foreach(l, *list)
    1440             :     {
    1441       19766 :         OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
    1442             : 
    1443       19766 :         if (old->number == member->number &&
    1444         390 :             old->lefttype == member->lefttype &&
    1445         390 :             old->righttype == member->righttype)
    1446             :         {
    1447          12 :             if (member->is_func)
    1448           6 :                 ereport(ERROR,
    1449             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1450             :                          errmsg("function number %d for (%s,%s) appears more than once",
    1451             :                                 member->number,
    1452             :                                 format_type_be(member->lefttype),
    1453             :                                 format_type_be(member->righttype))));
    1454             :             else
    1455           6 :                 ereport(ERROR,
    1456             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1457             :                          errmsg("operator number %d for (%s,%s) appears more than once",
    1458             :                                 member->number,
    1459             :                                 format_type_be(member->lefttype),
    1460             :                                 format_type_be(member->righttype))));
    1461             :         }
    1462             :     }
    1463        6396 :     *list = lappend(*list, member);
    1464        6396 : }
    1465             : 
    1466             : /*
    1467             :  * Dump the operators to pg_amop
    1468             :  *
    1469             :  * We also make dependency entries in pg_depend for the pg_amop entries.
    1470             :  */
    1471             : static void
    1472         818 : storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
    1473             :                List *operators, bool isAdd)
    1474             : {
    1475             :     Relation    rel;
    1476             :     Datum       values[Natts_pg_amop];
    1477             :     bool        nulls[Natts_pg_amop];
    1478             :     HeapTuple   tup;
    1479             :     Oid         entryoid;
    1480             :     ObjectAddress myself,
    1481             :                 referenced;
    1482             :     ListCell   *l;
    1483             : 
    1484         818 :     rel = table_open(AccessMethodOperatorRelationId, RowExclusiveLock);
    1485             : 
    1486        4134 :     foreach(l, operators)
    1487             :     {
    1488        3328 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
    1489             :         char        oppurpose;
    1490             : 
    1491             :         /*
    1492             :          * If adding to an existing family, check for conflict with an
    1493             :          * existing pg_amop entry (just to give a nicer error message)
    1494             :          */
    1495        4102 :         if (isAdd &&
    1496         774 :             SearchSysCacheExists4(AMOPSTRATEGY,
    1497             :                                   ObjectIdGetDatum(opfamilyoid),
    1498             :                                   ObjectIdGetDatum(op->lefttype),
    1499             :                                   ObjectIdGetDatum(op->righttype),
    1500             :                                   Int16GetDatum(op->number)))
    1501          12 :             ereport(ERROR,
    1502             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
    1503             :                      errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
    1504             :                             op->number,
    1505             :                             format_type_be(op->lefttype),
    1506             :                             format_type_be(op->righttype),
    1507             :                             NameListToString(opfamilyname))));
    1508             : 
    1509        3316 :         oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
    1510             : 
    1511             :         /* Create the pg_amop entry */
    1512        3316 :         memset(values, 0, sizeof(values));
    1513        3316 :         memset(nulls, false, sizeof(nulls));
    1514             : 
    1515        3316 :         entryoid = GetNewOidWithIndex(rel, AccessMethodOperatorOidIndexId,
    1516             :                                       Anum_pg_amop_oid);
    1517        3316 :         values[Anum_pg_amop_oid - 1] = ObjectIdGetDatum(entryoid);
    1518        3316 :         values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
    1519        3316 :         values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
    1520        3316 :         values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
    1521        3316 :         values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
    1522        3316 :         values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
    1523        3316 :         values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
    1524        3316 :         values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
    1525        3316 :         values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
    1526             : 
    1527        3316 :         tup = heap_form_tuple(rel->rd_att, values, nulls);
    1528             : 
    1529        3316 :         CatalogTupleInsert(rel, tup);
    1530             : 
    1531        3316 :         heap_freetuple(tup);
    1532             : 
    1533             :         /* Make its dependencies */
    1534        3316 :         myself.classId = AccessMethodOperatorRelationId;
    1535        3316 :         myself.objectId = entryoid;
    1536        3316 :         myself.objectSubId = 0;
    1537             : 
    1538        3316 :         referenced.classId = OperatorRelationId;
    1539        3316 :         referenced.objectId = op->object;
    1540        3316 :         referenced.objectSubId = 0;
    1541             : 
    1542             :         /* see comments in amapi.h about dependency strength */
    1543        3316 :         recordDependencyOn(&myself, &referenced,
    1544        3316 :                            op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1545             : 
    1546        3316 :         referenced.classId = op->ref_is_family ? OperatorFamilyRelationId :
    1547             :             OperatorClassRelationId;
    1548        3316 :         referenced.objectId = op->refobjid;
    1549        3316 :         referenced.objectSubId = 0;
    1550             : 
    1551        3316 :         recordDependencyOn(&myself, &referenced,
    1552        3316 :                            op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
    1553             : 
    1554        3316 :         if (typeDepNeeded(op->lefttype, op))
    1555             :         {
    1556           0 :             referenced.classId = TypeRelationId;
    1557           0 :             referenced.objectId = op->lefttype;
    1558           0 :             referenced.objectSubId = 0;
    1559             : 
    1560             :             /* see comments in amapi.h about dependency strength */
    1561           0 :             recordDependencyOn(&myself, &referenced,
    1562           0 :                                op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1563             :         }
    1564             : 
    1565        4152 :         if (op->lefttype != op->righttype &&
    1566         836 :             typeDepNeeded(op->righttype, op))
    1567             :         {
    1568           0 :             referenced.classId = TypeRelationId;
    1569           0 :             referenced.objectId = op->righttype;
    1570           0 :             referenced.objectSubId = 0;
    1571             : 
    1572             :             /* see comments in amapi.h about dependency strength */
    1573           0 :             recordDependencyOn(&myself, &referenced,
    1574           0 :                                op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1575             :         }
    1576             : 
    1577             :         /* A search operator also needs a dep on the referenced opfamily */
    1578        3316 :         if (OidIsValid(op->sortfamily))
    1579             :         {
    1580         114 :             referenced.classId = OperatorFamilyRelationId;
    1581         114 :             referenced.objectId = op->sortfamily;
    1582         114 :             referenced.objectSubId = 0;
    1583             : 
    1584         114 :             recordDependencyOn(&myself, &referenced,
    1585         114 :                                op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1586             :         }
    1587             : 
    1588             :         /* Post create hook of this access method operator */
    1589        3316 :         InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
    1590             :                                    entryoid, 0);
    1591             :     }
    1592             : 
    1593         806 :     table_close(rel, RowExclusiveLock);
    1594         806 : }
    1595             : 
    1596             : /*
    1597             :  * Dump the procedures (support routines) to pg_amproc
    1598             :  *
    1599             :  * We also make dependency entries in pg_depend for the pg_amproc entries.
    1600             :  */
    1601             : static void
    1602         806 : storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
    1603             :                 List *procedures, bool isAdd)
    1604             : {
    1605             :     Relation    rel;
    1606             :     Datum       values[Natts_pg_amproc];
    1607             :     bool        nulls[Natts_pg_amproc];
    1608             :     HeapTuple   tup;
    1609             :     Oid         entryoid;
    1610             :     ObjectAddress myself,
    1611             :                 referenced;
    1612             :     ListCell   *l;
    1613             : 
    1614         806 :     rel = table_open(AccessMethodProcedureRelationId, RowExclusiveLock);
    1615             : 
    1616        3714 :     foreach(l, procedures)
    1617             :     {
    1618        2908 :         OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
    1619             : 
    1620             :         /*
    1621             :          * If adding to an existing family, check for conflict with an
    1622             :          * existing pg_amproc entry (just to give a nicer error message)
    1623             :          */
    1624        3046 :         if (isAdd &&
    1625         138 :             SearchSysCacheExists4(AMPROCNUM,
    1626             :                                   ObjectIdGetDatum(opfamilyoid),
    1627             :                                   ObjectIdGetDatum(proc->lefttype),
    1628             :                                   ObjectIdGetDatum(proc->righttype),
    1629             :                                   Int16GetDatum(proc->number)))
    1630           0 :             ereport(ERROR,
    1631             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
    1632             :                      errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
    1633             :                             proc->number,
    1634             :                             format_type_be(proc->lefttype),
    1635             :                             format_type_be(proc->righttype),
    1636             :                             NameListToString(opfamilyname))));
    1637             : 
    1638             :         /* Create the pg_amproc entry */
    1639        2908 :         memset(values, 0, sizeof(values));
    1640        2908 :         memset(nulls, false, sizeof(nulls));
    1641             : 
    1642        2908 :         entryoid = GetNewOidWithIndex(rel, AccessMethodProcedureOidIndexId,
    1643             :                                       Anum_pg_amproc_oid);
    1644        2908 :         values[Anum_pg_amproc_oid - 1] = ObjectIdGetDatum(entryoid);
    1645        2908 :         values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
    1646        2908 :         values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
    1647        2908 :         values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
    1648        2908 :         values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
    1649        2908 :         values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
    1650             : 
    1651        2908 :         tup = heap_form_tuple(rel->rd_att, values, nulls);
    1652             : 
    1653        2908 :         CatalogTupleInsert(rel, tup);
    1654             : 
    1655        2908 :         heap_freetuple(tup);
    1656             : 
    1657             :         /* Make its dependencies */
    1658        2908 :         myself.classId = AccessMethodProcedureRelationId;
    1659        2908 :         myself.objectId = entryoid;
    1660        2908 :         myself.objectSubId = 0;
    1661             : 
    1662        2908 :         referenced.classId = ProcedureRelationId;
    1663        2908 :         referenced.objectId = proc->object;
    1664        2908 :         referenced.objectSubId = 0;
    1665             : 
    1666             :         /* see comments in amapi.h about dependency strength */
    1667        2908 :         recordDependencyOn(&myself, &referenced,
    1668        2908 :                            proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1669             : 
    1670        2908 :         referenced.classId = proc->ref_is_family ? OperatorFamilyRelationId :
    1671             :             OperatorClassRelationId;
    1672        2908 :         referenced.objectId = proc->refobjid;
    1673        2908 :         referenced.objectSubId = 0;
    1674             : 
    1675        2908 :         recordDependencyOn(&myself, &referenced,
    1676        2908 :                            proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
    1677             : 
    1678        2908 :         if (typeDepNeeded(proc->lefttype, proc))
    1679             :         {
    1680         218 :             referenced.classId = TypeRelationId;
    1681         218 :             referenced.objectId = proc->lefttype;
    1682         218 :             referenced.objectSubId = 0;
    1683             : 
    1684             :             /* see comments in amapi.h about dependency strength */
    1685         218 :             recordDependencyOn(&myself, &referenced,
    1686         218 :                                proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1687             :         }
    1688             : 
    1689        2972 :         if (proc->lefttype != proc->righttype &&
    1690          64 :             typeDepNeeded(proc->righttype, proc))
    1691             :         {
    1692           0 :             referenced.classId = TypeRelationId;
    1693           0 :             referenced.objectId = proc->righttype;
    1694           0 :             referenced.objectSubId = 0;
    1695             : 
    1696             :             /* see comments in amapi.h about dependency strength */
    1697           0 :             recordDependencyOn(&myself, &referenced,
    1698           0 :                                proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1699             :         }
    1700             : 
    1701             :         /* Post create hook of access method procedure */
    1702        2908 :         InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
    1703             :                                    entryoid, 0);
    1704             :     }
    1705             : 
    1706         806 :     table_close(rel, RowExclusiveLock);
    1707         806 : }
    1708             : 
    1709             : /*
    1710             :  * Detect whether a pg_amop or pg_amproc entry needs an explicit dependency
    1711             :  * on its lefttype or righttype.
    1712             :  *
    1713             :  * We make such a dependency unless the entry has an indirect dependency
    1714             :  * via its referenced operator or function.  That's nearly always true
    1715             :  * for operators, but might well not be true for support functions.
    1716             :  */
    1717             : static bool
    1718        7124 : typeDepNeeded(Oid typid, OpFamilyMember *member)
    1719             : {
    1720        7124 :     bool        result = true;
    1721             : 
    1722             :     /*
    1723             :      * If the type is pinned, we don't need a dependency.  This is a bit of a
    1724             :      * layering violation perhaps (recordDependencyOn would ignore the request
    1725             :      * anyway), but it's a cheap test and will frequently save a syscache
    1726             :      * lookup here.
    1727             :      */
    1728        7124 :     if (IsPinnedObject(TypeRelationId, typid))
    1729        5398 :         return false;
    1730             : 
    1731             :     /* Nope, so check the input types of the function or operator. */
    1732        1726 :     if (member->is_func)
    1733             :     {
    1734             :         Oid        *argtypes;
    1735             :         int         nargs;
    1736             : 
    1737         518 :         (void) get_func_signature(member->object, &argtypes, &nargs);
    1738        1034 :         for (int i = 0; i < nargs; i++)
    1739             :         {
    1740         816 :             if (typid == argtypes[i])
    1741             :             {
    1742         300 :                 result = false; /* match, no dependency needed */
    1743         300 :                 break;
    1744             :             }
    1745             :         }
    1746         518 :         pfree(argtypes);
    1747             :     }
    1748             :     else
    1749             :     {
    1750             :         Oid         lefttype,
    1751             :                     righttype;
    1752             : 
    1753        1208 :         op_input_types(member->object, &lefttype, &righttype);
    1754        1208 :         if (typid == lefttype || typid == righttype)
    1755        1208 :             result = false;     /* match, no dependency needed */
    1756             :     }
    1757        1726 :     return result;
    1758             : }
    1759             : 
    1760             : 
    1761             : /*
    1762             :  * Remove operator entries from an opfamily.
    1763             :  *
    1764             :  * Note: this is only allowed for "loose" members of an opfamily, hence
    1765             :  * behavior is always RESTRICT.
    1766             :  */
    1767             : static void
    1768          58 : dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
    1769             :               List *operators)
    1770             : {
    1771             :     ListCell   *l;
    1772             : 
    1773         102 :     foreach(l, operators)
    1774             :     {
    1775          50 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
    1776             :         Oid         amopid;
    1777             :         ObjectAddress object;
    1778             : 
    1779          50 :         amopid = GetSysCacheOid4(AMOPSTRATEGY, Anum_pg_amop_oid,
    1780             :                                  ObjectIdGetDatum(opfamilyoid),
    1781             :                                  ObjectIdGetDatum(op->lefttype),
    1782             :                                  ObjectIdGetDatum(op->righttype),
    1783             :                                  Int16GetDatum(op->number));
    1784          50 :         if (!OidIsValid(amopid))
    1785           6 :             ereport(ERROR,
    1786             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    1787             :                      errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
    1788             :                             op->number,
    1789             :                             format_type_be(op->lefttype),
    1790             :                             format_type_be(op->righttype),
    1791             :                             NameListToString(opfamilyname))));
    1792             : 
    1793          44 :         object.classId = AccessMethodOperatorRelationId;
    1794          44 :         object.objectId = amopid;
    1795          44 :         object.objectSubId = 0;
    1796             : 
    1797          44 :         performDeletion(&object, DROP_RESTRICT, 0);
    1798             :     }
    1799          52 : }
    1800             : 
    1801             : /*
    1802             :  * Remove procedure entries from an opfamily.
    1803             :  *
    1804             :  * Note: this is only allowed for "loose" members of an opfamily, hence
    1805             :  * behavior is always RESTRICT.
    1806             :  */
    1807             : static void
    1808          52 : dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
    1809             :                List *procedures)
    1810             : {
    1811             :     ListCell   *l;
    1812             : 
    1813          84 :     foreach(l, procedures)
    1814             :     {
    1815          38 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
    1816             :         Oid         amprocid;
    1817             :         ObjectAddress object;
    1818             : 
    1819          38 :         amprocid = GetSysCacheOid4(AMPROCNUM, Anum_pg_amproc_oid,
    1820             :                                    ObjectIdGetDatum(opfamilyoid),
    1821             :                                    ObjectIdGetDatum(op->lefttype),
    1822             :                                    ObjectIdGetDatum(op->righttype),
    1823             :                                    Int16GetDatum(op->number));
    1824          38 :         if (!OidIsValid(amprocid))
    1825           6 :             ereport(ERROR,
    1826             :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
    1827             :                      errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
    1828             :                             op->number,
    1829             :                             format_type_be(op->lefttype),
    1830             :                             format_type_be(op->righttype),
    1831             :                             NameListToString(opfamilyname))));
    1832             : 
    1833          32 :         object.classId = AccessMethodProcedureRelationId;
    1834          32 :         object.objectId = amprocid;
    1835          32 :         object.objectSubId = 0;
    1836             : 
    1837          32 :         performDeletion(&object, DROP_RESTRICT, 0);
    1838             :     }
    1839          46 : }
    1840             : 
    1841             : /*
    1842             :  * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
    1843             :  *
    1844             :  * Is there an operator class with the given name and signature already
    1845             :  * in the given namespace?  If so, raise an appropriate error message.
    1846             :  */
    1847             : void
    1848          36 : IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
    1849             :                           Oid opcnamespace)
    1850             : {
    1851             :     /* make sure the new name doesn't exist */
    1852          36 :     if (SearchSysCacheExists3(CLAAMNAMENSP,
    1853             :                               ObjectIdGetDatum(opcmethod),
    1854             :                               CStringGetDatum(opcname),
    1855             :                               ObjectIdGetDatum(opcnamespace)))
    1856          12 :         ereport(ERROR,
    1857             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    1858             :                  errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
    1859             :                         opcname,
    1860             :                         get_am_name(opcmethod),
    1861             :                         get_namespace_name(opcnamespace))));
    1862          24 : }
    1863             : 
    1864             : /*
    1865             :  * Subroutine for ALTER OPERATOR FAMILY SET SCHEMA/RENAME
    1866             :  *
    1867             :  * Is there an operator family with the given name and signature already
    1868             :  * in the given namespace?  If so, raise an appropriate error message.
    1869             :  */
    1870             : void
    1871          36 : IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
    1872             :                            Oid opfnamespace)
    1873             : {
    1874             :     /* make sure the new name doesn't exist */
    1875          36 :     if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
    1876             :                               ObjectIdGetDatum(opfmethod),
    1877             :                               CStringGetDatum(opfname),
    1878             :                               ObjectIdGetDatum(opfnamespace)))
    1879          12 :         ereport(ERROR,
    1880             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    1881             :                  errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
    1882             :                         opfname,
    1883             :                         get_am_name(opfmethod),
    1884             :                         get_namespace_name(opfnamespace))));
    1885          24 : }

Generated by: LCOV version 1.16