LCOV - code coverage report
Current view: top level - src/backend/commands - opclasscmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 89.2 % 623 556
Test Date: 2026-03-11 22:14:46 Functions: 100.0 % 21 21
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          443 : 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          443 :     DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
      89              : 
      90          443 :     if (schemaname)
      91              :     {
      92              :         /* Look in specific schema only */
      93              :         Oid         namespaceId;
      94              : 
      95           73 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
      96           70 :         if (!OidIsValid(namespaceId))
      97            3 :             htup = NULL;
      98              :         else
      99           67 :             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          370 :         Oid         opfID = OpfamilynameGetOpfid(amID, opfname);
     108              : 
     109          370 :         if (!OidIsValid(opfID))
     110            6 :             htup = NULL;
     111              :         else
     112          364 :             htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
     113              :     }
     114              : 
     115          440 :     if (!HeapTupleIsValid(htup) && !missing_ok)
     116              :     {
     117              :         HeapTuple   amtup;
     118              : 
     119            3 :         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
     120            3 :         if (!HeapTupleIsValid(amtup))
     121            0 :             elog(ERROR, "cache lookup failed for access method %u", amID);
     122            3 :         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          437 :     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          443 : get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
     140              : {
     141              :     HeapTuple   htup;
     142              :     Form_pg_opfamily opfamform;
     143              :     Oid         opfID;
     144              : 
     145          443 :     htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
     146          437 :     if (!HeapTupleIsValid(htup))
     147            6 :         return InvalidOid;
     148          431 :     opfamform = (Form_pg_opfamily) GETSTRUCT(htup);
     149          431 :     opfID = opfamform->oid;
     150          431 :     ReleaseSysCache(htup);
     151              : 
     152          431 :     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          107 : 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          107 :     DeconstructQualifiedName(opclassname, &schemaname, &opcname);
     170              : 
     171          107 :     if (schemaname)
     172              :     {
     173              :         /* Look in specific schema only */
     174              :         Oid         namespaceId;
     175              : 
     176           13 :         namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
     177           13 :         if (!OidIsValid(namespaceId))
     178            3 :             htup = NULL;
     179              :         else
     180           10 :             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           94 :         Oid         opcID = OpclassnameGetOpcid(amID, opcname);
     189              : 
     190           94 :         if (!OidIsValid(opcID))
     191            6 :             htup = NULL;
     192              :         else
     193           88 :             htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
     194              :     }
     195              : 
     196          107 :     if (!HeapTupleIsValid(htup) && !missing_ok)
     197              :     {
     198              :         HeapTuple   amtup;
     199              : 
     200            3 :         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
     201            3 :         if (!HeapTupleIsValid(amtup))
     202            0 :             elog(ERROR, "cache lookup failed for access method %u", amID);
     203            3 :         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          104 :     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          107 : get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
     221              : {
     222              :     HeapTuple   htup;
     223              :     Form_pg_opclass opcform;
     224              :     Oid         opcID;
     225              : 
     226          107 :     htup = OpClassCacheLookup(amID, opclassname, missing_ok);
     227          104 :     if (!HeapTupleIsValid(htup))
     228            6 :         return InvalidOid;
     229           98 :     opcform = (Form_pg_opclass) GETSTRUCT(htup);
     230           98 :     opcID = opcform->oid;
     231           98 :     ReleaseSysCache(htup);
     232              : 
     233           98 :     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          325 : 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          325 :     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          325 :     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          325 :     memset(values, 0, sizeof(values));
     274          325 :     memset(nulls, false, sizeof(nulls));
     275              : 
     276          325 :     opfamilyoid = GetNewOidWithIndex(rel, OpfamilyOidIndexId,
     277              :                                      Anum_pg_opfamily_oid);
     278          325 :     values[Anum_pg_opfamily_oid - 1] = ObjectIdGetDatum(opfamilyoid);
     279          325 :     values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
     280          325 :     namestrcpy(&opfName, opfname);
     281          325 :     values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
     282          325 :     values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     283          325 :     values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
     284              : 
     285          325 :     tup = heap_form_tuple(rel->rd_att, values, nulls);
     286              : 
     287          325 :     CatalogTupleInsert(rel, tup);
     288              : 
     289          325 :     heap_freetuple(tup);
     290              : 
     291              :     /*
     292              :      * Create dependencies for the opfamily proper.
     293              :      */
     294          325 :     myself.classId = OperatorFamilyRelationId;
     295          325 :     myself.objectId = opfamilyoid;
     296          325 :     myself.objectSubId = 0;
     297              : 
     298              :     /* dependency on access method */
     299          325 :     referenced.classId = AccessMethodRelationId;
     300          325 :     referenced.objectId = amoid;
     301          325 :     referenced.objectSubId = 0;
     302          325 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     303              : 
     304              :     /* dependency on namespace */
     305          325 :     referenced.classId = NamespaceRelationId;
     306          325 :     referenced.objectId = namespaceoid;
     307          325 :     referenced.objectSubId = 0;
     308          325 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     309              : 
     310              :     /* dependency on owner */
     311          325 :     recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
     312              : 
     313              :     /* dependency on extension */
     314          325 :     recordDependencyOnCurrentExtension(&myself, false);
     315              : 
     316              :     /* Report the new operator family to possibly interested event triggers */
     317          325 :     EventTriggerCollectSimpleCommand(myself, InvalidObjectAddress,
     318              :                                      (Node *) stmt);
     319              : 
     320              :     /* Post creation hook for new operator family */
     321          325 :     InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
     322              : 
     323          325 :     table_close(rel, RowExclusiveLock);
     324              : 
     325          325 :     return myself;
     326              : }
     327              : 
     328              : /*
     329              :  * DefineOpClass
     330              :  *      Define a new index operator class.
     331              :  */
     332              : ObjectAddress
     333          279 : 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          279 :     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          279 :     namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
     363              :                                                      &opcname);
     364              : 
     365              :     /* Check we have creation rights in target namespace */
     366          279 :     aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
     367          279 :     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          279 :     tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
     373          279 :     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          279 :     amform = (Form_pg_am) GETSTRUCT(tup);
     380          279 :     amoid = amform->oid;
     381          279 :     amroutine = GetIndexAmRoutineByAmId(amoid, false);
     382          279 :     ReleaseSysCache(tup);
     383              : 
     384          279 :     maxOpNumber = amroutine->amstrategies;
     385              :     /* if amstrategies is zero, just enforce that op numbers fit in int16 */
     386          279 :     if (maxOpNumber <= 0)
     387          170 :         maxOpNumber = SHRT_MAX;
     388          279 :     maxProcNumber = amroutine->amsupport;
     389          279 :     optsProcNumber = amroutine->amoptsprocnum;
     390          279 :     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          279 :     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          279 :     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          279 :     if (stmt->opfamilyname)
     434              :     {
     435           22 :         opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
     436              :     }
     437              :     else
     438              :     {
     439              :         /* Lookup existing family of same name and namespace */
     440          257 :         tup = SearchSysCache3(OPFAMILYAMNAMENSP,
     441              :                               ObjectIdGetDatum(amoid),
     442              :                               PointerGetDatum(opcname),
     443              :                               ObjectIdGetDatum(namespaceoid));
     444          257 :         if (HeapTupleIsValid(tup))
     445              :         {
     446            6 :             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            6 :             ReleaseSysCache(tup);
     453              :         }
     454              :         else
     455              :         {
     456              :             CreateOpFamilyStmt *opfstmt;
     457              :             ObjectAddress tmpAddr;
     458              : 
     459          251 :             opfstmt = makeNode(CreateOpFamilyStmt);
     460          251 :             opfstmt->opfamilyname = stmt->opclassname;
     461          251 :             opfstmt->amname = stmt->amname;
     462              : 
     463              :             /*
     464              :              * Create it ... again no need for more permissions ...
     465              :              */
     466          251 :             tmpAddr = CreateOpFamily(opfstmt, opcname, namespaceoid, amoid);
     467          251 :             opfamilyoid = tmpAddr.objectId;
     468              :         }
     469              :     }
     470              : 
     471          279 :     operators = NIL;
     472          279 :     procedures = NIL;
     473              : 
     474              :     /* Storage datatype is optional */
     475          279 :     storageoid = InvalidOid;
     476              : 
     477              :     /*
     478              :      * Scan the "items" list to obtain additional info.
     479              :      */
     480         3124 :     foreach(l, stmt->items)
     481              :     {
     482         2845 :         CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
     483              :         Oid         operOid;
     484              :         Oid         funcOid;
     485              :         Oid         sortfamilyOid;
     486              :         OpFamilyMember *member;
     487              : 
     488         2845 :         switch (item->itemtype)
     489              :         {
     490         1282 :             case OPCLASS_ITEM_OPERATOR:
     491         1282 :                 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         1282 :                 if (item->name->objargs != NIL)
     498          239 :                     operOid = LookupOperWithArgs(item->name, false);
     499              :                 else
     500              :                 {
     501              :                     /* Default to binary op on input datatype */
     502         1043 :                     operOid = LookupOperName(NULL, item->name->objname,
     503              :                                              typeoid, typeoid,
     504              :                                              false, -1);
     505              :                 }
     506              : 
     507         1282 :                 if (item->order_family)
     508           48 :                     sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
     509              :                                                      item->order_family,
     510              :                                                      false);
     511              :                 else
     512         1234 :                     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         1282 :                 member = palloc0_object(OpFamilyMember);
     528         1282 :                 member->is_func = false;
     529         1282 :                 member->object = operOid;
     530         1282 :                 member->number = item->number;
     531         1282 :                 member->sortfamily = sortfamilyOid;
     532         1282 :                 assignOperTypes(member, amoid, typeoid);
     533         1282 :                 addFamilyMember(&operators, member);
     534         1282 :                 break;
     535         1386 :             case OPCLASS_ITEM_FUNCTION:
     536         1386 :                 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         1386 :                 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         1386 :                 member = palloc0_object(OpFamilyMember);
     552         1386 :                 member->is_func = true;
     553         1386 :                 member->object = funcOid;
     554         1386 :                 member->number = item->number;
     555              : 
     556              :                 /* allow overriding of the function's actual arg types */
     557         1386 :                 if (item->class_args)
     558           78 :                     processTypesSpec(item->class_args,
     559              :                                      &member->lefttype, &member->righttype);
     560              : 
     561         1386 :                 assignProcTypes(member, amoid, typeoid, optsProcNumber);
     562         1386 :                 addFamilyMember(&procedures, member);
     563         1386 :                 break;
     564          177 :             case OPCLASS_ITEM_STORAGETYPE:
     565          177 :                 if (OidIsValid(storageoid))
     566            0 :                     ereport(ERROR,
     567              :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     568              :                              errmsg("storage type specified more than once")));
     569          177 :                 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          177 :                 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          279 :     if (OidIsValid(storageoid))
     588              :     {
     589              :         /* Just drop the spec if same as column datatype */
     590          177 :         if (storageoid == typeoid)
     591           76 :             storageoid = InvalidOid;
     592          101 :         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          279 :     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          279 :     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          279 :     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          279 :     if (isDefault)
     639              :     {
     640              :         ScanKeyData skey[1];
     641              :         SysScanDesc scan;
     642              : 
     643          214 :         ScanKeyInit(&skey[0],
     644              :                     Anum_pg_opclass_opcmethod,
     645              :                     BTEqualStrategyNumber, F_OIDEQ,
     646              :                     ObjectIdGetDatum(amoid));
     647              : 
     648          214 :         scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
     649              :                                   NULL, 1, skey);
     650              : 
     651         5235 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     652              :         {
     653         5021 :             Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
     654              : 
     655         5021 :             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          214 :         systable_endscan(scan);
     666              :     }
     667              : 
     668              :     /*
     669              :      * Okay, let's create the pg_opclass entry.
     670              :      */
     671          279 :     memset(values, 0, sizeof(values));
     672          279 :     memset(nulls, false, sizeof(nulls));
     673              : 
     674          279 :     opclassoid = GetNewOidWithIndex(rel, OpclassOidIndexId,
     675              :                                     Anum_pg_opclass_oid);
     676          279 :     values[Anum_pg_opclass_oid - 1] = ObjectIdGetDatum(opclassoid);
     677          279 :     values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
     678          279 :     namestrcpy(&opcName, opcname);
     679          279 :     values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
     680          279 :     values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
     681          279 :     values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
     682          279 :     values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
     683          279 :     values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
     684          279 :     values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(isDefault);
     685          279 :     values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
     686              : 
     687          279 :     tup = heap_form_tuple(rel->rd_att, values, nulls);
     688              : 
     689          279 :     CatalogTupleInsert(rel, tup);
     690              : 
     691          279 :     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         1561 :     foreach(l, operators)
     699              :     {
     700         1282 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
     701              : 
     702         1282 :         op->ref_is_hard = true;
     703         1282 :         op->ref_is_family = false;
     704         1282 :         op->refobjid = opclassoid;
     705              :     }
     706         1665 :     foreach(l, procedures)
     707              :     {
     708         1386 :         OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
     709              : 
     710         1386 :         proc->ref_is_hard = true;
     711         1386 :         proc->ref_is_family = false;
     712         1386 :         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          279 :     if (amroutine->amadjustmembers)
     720          274 :         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          279 :     storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
     730              :                    operators, false);
     731          279 :     storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
     732              :                     procedures, false);
     733              : 
     734              :     /* let event triggers know what happened */
     735          279 :     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          279 :     myself.classId = OperatorClassRelationId;
     742          279 :     myself.objectId = opclassoid;
     743          279 :     myself.objectSubId = 0;
     744              : 
     745              :     /* dependency on namespace */
     746          279 :     referenced.classId = NamespaceRelationId;
     747          279 :     referenced.objectId = namespaceoid;
     748          279 :     referenced.objectSubId = 0;
     749          279 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     750              : 
     751              :     /* dependency on opfamily */
     752          279 :     referenced.classId = OperatorFamilyRelationId;
     753          279 :     referenced.objectId = opfamilyoid;
     754          279 :     referenced.objectSubId = 0;
     755          279 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
     756              : 
     757              :     /* dependency on indexed datatype */
     758          279 :     referenced.classId = TypeRelationId;
     759          279 :     referenced.objectId = typeoid;
     760          279 :     referenced.objectSubId = 0;
     761          279 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     762              : 
     763              :     /* dependency on storage datatype */
     764          279 :     if (OidIsValid(storageoid))
     765              :     {
     766          101 :         referenced.classId = TypeRelationId;
     767          101 :         referenced.objectId = storageoid;
     768          101 :         referenced.objectSubId = 0;
     769          101 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
     770              :     }
     771              : 
     772              :     /* dependency on owner */
     773          279 :     recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
     774              : 
     775              :     /* dependency on extension */
     776          279 :     recordDependencyOnCurrentExtension(&myself, false);
     777              : 
     778              :     /* Post creation hook for new operator class */
     779          279 :     InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
     780              : 
     781          279 :     table_close(rel, RowExclusiveLock);
     782              : 
     783          279 :     return myself;
     784              : }
     785              : 
     786              : 
     787              : /*
     788              :  * DefineOpFamily
     789              :  *      Define a new index operator family.
     790              :  */
     791              : ObjectAddress
     792           74 : 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           74 :     namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
     801              :                                                      &opfname);
     802              : 
     803              :     /* Check we have creation rights in target namespace */
     804           74 :     aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
     805           74 :     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           74 :     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           74 :     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           74 :     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          229 : 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          229 :     tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
     850          229 :     if (!HeapTupleIsValid(tup))
     851            3 :         ereport(ERROR,
     852              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     853              :                  errmsg("access method \"%s\" does not exist",
     854              :                         stmt->amname)));
     855              : 
     856          226 :     amform = (Form_pg_am) GETSTRUCT(tup);
     857          226 :     amoid = amform->oid;
     858          226 :     amroutine = GetIndexAmRoutineByAmId(amoid, false);
     859          226 :     ReleaseSysCache(tup);
     860              : 
     861          226 :     maxOpNumber = amroutine->amstrategies;
     862              :     /* if amstrategies is zero, just enforce that op numbers fit in int16 */
     863          226 :     if (maxOpNumber <= 0)
     864           70 :         maxOpNumber = SHRT_MAX;
     865          226 :     maxProcNumber = amroutine->amsupport;
     866          226 :     optsProcNumber = amroutine->amoptsprocnum;
     867              : 
     868              :     /* XXX Should we make any privilege check against the AM? */
     869              : 
     870              :     /* Look up the opfamily */
     871          226 :     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          223 :     if (!superuser())
     879            3 :         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          220 :     if (stmt->isDrop)
     887           32 :         AlterOpFamilyDrop(stmt, amoid, opfamilyoid,
     888              :                           maxOpNumber, maxProcNumber, stmt->items);
     889              :     else
     890          188 :         AlterOpFamilyAdd(stmt, amoid, opfamilyoid,
     891              :                          maxOpNumber, maxProcNumber, optsProcNumber,
     892              :                          stmt->items);
     893              : 
     894          148 :     return opfamilyoid;
     895              : }
     896              : 
     897              : /*
     898              :  * ADD part of ALTER OP FAMILY
     899              :  */
     900              : static void
     901          188 : AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
     902              :                  int maxOpNumber, int maxProcNumber, int optsProcNumber,
     903              :                  List *items)
     904              : {
     905          188 :     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          188 :     operators = NIL;
     911          188 :     procedures = NIL;
     912              : 
     913              :     /*
     914              :      * Scan the "items" list to obtain additional info.
     915              :      */
     916          680 :     foreach(l, items)
     917              :     {
     918          549 :         CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
     919              :         Oid         operOid;
     920              :         Oid         funcOid;
     921              :         Oid         sortfamilyOid;
     922              :         OpFamilyMember *member;
     923              : 
     924          549 :         switch (item->itemtype)
     925              :         {
     926          432 :             case OPCLASS_ITEM_OPERATOR:
     927          432 :                 if (item->number <= 0 || item->number > maxOpNumber)
     928            6 :                     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          426 :                 if (item->name->objargs != NIL)
     934          423 :                     operOid = LookupOperWithArgs(item->name, false);
     935              :                 else
     936              :                 {
     937            3 :                     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          423 :                 if (item->order_family)
     944           12 :                     sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
     945              :                                                      item->order_family,
     946              :                                                      false);
     947              :                 else
     948          411 :                     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          423 :                 member = palloc0_object(OpFamilyMember);
     964          423 :                 member->is_func = false;
     965          423 :                 member->object = operOid;
     966          423 :                 member->number = item->number;
     967          423 :                 member->sortfamily = sortfamilyOid;
     968              :                 /* We can set up dependency fields immediately */
     969              :                 /* Historically, ALTER ADD has created soft dependencies */
     970          423 :                 member->ref_is_hard = false;
     971          423 :                 member->ref_is_family = true;
     972          423 :                 member->refobjid = opfamilyoid;
     973          423 :                 assignOperTypes(member, amoid, InvalidOid);
     974          420 :                 addFamilyMember(&operators, member);
     975          417 :                 break;
     976          114 :             case OPCLASS_ITEM_FUNCTION:
     977          114 :                 if (item->number <= 0 || item->number > maxProcNumber)
     978            6 :                     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          108 :                 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          105 :                 member = palloc0_object(OpFamilyMember);
     994          105 :                 member->is_func = true;
     995          105 :                 member->object = funcOid;
     996          105 :                 member->number = item->number;
     997              :                 /* We can set up dependency fields immediately */
     998              :                 /* Historically, ALTER ADD has created soft dependencies */
     999          105 :                 member->ref_is_hard = false;
    1000          105 :                 member->ref_is_family = true;
    1001          105 :                 member->refobjid = opfamilyoid;
    1002              : 
    1003              :                 /* allow overriding of the function's actual arg types */
    1004          105 :                 if (item->class_args)
    1005           37 :                     processTypesSpec(item->class_args,
    1006              :                                      &member->lefttype, &member->righttype);
    1007              : 
    1008          105 :                 assignProcTypes(member, amoid, InvalidOid, optsProcNumber);
    1009           78 :                 addFamilyMember(&procedures, member);
    1010           75 :                 break;
    1011            3 :             case OPCLASS_ITEM_STORAGETYPE:
    1012            3 :                 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          131 :     if (amroutine->amadjustmembers)
    1027          131 :         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          131 :     storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
    1037              :                    operators, true);
    1038          125 :     storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
    1039              :                     procedures, true);
    1040              : 
    1041              :     /* make information available to event triggers */
    1042          125 :     EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
    1043              :                                   operators, procedures);
    1044          125 : }
    1045              : 
    1046              : /*
    1047              :  * DROP part of ALTER OP FAMILY
    1048              :  */
    1049              : static void
    1050           32 : 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           32 :     operators = NIL;
    1058           32 :     procedures = NIL;
    1059              : 
    1060              :     /*
    1061              :      * Scan the "items" list to obtain additional info.
    1062              :      */
    1063           76 :     foreach(l, items)
    1064              :     {
    1065           47 :         CreateOpClassItem *item = lfirst_node(CreateOpClassItem, l);
    1066              :         Oid         lefttype,
    1067              :                     righttype;
    1068              :         OpFamilyMember *member;
    1069              : 
    1070           47 :         switch (item->itemtype)
    1071              :         {
    1072           28 :             case OPCLASS_ITEM_OPERATOR:
    1073           28 :                 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           28 :                 processTypesSpec(item->class_args, &lefttype, &righttype);
    1080              :                 /* Save the info */
    1081           25 :                 member = palloc0_object(OpFamilyMember);
    1082           25 :                 member->is_func = false;
    1083           25 :                 member->number = item->number;
    1084           25 :                 member->lefttype = lefttype;
    1085           25 :                 member->righttype = righttype;
    1086           25 :                 addFamilyMember(&operators, member);
    1087           25 :                 break;
    1088           19 :             case OPCLASS_ITEM_FUNCTION:
    1089           19 :                 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           19 :                 processTypesSpec(item->class_args, &lefttype, &righttype);
    1096              :                 /* Save the info */
    1097           19 :                 member = palloc0_object(OpFamilyMember);
    1098           19 :                 member->is_func = true;
    1099           19 :                 member->number = item->number;
    1100           19 :                 member->lefttype = lefttype;
    1101           19 :                 member->righttype = righttype;
    1102           19 :                 addFamilyMember(&procedures, member);
    1103           19 :                 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           29 :     dropOperators(stmt->opfamilyname, amoid, opfamilyoid, operators);
    1116           26 :     dropProcedures(stmt->opfamilyname, amoid, opfamilyoid, procedures);
    1117              : 
    1118              :     /* make information available to event triggers */
    1119           23 :     EventTriggerCollectAlterOpFam(stmt, opfamilyoid,
    1120              :                                   operators, procedures);
    1121           23 : }
    1122              : 
    1123              : 
    1124              : /*
    1125              :  * Deal with explicit arg types used in ALTER ADD/DROP
    1126              :  */
    1127              : static void
    1128          162 : processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
    1129              : {
    1130              :     TypeName   *typeName;
    1131              : 
    1132              :     Assert(args != NIL);
    1133              : 
    1134          162 :     typeName = (TypeName *) linitial(args);
    1135          162 :     *lefttype = typenameTypeId(NULL, typeName);
    1136              : 
    1137          162 :     if (list_length(args) > 1)
    1138              :     {
    1139          132 :         typeName = (TypeName *) lsecond(args);
    1140          132 :         *righttype = typenameTypeId(NULL, typeName);
    1141              :     }
    1142              :     else
    1143           30 :         *righttype = *lefttype;
    1144              : 
    1145          162 :     if (list_length(args) > 2)
    1146            3 :         ereport(ERROR,
    1147              :                 (errcode(ERRCODE_SYNTAX_ERROR),
    1148              :                  errmsg("one or two argument types must be specified")));
    1149          159 : }
    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         1705 : assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
    1158              : {
    1159              :     Operator    optup;
    1160              :     Form_pg_operator opform;
    1161              : 
    1162              :     /* Fetch the operator definition */
    1163         1705 :     optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
    1164         1705 :     if (!HeapTupleIsValid(optup))
    1165            0 :         elog(ERROR, "cache lookup failed for operator %u", member->object);
    1166         1705 :     opform = (Form_pg_operator) GETSTRUCT(optup);
    1167              : 
    1168              :     /*
    1169              :      * Opfamily operators must be binary.
    1170              :      */
    1171         1705 :     if (opform->oprkind != 'b')
    1172            0 :         ereport(ERROR,
    1173              :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1174              :                  errmsg("index operators must be binary")));
    1175              : 
    1176         1705 :     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           60 :         if (!GetIndexAmRoutineByAmId(amoid, false)->amcanorderbyop)
    1189            3 :             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         1645 :         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         1702 :     if (!OidIsValid(member->lefttype))
    1209         1702 :         member->lefttype = opform->oprleft;
    1210         1702 :     if (!OidIsValid(member->righttype))
    1211         1702 :         member->righttype = opform->oprright;
    1212              : 
    1213         1702 :     ReleaseSysCache(optup);
    1214         1702 : }
    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         1491 : 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         1491 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
    1229         1491 :     if (!HeapTupleIsValid(proctup))
    1230            0 :         elog(ERROR, "cache lookup failed for function %u", member->object);
    1231         1491 :     procform = (Form_pg_proc) GETSTRUCT(proctup);
    1232              : 
    1233              :     /* Check the signature of the opclass options parsing function */
    1234         1491 :     if (member->number == opclassOptsProcNum)
    1235              :     {
    1236           23 :         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           23 :             if (member->lefttype != member->righttype)
    1247            3 :                 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           20 :         if (procform->prorettype != VOIDOID ||
    1253           17 :             procform->pronargs != 1 ||
    1254           17 :             procform->proargtypes.values[0] != INTERNALOID)
    1255            3 :             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         1468 :     else if (GetIndexAmRoutineByAmId(amoid, false)->amcanorder)
    1271              :     {
    1272          102 :         if (member->number == BTORDER_PROC)
    1273              :         {
    1274           92 :             if (procform->pronargs != 2)
    1275            3 :                 ereport(ERROR,
    1276              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1277              :                          errmsg("ordering comparison functions must have two arguments")));
    1278           89 :             if (procform->prorettype != INT4OID)
    1279            3 :                 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           86 :             if (!OidIsValid(member->lefttype))
    1288           85 :                 member->lefttype = procform->proargtypes.values[0];
    1289           86 :             if (!OidIsValid(member->righttype))
    1290           85 :                 member->righttype = procform->proargtypes.values[1];
    1291              :         }
    1292           10 :         else if (member->number == BTSORTSUPPORT_PROC)
    1293              :         {
    1294            2 :             if (procform->pronargs != 1 ||
    1295            2 :                 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            2 :             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            8 :         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            8 :         else if (member->number == BTEQUALIMAGE_PROC)
    1329              :         {
    1330            5 :             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            5 :             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            5 :             if (member->lefttype != member->righttype)
    1348            3 :                 ereport(ERROR,
    1349              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1350              :                          errmsg("ordering equal image functions must not be cross-type")));
    1351              :         }
    1352            3 :         else if (member->number == BTSKIPSUPPORT_PROC)
    1353              :         {
    1354            3 :             if (procform->pronargs != 1 ||
    1355            3 :                 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            3 :             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            3 :             if (member->lefttype != member->righttype)
    1373            3 :                 ereport(ERROR,
    1374              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1375              :                          errmsg("btree skip support functions must not be cross-type")));
    1376              :         }
    1377              :     }
    1378         1366 :     else if (GetIndexAmRoutineByAmId(amoid, false)->amcanhash)
    1379              :     {
    1380           60 :         if (member->number == HASHSTANDARD_PROC)
    1381              :         {
    1382           31 :             if (procform->pronargs != 1)
    1383            3 :                 ereport(ERROR,
    1384              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1385              :                          errmsg("hash function 1 must have one argument")));
    1386           28 :             if (procform->prorettype != INT4OID)
    1387            3 :                 ereport(ERROR,
    1388              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1389              :                          errmsg("hash function 1 must return integer")));
    1390              :         }
    1391           29 :         else if (member->number == HASHEXTENDED_PROC)
    1392              :         {
    1393           29 :             if (procform->pronargs != 2)
    1394            0 :                 ereport(ERROR,
    1395              :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1396              :                          errmsg("hash function 2 must have two arguments")));
    1397           29 :             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           54 :         if (!OidIsValid(member->lefttype))
    1407           51 :             member->lefttype = procform->proargtypes.values[0];
    1408           54 :         if (!OidIsValid(member->righttype))
    1409           51 :             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         1467 :     if (!OidIsValid(member->lefttype))
    1418         1228 :         member->lefttype = typeoid;
    1419         1467 :     if (!OidIsValid(member->righttype))
    1420         1228 :         member->righttype = typeoid;
    1421              : 
    1422         1467 :     if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
    1423            3 :         ereport(ERROR,
    1424              :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1425              :                  errmsg("associated data types must be specified for index support function")));
    1426              : 
    1427         1464 :     ReleaseSysCache(proctup);
    1428         1464 : }
    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         3210 : addFamilyMember(List **list, OpFamilyMember *member)
    1436              : {
    1437              :     ListCell   *l;
    1438              : 
    1439        13097 :     foreach(l, *list)
    1440              :     {
    1441         9893 :         OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
    1442              : 
    1443         9893 :         if (old->number == member->number &&
    1444          195 :             old->lefttype == member->lefttype &&
    1445          195 :             old->righttype == member->righttype)
    1446              :         {
    1447            6 :             if (member->is_func)
    1448            3 :                 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            3 :                 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         3204 :     *list = lappend(*list, member);
    1464         3204 : }
    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          410 : 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          410 :     rel = table_open(AccessMethodOperatorRelationId, RowExclusiveLock);
    1485              : 
    1486         2073 :     foreach(l, operators)
    1487              :     {
    1488         1669 :         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         2056 :         if (isAdd &&
    1496          387 :             SearchSysCacheExists4(AMOPSTRATEGY,
    1497              :                                   ObjectIdGetDatum(opfamilyoid),
    1498              :                                   ObjectIdGetDatum(op->lefttype),
    1499              :                                   ObjectIdGetDatum(op->righttype),
    1500              :                                   Int16GetDatum(op->number)))
    1501            6 :             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         1663 :         oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
    1510              : 
    1511              :         /* Create the pg_amop entry */
    1512         1663 :         memset(values, 0, sizeof(values));
    1513         1663 :         memset(nulls, false, sizeof(nulls));
    1514              : 
    1515         1663 :         entryoid = GetNewOidWithIndex(rel, AccessMethodOperatorOidIndexId,
    1516              :                                       Anum_pg_amop_oid);
    1517         1663 :         values[Anum_pg_amop_oid - 1] = ObjectIdGetDatum(entryoid);
    1518         1663 :         values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
    1519         1663 :         values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
    1520         1663 :         values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
    1521         1663 :         values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
    1522         1663 :         values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
    1523         1663 :         values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
    1524         1663 :         values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
    1525         1663 :         values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
    1526              : 
    1527         1663 :         tup = heap_form_tuple(rel->rd_att, values, nulls);
    1528              : 
    1529         1663 :         CatalogTupleInsert(rel, tup);
    1530              : 
    1531         1663 :         heap_freetuple(tup);
    1532              : 
    1533              :         /* Make its dependencies */
    1534         1663 :         myself.classId = AccessMethodOperatorRelationId;
    1535         1663 :         myself.objectId = entryoid;
    1536         1663 :         myself.objectSubId = 0;
    1537              : 
    1538         1663 :         referenced.classId = OperatorRelationId;
    1539         1663 :         referenced.objectId = op->object;
    1540         1663 :         referenced.objectSubId = 0;
    1541              : 
    1542              :         /* see comments in amapi.h about dependency strength */
    1543         1663 :         recordDependencyOn(&myself, &referenced,
    1544         1663 :                            op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1545              : 
    1546         1663 :         referenced.classId = op->ref_is_family ? OperatorFamilyRelationId :
    1547              :             OperatorClassRelationId;
    1548         1663 :         referenced.objectId = op->refobjid;
    1549         1663 :         referenced.objectSubId = 0;
    1550              : 
    1551         1663 :         recordDependencyOn(&myself, &referenced,
    1552         1663 :                            op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
    1553              : 
    1554         1663 :         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         2081 :         if (op->lefttype != op->righttype &&
    1566          418 :             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         1663 :         if (OidIsValid(op->sortfamily))
    1579              :         {
    1580           57 :             referenced.classId = OperatorFamilyRelationId;
    1581           57 :             referenced.objectId = op->sortfamily;
    1582           57 :             referenced.objectSubId = 0;
    1583              : 
    1584           57 :             recordDependencyOn(&myself, &referenced,
    1585           57 :                                op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1586              :         }
    1587              : 
    1588              :         /* Post create hook of this access method operator */
    1589         1663 :         InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
    1590              :                                    entryoid, 0);
    1591              :     }
    1592              : 
    1593          404 :     table_close(rel, RowExclusiveLock);
    1594          404 : }
    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          404 : 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          404 :     rel = table_open(AccessMethodProcedureRelationId, RowExclusiveLock);
    1615              : 
    1616         1859 :     foreach(l, procedures)
    1617              :     {
    1618         1455 :         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         1524 :         if (isAdd &&
    1625           69 :             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         1455 :         memset(values, 0, sizeof(values));
    1640         1455 :         memset(nulls, false, sizeof(nulls));
    1641              : 
    1642         1455 :         entryoid = GetNewOidWithIndex(rel, AccessMethodProcedureOidIndexId,
    1643              :                                       Anum_pg_amproc_oid);
    1644         1455 :         values[Anum_pg_amproc_oid - 1] = ObjectIdGetDatum(entryoid);
    1645         1455 :         values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
    1646         1455 :         values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
    1647         1455 :         values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
    1648         1455 :         values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
    1649         1455 :         values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
    1650              : 
    1651         1455 :         tup = heap_form_tuple(rel->rd_att, values, nulls);
    1652              : 
    1653         1455 :         CatalogTupleInsert(rel, tup);
    1654              : 
    1655         1455 :         heap_freetuple(tup);
    1656              : 
    1657              :         /* Make its dependencies */
    1658         1455 :         myself.classId = AccessMethodProcedureRelationId;
    1659         1455 :         myself.objectId = entryoid;
    1660         1455 :         myself.objectSubId = 0;
    1661              : 
    1662         1455 :         referenced.classId = ProcedureRelationId;
    1663         1455 :         referenced.objectId = proc->object;
    1664         1455 :         referenced.objectSubId = 0;
    1665              : 
    1666              :         /* see comments in amapi.h about dependency strength */
    1667         1455 :         recordDependencyOn(&myself, &referenced,
    1668         1455 :                            proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1669              : 
    1670         1455 :         referenced.classId = proc->ref_is_family ? OperatorFamilyRelationId :
    1671              :             OperatorClassRelationId;
    1672         1455 :         referenced.objectId = proc->refobjid;
    1673         1455 :         referenced.objectSubId = 0;
    1674              : 
    1675         1455 :         recordDependencyOn(&myself, &referenced,
    1676         1455 :                            proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
    1677              : 
    1678         1455 :         if (typeDepNeeded(proc->lefttype, proc))
    1679              :         {
    1680          109 :             referenced.classId = TypeRelationId;
    1681          109 :             referenced.objectId = proc->lefttype;
    1682          109 :             referenced.objectSubId = 0;
    1683              : 
    1684              :             /* see comments in amapi.h about dependency strength */
    1685          109 :             recordDependencyOn(&myself, &referenced,
    1686          109 :                                proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
    1687              :         }
    1688              : 
    1689         1487 :         if (proc->lefttype != proc->righttype &&
    1690           32 :             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         1455 :         InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
    1703              :                                    entryoid, 0);
    1704              :     }
    1705              : 
    1706          404 :     table_close(rel, RowExclusiveLock);
    1707          404 : }
    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         3568 : typeDepNeeded(Oid typid, OpFamilyMember *member)
    1719              : {
    1720         3568 :     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         3568 :     if (IsPinnedObject(TypeRelationId, typid))
    1729         2699 :         return false;
    1730              : 
    1731              :     /* Nope, so check the input types of the function or operator. */
    1732          869 :     if (member->is_func)
    1733              :     {
    1734              :         Oid        *argtypes;
    1735              :         int         nargs;
    1736              : 
    1737          260 :         (void) get_func_signature(member->object, &argtypes, &nargs);
    1738          518 :         for (int i = 0; i < nargs; i++)
    1739              :         {
    1740          409 :             if (typid == argtypes[i])
    1741              :             {
    1742          151 :                 result = false; /* match, no dependency needed */
    1743          151 :                 break;
    1744              :             }
    1745              :         }
    1746          260 :         pfree(argtypes);
    1747              :     }
    1748              :     else
    1749              :     {
    1750              :         Oid         lefttype,
    1751              :                     righttype;
    1752              : 
    1753          609 :         op_input_types(member->object, &lefttype, &righttype);
    1754          609 :         if (typid == lefttype || typid == righttype)
    1755          609 :             result = false;     /* match, no dependency needed */
    1756              :     }
    1757          869 :     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           29 : dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
    1769              :               List *operators)
    1770              : {
    1771              :     ListCell   *l;
    1772              : 
    1773           51 :     foreach(l, operators)
    1774              :     {
    1775           25 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
    1776              :         Oid         amopid;
    1777              :         ObjectAddress object;
    1778              : 
    1779           25 :         amopid = GetSysCacheOid4(AMOPSTRATEGY, Anum_pg_amop_oid,
    1780              :                                  ObjectIdGetDatum(opfamilyoid),
    1781              :                                  ObjectIdGetDatum(op->lefttype),
    1782              :                                  ObjectIdGetDatum(op->righttype),
    1783              :                                  Int16GetDatum(op->number));
    1784           25 :         if (!OidIsValid(amopid))
    1785            3 :             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           22 :         object.classId = AccessMethodOperatorRelationId;
    1794           22 :         object.objectId = amopid;
    1795           22 :         object.objectSubId = 0;
    1796              : 
    1797           22 :         performDeletion(&object, DROP_RESTRICT, 0);
    1798              :     }
    1799           26 : }
    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           26 : dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
    1809              :                List *procedures)
    1810              : {
    1811              :     ListCell   *l;
    1812              : 
    1813           42 :     foreach(l, procedures)
    1814              :     {
    1815           19 :         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
    1816              :         Oid         amprocid;
    1817              :         ObjectAddress object;
    1818              : 
    1819           19 :         amprocid = GetSysCacheOid4(AMPROCNUM, Anum_pg_amproc_oid,
    1820              :                                    ObjectIdGetDatum(opfamilyoid),
    1821              :                                    ObjectIdGetDatum(op->lefttype),
    1822              :                                    ObjectIdGetDatum(op->righttype),
    1823              :                                    Int16GetDatum(op->number));
    1824           19 :         if (!OidIsValid(amprocid))
    1825            3 :             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           16 :         object.classId = AccessMethodProcedureRelationId;
    1834           16 :         object.objectId = amprocid;
    1835           16 :         object.objectSubId = 0;
    1836              : 
    1837           16 :         performDeletion(&object, DROP_RESTRICT, 0);
    1838              :     }
    1839           23 : }
    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           18 : IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
    1849              :                           Oid opcnamespace)
    1850              : {
    1851              :     /* make sure the new name doesn't exist */
    1852           18 :     if (SearchSysCacheExists3(CLAAMNAMENSP,
    1853              :                               ObjectIdGetDatum(opcmethod),
    1854              :                               CStringGetDatum(opcname),
    1855              :                               ObjectIdGetDatum(opcnamespace)))
    1856            6 :         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           12 : }
    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           18 : IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
    1872              :                            Oid opfnamespace)
    1873              : {
    1874              :     /* make sure the new name doesn't exist */
    1875           18 :     if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
    1876              :                               ObjectIdGetDatum(opfmethod),
    1877              :                               CStringGetDatum(opfname),
    1878              :                               ObjectIdGetDatum(opfnamespace)))
    1879            6 :         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           12 : }
        

Generated by: LCOV version 2.0-1