Header And Logo

PostgreSQL
| The world's most advanced open source database.

opclasscmds.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * opclasscmds.c
00004  *
00005  *    Routines for opclass (and opfamily) manipulation commands
00006  *
00007  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00008  * Portions Copyright (c) 1994, Regents of the University of California
00009  *
00010  *
00011  * IDENTIFICATION
00012  *    src/backend/commands/opclasscmds.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 #include "postgres.h"
00017 
00018 #include <limits.h>
00019 
00020 #include "access/genam.h"
00021 #include "access/heapam.h"
00022 #include "access/nbtree.h"
00023 #include "access/htup_details.h"
00024 #include "access/sysattr.h"
00025 #include "catalog/dependency.h"
00026 #include "catalog/indexing.h"
00027 #include "catalog/objectaccess.h"
00028 #include "catalog/pg_amop.h"
00029 #include "catalog/pg_amproc.h"
00030 #include "catalog/pg_namespace.h"
00031 #include "catalog/pg_opclass.h"
00032 #include "catalog/pg_operator.h"
00033 #include "catalog/pg_opfamily.h"
00034 #include "catalog/pg_proc.h"
00035 #include "catalog/pg_type.h"
00036 #include "commands/alter.h"
00037 #include "commands/defrem.h"
00038 #include "miscadmin.h"
00039 #include "parser/parse_func.h"
00040 #include "parser/parse_oper.h"
00041 #include "parser/parse_type.h"
00042 #include "utils/builtins.h"
00043 #include "utils/fmgroids.h"
00044 #include "utils/lsyscache.h"
00045 #include "utils/rel.h"
00046 #include "utils/syscache.h"
00047 #include "utils/tqual.h"
00048 
00049 
00050 /*
00051  * We use lists of this struct type to keep track of both operators and
00052  * procedures while building or adding to an opfamily.
00053  */
00054 typedef struct
00055 {
00056     Oid         object;         /* operator or support proc's OID */
00057     int         number;         /* strategy or support proc number */
00058     Oid         lefttype;       /* lefttype */
00059     Oid         righttype;      /* righttype */
00060     Oid         sortfamily;     /* ordering operator's sort opfamily, or 0 */
00061 } OpFamilyMember;
00062 
00063 
00064 static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
00065                  int maxOpNumber, int maxProcNumber,
00066                  List *items);
00067 static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
00068                   int maxOpNumber, int maxProcNumber,
00069                   List *items);
00070 static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
00071 static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
00072 static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
00073 static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
00074 static void storeOperators(List *opfamilyname, Oid amoid,
00075                Oid opfamilyoid, Oid opclassoid,
00076                List *operators, bool isAdd);
00077 static void storeProcedures(List *opfamilyname, Oid amoid,
00078                 Oid opfamilyoid, Oid opclassoid,
00079                 List *procedures, bool isAdd);
00080 static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
00081               List *operators);
00082 static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
00083                List *procedures);
00084 
00085 /*
00086  * OpFamilyCacheLookup
00087  *      Look up an existing opfamily by name.
00088  *
00089  * Returns a syscache tuple reference, or NULL if not found.
00090  */
00091 static HeapTuple
00092 OpFamilyCacheLookup(Oid amID, List *opfamilyname, bool missing_ok)
00093 {
00094     char       *schemaname;
00095     char       *opfname;
00096     HeapTuple   htup;
00097 
00098     /* deconstruct the name list */
00099     DeconstructQualifiedName(opfamilyname, &schemaname, &opfname);
00100 
00101     if (schemaname)
00102     {
00103         /* Look in specific schema only */
00104         Oid         namespaceId;
00105 
00106         namespaceId = LookupExplicitNamespace(schemaname, false);
00107         htup = SearchSysCache3(OPFAMILYAMNAMENSP,
00108                                ObjectIdGetDatum(amID),
00109                                PointerGetDatum(opfname),
00110                                ObjectIdGetDatum(namespaceId));
00111     }
00112     else
00113     {
00114         /* Unqualified opfamily name, so search the search path */
00115         Oid         opfID = OpfamilynameGetOpfid(amID, opfname);
00116 
00117         if (!OidIsValid(opfID))
00118             htup = NULL;
00119         else
00120             htup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfID));
00121     }
00122 
00123     if (!HeapTupleIsValid(htup) && !missing_ok)
00124     {
00125         HeapTuple   amtup;
00126 
00127         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
00128         if (!HeapTupleIsValid(amtup))
00129             elog(ERROR, "cache lookup failed for access method %u", amID);
00130         ereport(ERROR,
00131                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00132                  errmsg("operator family \"%s\" does not exist for access method \"%s\"",
00133                         NameListToString(opfamilyname),
00134                         NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
00135     }
00136 
00137     return htup;
00138 }
00139 
00140 /*
00141  * get_opfamily_oid
00142  *    find an opfamily OID by possibly qualified name
00143  *
00144  * If not found, returns InvalidOid if missing_ok, else throws error.
00145  */
00146 Oid
00147 get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok)
00148 {
00149     HeapTuple   htup;
00150     Oid         opfID;
00151 
00152     htup = OpFamilyCacheLookup(amID, opfamilyname, missing_ok);
00153     if (!HeapTupleIsValid(htup))
00154         return InvalidOid;
00155     opfID = HeapTupleGetOid(htup);
00156     ReleaseSysCache(htup);
00157 
00158     return opfID;
00159 }
00160 
00161 /*
00162  * OpClassCacheLookup
00163  *      Look up an existing opclass by name.
00164  *
00165  * Returns a syscache tuple reference, or NULL if not found.
00166  */
00167 static HeapTuple
00168 OpClassCacheLookup(Oid amID, List *opclassname, bool missing_ok)
00169 {
00170     char       *schemaname;
00171     char       *opcname;
00172     HeapTuple   htup;
00173 
00174     /* deconstruct the name list */
00175     DeconstructQualifiedName(opclassname, &schemaname, &opcname);
00176 
00177     if (schemaname)
00178     {
00179         /* Look in specific schema only */
00180         Oid         namespaceId;
00181 
00182         namespaceId = LookupExplicitNamespace(schemaname, false);
00183         htup = SearchSysCache3(CLAAMNAMENSP,
00184                                ObjectIdGetDatum(amID),
00185                                PointerGetDatum(opcname),
00186                                ObjectIdGetDatum(namespaceId));
00187     }
00188     else
00189     {
00190         /* Unqualified opclass name, so search the search path */
00191         Oid         opcID = OpclassnameGetOpcid(amID, opcname);
00192 
00193         if (!OidIsValid(opcID))
00194             htup = NULL;
00195         else
00196             htup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcID));
00197     }
00198 
00199     if (!HeapTupleIsValid(htup) && !missing_ok)
00200     {
00201         HeapTuple   amtup;
00202 
00203         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amID));
00204         if (!HeapTupleIsValid(amtup))
00205             elog(ERROR, "cache lookup failed for access method %u", amID);
00206         ereport(ERROR,
00207                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00208                  errmsg("operator class \"%s\" does not exist for access method \"%s\"",
00209                         NameListToString(opclassname),
00210                         NameStr(((Form_pg_am) GETSTRUCT(amtup))->amname))));
00211     }
00212 
00213     return htup;
00214 }
00215 
00216 /*
00217  * get_opclass_oid
00218  *    find an opclass OID by possibly qualified name
00219  *
00220  * If not found, returns InvalidOid if missing_ok, else throws error.
00221  */
00222 Oid
00223 get_opclass_oid(Oid amID, List *opclassname, bool missing_ok)
00224 {
00225     HeapTuple   htup;
00226     Oid         opcID;
00227 
00228     htup = OpClassCacheLookup(amID, opclassname, missing_ok);
00229     if (!HeapTupleIsValid(htup))
00230         return InvalidOid;
00231     opcID = HeapTupleGetOid(htup);
00232     ReleaseSysCache(htup);
00233 
00234     return opcID;
00235 }
00236 
00237 /*
00238  * CreateOpFamily
00239  *      Internal routine to make the catalog entry for a new operator family.
00240  *
00241  * Caller must have done permissions checks etc. already.
00242  */
00243 static Oid
00244 CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
00245 {
00246     Oid         opfamilyoid;
00247     Relation    rel;
00248     HeapTuple   tup;
00249     Datum       values[Natts_pg_opfamily];
00250     bool        nulls[Natts_pg_opfamily];
00251     NameData    opfName;
00252     ObjectAddress myself,
00253                 referenced;
00254 
00255     rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
00256 
00257     /*
00258      * Make sure there is no existing opfamily of this name (this is just to
00259      * give a more friendly error message than "duplicate key").
00260      */
00261     if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
00262                               ObjectIdGetDatum(amoid),
00263                               CStringGetDatum(opfname),
00264                               ObjectIdGetDatum(namespaceoid)))
00265         ereport(ERROR,
00266                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00267                  errmsg("operator family \"%s\" for access method \"%s\" already exists",
00268                         opfname, amname)));
00269 
00270     /*
00271      * Okay, let's create the pg_opfamily entry.
00272      */
00273     memset(values, 0, sizeof(values));
00274     memset(nulls, false, sizeof(nulls));
00275 
00276     values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
00277     namestrcpy(&opfName, opfname);
00278     values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
00279     values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00280     values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
00281 
00282     tup = heap_form_tuple(rel->rd_att, values, nulls);
00283 
00284     opfamilyoid = simple_heap_insert(rel, tup);
00285 
00286     CatalogUpdateIndexes(rel, tup);
00287 
00288     heap_freetuple(tup);
00289 
00290     /*
00291      * Create dependencies for the opfamily proper.  Note: we do not create a
00292      * dependency link to the AM, because we don't currently support DROP
00293      * ACCESS METHOD.
00294      */
00295     myself.classId = OperatorFamilyRelationId;
00296     myself.objectId = opfamilyoid;
00297     myself.objectSubId = 0;
00298 
00299     /* dependency on namespace */
00300     referenced.classId = NamespaceRelationId;
00301     referenced.objectId = namespaceoid;
00302     referenced.objectSubId = 0;
00303     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00304 
00305     /* dependency on owner */
00306     recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
00307 
00308     /* dependency on extension */
00309     recordDependencyOnCurrentExtension(&myself, false);
00310 
00311     /* Post creation hook for new operator family */
00312     InvokeObjectPostCreateHook(OperatorFamilyRelationId, opfamilyoid, 0);
00313 
00314     heap_close(rel, RowExclusiveLock);
00315 
00316     return opfamilyoid;
00317 }
00318 
00319 /*
00320  * DefineOpClass
00321  *      Define a new index operator class.
00322  */
00323 Oid
00324 DefineOpClass(CreateOpClassStmt *stmt)
00325 {
00326     char       *opcname;        /* name of opclass we're creating */
00327     Oid         amoid,          /* our AM's oid */
00328                 typeoid,        /* indexable datatype oid */
00329                 storageoid,     /* storage datatype oid, if any */
00330                 namespaceoid,   /* namespace to create opclass in */
00331                 opfamilyoid,    /* oid of containing opfamily */
00332                 opclassoid;     /* oid of opclass we create */
00333     int         maxOpNumber,    /* amstrategies value */
00334                 maxProcNumber;  /* amsupport value */
00335     bool        amstorage;      /* amstorage flag */
00336     List       *operators;      /* OpFamilyMember list for operators */
00337     List       *procedures;     /* OpFamilyMember list for support procs */
00338     ListCell   *l;
00339     Relation    rel;
00340     HeapTuple   tup;
00341     Form_pg_am  pg_am;
00342     Datum       values[Natts_pg_opclass];
00343     bool        nulls[Natts_pg_opclass];
00344     AclResult   aclresult;
00345     NameData    opcName;
00346     ObjectAddress myself,
00347                 referenced;
00348 
00349     /* Convert list of names to a name and namespace */
00350     namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
00351                                                      &opcname);
00352 
00353     /* Check we have creation rights in target namespace */
00354     aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
00355     if (aclresult != ACLCHECK_OK)
00356         aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00357                        get_namespace_name(namespaceoid));
00358 
00359     /* Get necessary info about access method */
00360     tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
00361     if (!HeapTupleIsValid(tup))
00362         ereport(ERROR,
00363                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00364                  errmsg("access method \"%s\" does not exist",
00365                         stmt->amname)));
00366 
00367     amoid = HeapTupleGetOid(tup);
00368     pg_am = (Form_pg_am) GETSTRUCT(tup);
00369     maxOpNumber = pg_am->amstrategies;
00370     /* if amstrategies is zero, just enforce that op numbers fit in int16 */
00371     if (maxOpNumber <= 0)
00372         maxOpNumber = SHRT_MAX;
00373     maxProcNumber = pg_am->amsupport;
00374     amstorage = pg_am->amstorage;
00375 
00376     /* XXX Should we make any privilege check against the AM? */
00377 
00378     ReleaseSysCache(tup);
00379 
00380     /*
00381      * The question of appropriate permissions for CREATE OPERATOR CLASS is
00382      * interesting.  Creating an opclass is tantamount to granting public
00383      * execute access on the functions involved, since the index machinery
00384      * generally does not check access permission before using the functions.
00385      * A minimum expectation therefore is that the caller have execute
00386      * privilege with grant option.  Since we don't have a way to make the
00387      * opclass go away if the grant option is revoked, we choose instead to
00388      * require ownership of the functions.  It's also not entirely clear what
00389      * permissions should be required on the datatype, but ownership seems
00390      * like a safe choice.
00391      *
00392      * Currently, we require superuser privileges to create an opclass. This
00393      * seems necessary because we have no way to validate that the offered set
00394      * of operators and functions are consistent with the AM's expectations.
00395      * It would be nice to provide such a check someday, if it can be done
00396      * without solving the halting problem :-(
00397      *
00398      * XXX re-enable NOT_USED code sections below if you remove this test.
00399      */
00400     if (!superuser())
00401         ereport(ERROR,
00402                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00403                  errmsg("must be superuser to create an operator class")));
00404 
00405     /* Look up the datatype */
00406     typeoid = typenameTypeId(NULL, stmt->datatype);
00407 
00408 #ifdef NOT_USED
00409     /* XXX this is unnecessary given the superuser check above */
00410     /* Check we have ownership of the datatype */
00411     if (!pg_type_ownercheck(typeoid, GetUserId()))
00412         aclcheck_error_type(ACLCHECK_NOT_OWNER, typeoid);
00413 #endif
00414 
00415     /*
00416      * Look up the containing operator family, or create one if FAMILY option
00417      * was omitted and there's not a match already.
00418      */
00419     if (stmt->opfamilyname)
00420     {
00421         opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
00422     }
00423     else
00424     {
00425         /* Lookup existing family of same name and namespace */
00426         tup = SearchSysCache3(OPFAMILYAMNAMENSP,
00427                               ObjectIdGetDatum(amoid),
00428                               PointerGetDatum(opcname),
00429                               ObjectIdGetDatum(namespaceoid));
00430         if (HeapTupleIsValid(tup))
00431         {
00432             opfamilyoid = HeapTupleGetOid(tup);
00433 
00434             /*
00435              * XXX given the superuser check above, there's no need for an
00436              * ownership check here
00437              */
00438             ReleaseSysCache(tup);
00439         }
00440         else
00441         {
00442             /*
00443              * Create it ... again no need for more permissions ...
00444              */
00445             opfamilyoid = CreateOpFamily(stmt->amname, opcname,
00446                                          namespaceoid, amoid);
00447         }
00448     }
00449 
00450     operators = NIL;
00451     procedures = NIL;
00452 
00453     /* Storage datatype is optional */
00454     storageoid = InvalidOid;
00455 
00456     /*
00457      * Scan the "items" list to obtain additional info.
00458      */
00459     foreach(l, stmt->items)
00460     {
00461         CreateOpClassItem *item = lfirst(l);
00462         Oid         operOid;
00463         Oid         funcOid;
00464         Oid         sortfamilyOid;
00465         OpFamilyMember *member;
00466 
00467         Assert(IsA(item, CreateOpClassItem));
00468         switch (item->itemtype)
00469         {
00470             case OPCLASS_ITEM_OPERATOR:
00471                 if (item->number <= 0 || item->number > maxOpNumber)
00472                     ereport(ERROR,
00473                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00474                              errmsg("invalid operator number %d,"
00475                                     " must be between 1 and %d",
00476                                     item->number, maxOpNumber)));
00477                 if (item->args != NIL)
00478                 {
00479                     TypeName   *typeName1 = (TypeName *) linitial(item->args);
00480                     TypeName   *typeName2 = (TypeName *) lsecond(item->args);
00481 
00482                     operOid = LookupOperNameTypeNames(NULL, item->name,
00483                                                       typeName1, typeName2,
00484                                                       false, -1);
00485                 }
00486                 else
00487                 {
00488                     /* Default to binary op on input datatype */
00489                     operOid = LookupOperName(NULL, item->name,
00490                                              typeoid, typeoid,
00491                                              false, -1);
00492                 }
00493 
00494                 if (item->order_family)
00495                     sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
00496                                                      item->order_family,
00497                                                      false);
00498                 else
00499                     sortfamilyOid = InvalidOid;
00500 
00501 #ifdef NOT_USED
00502                 /* XXX this is unnecessary given the superuser check above */
00503                 /* Caller must own operator and its underlying function */
00504                 if (!pg_oper_ownercheck(operOid, GetUserId()))
00505                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
00506                                    get_opname(operOid));
00507                 funcOid = get_opcode(operOid);
00508                 if (!pg_proc_ownercheck(funcOid, GetUserId()))
00509                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
00510                                    get_func_name(funcOid));
00511 #endif
00512 
00513                 /* Save the info */
00514                 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
00515                 member->object = operOid;
00516                 member->number = item->number;
00517                 member->sortfamily = sortfamilyOid;
00518                 assignOperTypes(member, amoid, typeoid);
00519                 addFamilyMember(&operators, member, false);
00520                 break;
00521             case OPCLASS_ITEM_FUNCTION:
00522                 if (item->number <= 0 || item->number > maxProcNumber)
00523                     ereport(ERROR,
00524                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00525                              errmsg("invalid procedure number %d,"
00526                                     " must be between 1 and %d",
00527                                     item->number, maxProcNumber)));
00528                 funcOid = LookupFuncNameTypeNames(item->name, item->args,
00529                                                   false);
00530 #ifdef NOT_USED
00531                 /* XXX this is unnecessary given the superuser check above */
00532                 /* Caller must own function */
00533                 if (!pg_proc_ownercheck(funcOid, GetUserId()))
00534                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
00535                                    get_func_name(funcOid));
00536 #endif
00537 
00538                 /* Save the info */
00539                 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
00540                 member->object = funcOid;
00541                 member->number = item->number;
00542 
00543                 /* allow overriding of the function's actual arg types */
00544                 if (item->class_args)
00545                     processTypesSpec(item->class_args,
00546                                      &member->lefttype, &member->righttype);
00547 
00548                 assignProcTypes(member, amoid, typeoid);
00549                 addFamilyMember(&procedures, member, true);
00550                 break;
00551             case OPCLASS_ITEM_STORAGETYPE:
00552                 if (OidIsValid(storageoid))
00553                     ereport(ERROR,
00554                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00555                            errmsg("storage type specified more than once")));
00556                 storageoid = typenameTypeId(NULL, item->storedtype);
00557 
00558 #ifdef NOT_USED
00559                 /* XXX this is unnecessary given the superuser check above */
00560                 /* Check we have ownership of the datatype */
00561                 if (!pg_type_ownercheck(storageoid, GetUserId()))
00562                     aclcheck_error_type(ACLCHECK_NOT_OWNER, storageoid);
00563 #endif
00564                 break;
00565             default:
00566                 elog(ERROR, "unrecognized item type: %d", item->itemtype);
00567                 break;
00568         }
00569     }
00570 
00571     /*
00572      * If storagetype is specified, make sure it's legal.
00573      */
00574     if (OidIsValid(storageoid))
00575     {
00576         /* Just drop the spec if same as column datatype */
00577         if (storageoid == typeoid)
00578             storageoid = InvalidOid;
00579         else if (!amstorage)
00580             ereport(ERROR,
00581                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00582                      errmsg("storage type cannot be different from data type for access method \"%s\"",
00583                             stmt->amname)));
00584     }
00585 
00586     rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
00587 
00588     /*
00589      * Make sure there is no existing opclass of this name (this is just to
00590      * give a more friendly error message than "duplicate key").
00591      */
00592     if (SearchSysCacheExists3(CLAAMNAMENSP,
00593                               ObjectIdGetDatum(amoid),
00594                               CStringGetDatum(opcname),
00595                               ObjectIdGetDatum(namespaceoid)))
00596         ereport(ERROR,
00597                 (errcode(ERRCODE_DUPLICATE_OBJECT),
00598                  errmsg("operator class \"%s\" for access method \"%s\" already exists",
00599                         opcname, stmt->amname)));
00600 
00601     /*
00602      * If we are creating a default opclass, check there isn't one already.
00603      * (Note we do not restrict this test to visible opclasses; this ensures
00604      * that typcache.c can find unique solutions to its questions.)
00605      */
00606     if (stmt->isDefault)
00607     {
00608         ScanKeyData skey[1];
00609         SysScanDesc scan;
00610 
00611         ScanKeyInit(&skey[0],
00612                     Anum_pg_opclass_opcmethod,
00613                     BTEqualStrategyNumber, F_OIDEQ,
00614                     ObjectIdGetDatum(amoid));
00615 
00616         scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
00617                                   SnapshotNow, 1, skey);
00618 
00619         while (HeapTupleIsValid(tup = systable_getnext(scan)))
00620         {
00621             Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
00622 
00623             if (opclass->opcintype == typeoid && opclass->opcdefault)
00624                 ereport(ERROR,
00625                         (errcode(ERRCODE_DUPLICATE_OBJECT),
00626                          errmsg("could not make operator class \"%s\" be default for type %s",
00627                                 opcname,
00628                                 TypeNameToString(stmt->datatype)),
00629                    errdetail("Operator class \"%s\" already is the default.",
00630                              NameStr(opclass->opcname))));
00631         }
00632 
00633         systable_endscan(scan);
00634     }
00635 
00636     /*
00637      * Okay, let's create the pg_opclass entry.
00638      */
00639     memset(values, 0, sizeof(values));
00640     memset(nulls, false, sizeof(nulls));
00641 
00642     values[Anum_pg_opclass_opcmethod - 1] = ObjectIdGetDatum(amoid);
00643     namestrcpy(&opcName, opcname);
00644     values[Anum_pg_opclass_opcname - 1] = NameGetDatum(&opcName);
00645     values[Anum_pg_opclass_opcnamespace - 1] = ObjectIdGetDatum(namespaceoid);
00646     values[Anum_pg_opclass_opcowner - 1] = ObjectIdGetDatum(GetUserId());
00647     values[Anum_pg_opclass_opcfamily - 1] = ObjectIdGetDatum(opfamilyoid);
00648     values[Anum_pg_opclass_opcintype - 1] = ObjectIdGetDatum(typeoid);
00649     values[Anum_pg_opclass_opcdefault - 1] = BoolGetDatum(stmt->isDefault);
00650     values[Anum_pg_opclass_opckeytype - 1] = ObjectIdGetDatum(storageoid);
00651 
00652     tup = heap_form_tuple(rel->rd_att, values, nulls);
00653 
00654     opclassoid = simple_heap_insert(rel, tup);
00655 
00656     CatalogUpdateIndexes(rel, tup);
00657 
00658     heap_freetuple(tup);
00659 
00660     /*
00661      * Now add tuples to pg_amop and pg_amproc tying in the operators and
00662      * functions.  Dependencies on them are inserted, too.
00663      */
00664     storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
00665                    opclassoid, operators, false);
00666     storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
00667                     opclassoid, procedures, false);
00668 
00669     /*
00670      * Create dependencies for the opclass proper.  Note: we do not create a
00671      * dependency link to the AM, because we don't currently support DROP
00672      * ACCESS METHOD.
00673      */
00674     myself.classId = OperatorClassRelationId;
00675     myself.objectId = opclassoid;
00676     myself.objectSubId = 0;
00677 
00678     /* dependency on namespace */
00679     referenced.classId = NamespaceRelationId;
00680     referenced.objectId = namespaceoid;
00681     referenced.objectSubId = 0;
00682     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00683 
00684     /* dependency on opfamily */
00685     referenced.classId = OperatorFamilyRelationId;
00686     referenced.objectId = opfamilyoid;
00687     referenced.objectSubId = 0;
00688     recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
00689 
00690     /* dependency on indexed datatype */
00691     referenced.classId = TypeRelationId;
00692     referenced.objectId = typeoid;
00693     referenced.objectSubId = 0;
00694     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00695 
00696     /* dependency on storage datatype */
00697     if (OidIsValid(storageoid))
00698     {
00699         referenced.classId = TypeRelationId;
00700         referenced.objectId = storageoid;
00701         referenced.objectSubId = 0;
00702         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
00703     }
00704 
00705     /* dependency on owner */
00706     recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId());
00707 
00708     /* dependency on extension */
00709     recordDependencyOnCurrentExtension(&myself, false);
00710 
00711     /* Post creation hook for new operator class */
00712     InvokeObjectPostCreateHook(OperatorClassRelationId, opclassoid, 0);
00713 
00714     heap_close(rel, RowExclusiveLock);
00715 
00716     return opclassoid;
00717 }
00718 
00719 
00720 /*
00721  * DefineOpFamily
00722  *      Define a new index operator family.
00723  */
00724 Oid
00725 DefineOpFamily(CreateOpFamilyStmt *stmt)
00726 {
00727     char       *opfname;        /* name of opfamily we're creating */
00728     Oid         amoid,          /* our AM's oid */
00729                 namespaceoid;   /* namespace to create opfamily in */
00730     AclResult   aclresult;
00731 
00732     /* Convert list of names to a name and namespace */
00733     namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
00734                                                      &opfname);
00735 
00736     /* Check we have creation rights in target namespace */
00737     aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
00738     if (aclresult != ACLCHECK_OK)
00739         aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
00740                        get_namespace_name(namespaceoid));
00741 
00742     /* Get access method OID, throwing an error if it doesn't exist. */
00743     amoid = get_am_oid(stmt->amname, false);
00744 
00745     /* XXX Should we make any privilege check against the AM? */
00746 
00747     /*
00748      * Currently, we require superuser privileges to create an opfamily. See
00749      * comments in DefineOpClass.
00750      */
00751     if (!superuser())
00752         ereport(ERROR,
00753                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00754                  errmsg("must be superuser to create an operator family")));
00755 
00756     /* Insert pg_opfamily catalog entry */
00757     return CreateOpFamily(stmt->amname, opfname, namespaceoid, amoid);
00758 }
00759 
00760 
00761 /*
00762  * AlterOpFamily
00763  *      Add or remove operators/procedures within an existing operator family.
00764  *
00765  * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP.  Some
00766  * other commands called ALTER OPERATOR FAMILY exist, but go through
00767  * different code paths.
00768  */
00769 Oid
00770 AlterOpFamily(AlterOpFamilyStmt *stmt)
00771 {
00772     Oid         amoid,          /* our AM's oid */
00773                 opfamilyoid;    /* oid of opfamily */
00774     int         maxOpNumber,    /* amstrategies value */
00775                 maxProcNumber;  /* amsupport value */
00776     HeapTuple   tup;
00777     Form_pg_am  pg_am;
00778 
00779     /* Get necessary info about access method */
00780     tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
00781     if (!HeapTupleIsValid(tup))
00782         ereport(ERROR,
00783                 (errcode(ERRCODE_UNDEFINED_OBJECT),
00784                  errmsg("access method \"%s\" does not exist",
00785                         stmt->amname)));
00786 
00787     amoid = HeapTupleGetOid(tup);
00788     pg_am = (Form_pg_am) GETSTRUCT(tup);
00789     maxOpNumber = pg_am->amstrategies;
00790     /* if amstrategies is zero, just enforce that op numbers fit in int16 */
00791     if (maxOpNumber <= 0)
00792         maxOpNumber = SHRT_MAX;
00793     maxProcNumber = pg_am->amsupport;
00794 
00795     /* XXX Should we make any privilege check against the AM? */
00796 
00797     ReleaseSysCache(tup);
00798 
00799     /* Look up the opfamily */
00800     opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
00801 
00802     /*
00803      * Currently, we require superuser privileges to alter an opfamily.
00804      *
00805      * XXX re-enable NOT_USED code sections below if you remove this test.
00806      */
00807     if (!superuser())
00808         ereport(ERROR,
00809                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00810                  errmsg("must be superuser to alter an operator family")));
00811 
00812     /*
00813      * ADD and DROP cases need separate code from here on down.
00814      */
00815     if (stmt->isDrop)
00816         AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
00817                           maxOpNumber, maxProcNumber,
00818                           stmt->items);
00819     else
00820         AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
00821                          maxOpNumber, maxProcNumber,
00822                          stmt->items);
00823 
00824     return opfamilyoid;
00825 }
00826 
00827 /*
00828  * ADD part of ALTER OP FAMILY
00829  */
00830 static void
00831 AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
00832                  int maxOpNumber, int maxProcNumber,
00833                  List *items)
00834 {
00835     List       *operators;      /* OpFamilyMember list for operators */
00836     List       *procedures;     /* OpFamilyMember list for support procs */
00837     ListCell   *l;
00838 
00839     operators = NIL;
00840     procedures = NIL;
00841 
00842     /*
00843      * Scan the "items" list to obtain additional info.
00844      */
00845     foreach(l, items)
00846     {
00847         CreateOpClassItem *item = lfirst(l);
00848         Oid         operOid;
00849         Oid         funcOid;
00850         Oid         sortfamilyOid;
00851         OpFamilyMember *member;
00852 
00853         Assert(IsA(item, CreateOpClassItem));
00854         switch (item->itemtype)
00855         {
00856             case OPCLASS_ITEM_OPERATOR:
00857                 if (item->number <= 0 || item->number > maxOpNumber)
00858                     ereport(ERROR,
00859                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00860                              errmsg("invalid operator number %d,"
00861                                     " must be between 1 and %d",
00862                                     item->number, maxOpNumber)));
00863                 if (item->args != NIL)
00864                 {
00865                     TypeName   *typeName1 = (TypeName *) linitial(item->args);
00866                     TypeName   *typeName2 = (TypeName *) lsecond(item->args);
00867 
00868                     operOid = LookupOperNameTypeNames(NULL, item->name,
00869                                                       typeName1, typeName2,
00870                                                       false, -1);
00871                 }
00872                 else
00873                 {
00874                     ereport(ERROR,
00875                             (errcode(ERRCODE_SYNTAX_ERROR),
00876                              errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
00877                     operOid = InvalidOid;       /* keep compiler quiet */
00878                 }
00879 
00880                 if (item->order_family)
00881                     sortfamilyOid = get_opfamily_oid(BTREE_AM_OID,
00882                                                      item->order_family,
00883                                                      false);
00884                 else
00885                     sortfamilyOid = InvalidOid;
00886 
00887 #ifdef NOT_USED
00888                 /* XXX this is unnecessary given the superuser check above */
00889                 /* Caller must own operator and its underlying function */
00890                 if (!pg_oper_ownercheck(operOid, GetUserId()))
00891                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
00892                                    get_opname(operOid));
00893                 funcOid = get_opcode(operOid);
00894                 if (!pg_proc_ownercheck(funcOid, GetUserId()))
00895                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
00896                                    get_func_name(funcOid));
00897 #endif
00898 
00899                 /* Save the info */
00900                 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
00901                 member->object = operOid;
00902                 member->number = item->number;
00903                 member->sortfamily = sortfamilyOid;
00904                 assignOperTypes(member, amoid, InvalidOid);
00905                 addFamilyMember(&operators, member, false);
00906                 break;
00907             case OPCLASS_ITEM_FUNCTION:
00908                 if (item->number <= 0 || item->number > maxProcNumber)
00909                     ereport(ERROR,
00910                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00911                              errmsg("invalid procedure number %d,"
00912                                     " must be between 1 and %d",
00913                                     item->number, maxProcNumber)));
00914                 funcOid = LookupFuncNameTypeNames(item->name, item->args,
00915                                                   false);
00916 #ifdef NOT_USED
00917                 /* XXX this is unnecessary given the superuser check above */
00918                 /* Caller must own function */
00919                 if (!pg_proc_ownercheck(funcOid, GetUserId()))
00920                     aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
00921                                    get_func_name(funcOid));
00922 #endif
00923 
00924                 /* Save the info */
00925                 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
00926                 member->object = funcOid;
00927                 member->number = item->number;
00928 
00929                 /* allow overriding of the function's actual arg types */
00930                 if (item->class_args)
00931                     processTypesSpec(item->class_args,
00932                                      &member->lefttype, &member->righttype);
00933 
00934                 assignProcTypes(member, amoid, InvalidOid);
00935                 addFamilyMember(&procedures, member, true);
00936                 break;
00937             case OPCLASS_ITEM_STORAGETYPE:
00938                 ereport(ERROR,
00939                         (errcode(ERRCODE_SYNTAX_ERROR),
00940                          errmsg("STORAGE cannot be specified in ALTER OPERATOR FAMILY")));
00941                 break;
00942             default:
00943                 elog(ERROR, "unrecognized item type: %d", item->itemtype);
00944                 break;
00945         }
00946     }
00947 
00948     /*
00949      * Add tuples to pg_amop and pg_amproc tying in the operators and
00950      * functions.  Dependencies on them are inserted, too.
00951      */
00952     storeOperators(opfamilyname, amoid, opfamilyoid,
00953                    InvalidOid, operators, true);
00954     storeProcedures(opfamilyname, amoid, opfamilyoid,
00955                     InvalidOid, procedures, true);
00956 }
00957 
00958 /*
00959  * DROP part of ALTER OP FAMILY
00960  */
00961 static void
00962 AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
00963                   int maxOpNumber, int maxProcNumber,
00964                   List *items)
00965 {
00966     List       *operators;      /* OpFamilyMember list for operators */
00967     List       *procedures;     /* OpFamilyMember list for support procs */
00968     ListCell   *l;
00969 
00970     operators = NIL;
00971     procedures = NIL;
00972 
00973     /*
00974      * Scan the "items" list to obtain additional info.
00975      */
00976     foreach(l, items)
00977     {
00978         CreateOpClassItem *item = lfirst(l);
00979         Oid         lefttype,
00980                     righttype;
00981         OpFamilyMember *member;
00982 
00983         Assert(IsA(item, CreateOpClassItem));
00984         switch (item->itemtype)
00985         {
00986             case OPCLASS_ITEM_OPERATOR:
00987                 if (item->number <= 0 || item->number > maxOpNumber)
00988                     ereport(ERROR,
00989                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
00990                              errmsg("invalid operator number %d,"
00991                                     " must be between 1 and %d",
00992                                     item->number, maxOpNumber)));
00993                 processTypesSpec(item->args, &lefttype, &righttype);
00994                 /* Save the info */
00995                 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
00996                 member->number = item->number;
00997                 member->lefttype = lefttype;
00998                 member->righttype = righttype;
00999                 addFamilyMember(&operators, member, false);
01000                 break;
01001             case OPCLASS_ITEM_FUNCTION:
01002                 if (item->number <= 0 || item->number > maxProcNumber)
01003                     ereport(ERROR,
01004                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01005                              errmsg("invalid procedure number %d,"
01006                                     " must be between 1 and %d",
01007                                     item->number, maxProcNumber)));
01008                 processTypesSpec(item->args, &lefttype, &righttype);
01009                 /* Save the info */
01010                 member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
01011                 member->number = item->number;
01012                 member->lefttype = lefttype;
01013                 member->righttype = righttype;
01014                 addFamilyMember(&procedures, member, true);
01015                 break;
01016             case OPCLASS_ITEM_STORAGETYPE:
01017                 /* grammar prevents this from appearing */
01018             default:
01019                 elog(ERROR, "unrecognized item type: %d", item->itemtype);
01020                 break;
01021         }
01022     }
01023 
01024     /*
01025      * Remove tuples from pg_amop and pg_amproc.
01026      */
01027     dropOperators(opfamilyname, amoid, opfamilyoid, operators);
01028     dropProcedures(opfamilyname, amoid, opfamilyoid, procedures);
01029 }
01030 
01031 
01032 /*
01033  * Deal with explicit arg types used in ALTER ADD/DROP
01034  */
01035 static void
01036 processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
01037 {
01038     TypeName   *typeName;
01039 
01040     Assert(args != NIL);
01041 
01042     typeName = (TypeName *) linitial(args);
01043     *lefttype = typenameTypeId(NULL, typeName);
01044 
01045     if (list_length(args) > 1)
01046     {
01047         typeName = (TypeName *) lsecond(args);
01048         *righttype = typenameTypeId(NULL, typeName);
01049     }
01050     else
01051         *righttype = *lefttype;
01052 
01053     if (list_length(args) > 2)
01054         ereport(ERROR,
01055                 (errcode(ERRCODE_SYNTAX_ERROR),
01056                  errmsg("one or two argument types must be specified")));
01057 }
01058 
01059 
01060 /*
01061  * Determine the lefttype/righttype to assign to an operator,
01062  * and do any validity checking we can manage.
01063  */
01064 static void
01065 assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
01066 {
01067     Operator    optup;
01068     Form_pg_operator opform;
01069 
01070     /* Fetch the operator definition */
01071     optup = SearchSysCache1(OPEROID, ObjectIdGetDatum(member->object));
01072     if (optup == NULL)
01073         elog(ERROR, "cache lookup failed for operator %u", member->object);
01074     opform = (Form_pg_operator) GETSTRUCT(optup);
01075 
01076     /*
01077      * Opfamily operators must be binary.
01078      */
01079     if (opform->oprkind != 'b')
01080         ereport(ERROR,
01081                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01082                  errmsg("index operators must be binary")));
01083 
01084     if (OidIsValid(member->sortfamily))
01085     {
01086         /*
01087          * Ordering op, check index supports that.  (We could perhaps also
01088          * check that the operator returns a type supported by the sortfamily,
01089          * but that seems more trouble than it's worth here.  If it does not,
01090          * the operator will never be matchable to any ORDER BY clause, but no
01091          * worse consequences can ensue.  Also, trying to check that would
01092          * create an ordering hazard during dump/reload: it's possible that
01093          * the family has been created but not yet populated with the required
01094          * operators.)
01095          */
01096         HeapTuple   amtup;
01097         Form_pg_am  pg_am;
01098 
01099         amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
01100         if (amtup == NULL)
01101             elog(ERROR, "cache lookup failed for access method %u", amoid);
01102         pg_am = (Form_pg_am) GETSTRUCT(amtup);
01103 
01104         if (!pg_am->amcanorderbyop)
01105             ereport(ERROR,
01106                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01107             errmsg("access method \"%s\" does not support ordering operators",
01108                    NameStr(pg_am->amname))));
01109 
01110         ReleaseSysCache(amtup);
01111     }
01112     else
01113     {
01114         /*
01115          * Search operators must return boolean.
01116          */
01117         if (opform->oprresult != BOOLOID)
01118             ereport(ERROR,
01119                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01120                      errmsg("index search operators must return boolean")));
01121     }
01122 
01123     /*
01124      * If lefttype/righttype isn't specified, use the operator's input types
01125      */
01126     if (!OidIsValid(member->lefttype))
01127         member->lefttype = opform->oprleft;
01128     if (!OidIsValid(member->righttype))
01129         member->righttype = opform->oprright;
01130 
01131     ReleaseSysCache(optup);
01132 }
01133 
01134 /*
01135  * Determine the lefttype/righttype to assign to a support procedure,
01136  * and do any validity checking we can manage.
01137  */
01138 static void
01139 assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
01140 {
01141     HeapTuple   proctup;
01142     Form_pg_proc procform;
01143 
01144     /* Fetch the procedure definition */
01145     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(member->object));
01146     if (proctup == NULL)
01147         elog(ERROR, "cache lookup failed for function %u", member->object);
01148     procform = (Form_pg_proc) GETSTRUCT(proctup);
01149 
01150     /*
01151      * btree comparison procs must be 2-arg procs returning int4, while btree
01152      * sortsupport procs must take internal and return void.  hash support
01153      * procs must be 1-arg procs returning int4.  Otherwise we don't know.
01154      */
01155     if (amoid == BTREE_AM_OID)
01156     {
01157         if (member->number == BTORDER_PROC)
01158         {
01159             if (procform->pronargs != 2)
01160                 ereport(ERROR,
01161                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01162                          errmsg("btree comparison procedures must have two arguments")));
01163             if (procform->prorettype != INT4OID)
01164                 ereport(ERROR,
01165                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01166                  errmsg("btree comparison procedures must return integer")));
01167 
01168             /*
01169              * If lefttype/righttype isn't specified, use the proc's input
01170              * types
01171              */
01172             if (!OidIsValid(member->lefttype))
01173                 member->lefttype = procform->proargtypes.values[0];
01174             if (!OidIsValid(member->righttype))
01175                 member->righttype = procform->proargtypes.values[1];
01176         }
01177         else if (member->number == BTSORTSUPPORT_PROC)
01178         {
01179             if (procform->pronargs != 1 ||
01180                 procform->proargtypes.values[0] != INTERNALOID)
01181                 ereport(ERROR,
01182                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01183                          errmsg("btree sort support procedures must accept type \"internal\"")));
01184             if (procform->prorettype != VOIDOID)
01185                 ereport(ERROR,
01186                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01187                   errmsg("btree sort support procedures must return void")));
01188 
01189             /*
01190              * Can't infer lefttype/righttype from proc, so use default rule
01191              */
01192         }
01193     }
01194     else if (amoid == HASH_AM_OID)
01195     {
01196         if (procform->pronargs != 1)
01197             ereport(ERROR,
01198                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01199                      errmsg("hash procedures must have one argument")));
01200         if (procform->prorettype != INT4OID)
01201             ereport(ERROR,
01202                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01203                      errmsg("hash procedures must return integer")));
01204 
01205         /*
01206          * If lefttype/righttype isn't specified, use the proc's input type
01207          */
01208         if (!OidIsValid(member->lefttype))
01209             member->lefttype = procform->proargtypes.values[0];
01210         if (!OidIsValid(member->righttype))
01211             member->righttype = procform->proargtypes.values[0];
01212     }
01213 
01214     /*
01215      * The default in CREATE OPERATOR CLASS is to use the class' opcintype as
01216      * lefttype and righttype.  In CREATE or ALTER OPERATOR FAMILY, opcintype
01217      * isn't available, so make the user specify the types.
01218      */
01219     if (!OidIsValid(member->lefttype))
01220         member->lefttype = typeoid;
01221     if (!OidIsValid(member->righttype))
01222         member->righttype = typeoid;
01223 
01224     if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
01225         ereport(ERROR,
01226                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01227                  errmsg("associated data types must be specified for index support procedure")));
01228 
01229     ReleaseSysCache(proctup);
01230 }
01231 
01232 /*
01233  * Add a new family member to the appropriate list, after checking for
01234  * duplicated strategy or proc number.
01235  */
01236 static void
01237 addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
01238 {
01239     ListCell   *l;
01240 
01241     foreach(l, *list)
01242     {
01243         OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
01244 
01245         if (old->number == member->number &&
01246             old->lefttype == member->lefttype &&
01247             old->righttype == member->righttype)
01248         {
01249             if (isProc)
01250                 ereport(ERROR,
01251                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01252                          errmsg("procedure number %d for (%s,%s) appears more than once",
01253                                 member->number,
01254                                 format_type_be(member->lefttype),
01255                                 format_type_be(member->righttype))));
01256             else
01257                 ereport(ERROR,
01258                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
01259                          errmsg("operator number %d for (%s,%s) appears more than once",
01260                                 member->number,
01261                                 format_type_be(member->lefttype),
01262                                 format_type_be(member->righttype))));
01263         }
01264     }
01265     *list = lappend(*list, member);
01266 }
01267 
01268 /*
01269  * Dump the operators to pg_amop
01270  *
01271  * We also make dependency entries in pg_depend for the opfamily entries.
01272  * If opclassoid is valid then make an INTERNAL dependency on that opclass,
01273  * else make an AUTO dependency on the opfamily.
01274  */
01275 static void
01276 storeOperators(List *opfamilyname, Oid amoid,
01277                Oid opfamilyoid, Oid opclassoid,
01278                List *operators, bool isAdd)
01279 {
01280     Relation    rel;
01281     Datum       values[Natts_pg_amop];
01282     bool        nulls[Natts_pg_amop];
01283     HeapTuple   tup;
01284     Oid         entryoid;
01285     ObjectAddress myself,
01286                 referenced;
01287     ListCell   *l;
01288 
01289     rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
01290 
01291     foreach(l, operators)
01292     {
01293         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
01294         char        oppurpose;
01295 
01296         /*
01297          * If adding to an existing family, check for conflict with an
01298          * existing pg_amop entry (just to give a nicer error message)
01299          */
01300         if (isAdd &&
01301             SearchSysCacheExists4(AMOPSTRATEGY,
01302                                   ObjectIdGetDatum(opfamilyoid),
01303                                   ObjectIdGetDatum(op->lefttype),
01304                                   ObjectIdGetDatum(op->righttype),
01305                                   Int16GetDatum(op->number)))
01306             ereport(ERROR,
01307                     (errcode(ERRCODE_DUPLICATE_OBJECT),
01308                      errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
01309                             op->number,
01310                             format_type_be(op->lefttype),
01311                             format_type_be(op->righttype),
01312                             NameListToString(opfamilyname))));
01313 
01314         oppurpose = OidIsValid(op->sortfamily) ? AMOP_ORDER : AMOP_SEARCH;
01315 
01316         /* Create the pg_amop entry */
01317         memset(values, 0, sizeof(values));
01318         memset(nulls, false, sizeof(nulls));
01319 
01320         values[Anum_pg_amop_amopfamily - 1] = ObjectIdGetDatum(opfamilyoid);
01321         values[Anum_pg_amop_amoplefttype - 1] = ObjectIdGetDatum(op->lefttype);
01322         values[Anum_pg_amop_amoprighttype - 1] = ObjectIdGetDatum(op->righttype);
01323         values[Anum_pg_amop_amopstrategy - 1] = Int16GetDatum(op->number);
01324         values[Anum_pg_amop_amoppurpose - 1] = CharGetDatum(oppurpose);
01325         values[Anum_pg_amop_amopopr - 1] = ObjectIdGetDatum(op->object);
01326         values[Anum_pg_amop_amopmethod - 1] = ObjectIdGetDatum(amoid);
01327         values[Anum_pg_amop_amopsortfamily - 1] = ObjectIdGetDatum(op->sortfamily);
01328 
01329         tup = heap_form_tuple(rel->rd_att, values, nulls);
01330 
01331         entryoid = simple_heap_insert(rel, tup);
01332 
01333         CatalogUpdateIndexes(rel, tup);
01334 
01335         heap_freetuple(tup);
01336 
01337         /* Make its dependencies */
01338         myself.classId = AccessMethodOperatorRelationId;
01339         myself.objectId = entryoid;
01340         myself.objectSubId = 0;
01341 
01342         referenced.classId = OperatorRelationId;
01343         referenced.objectId = op->object;
01344         referenced.objectSubId = 0;
01345 
01346         if (OidIsValid(opclassoid))
01347         {
01348             /* if contained in an opclass, use a NORMAL dep on operator */
01349             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
01350 
01351             /* ... and an INTERNAL dep on the opclass */
01352             referenced.classId = OperatorClassRelationId;
01353             referenced.objectId = opclassoid;
01354             referenced.objectSubId = 0;
01355             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
01356         }
01357         else
01358         {
01359             /* if "loose" in the opfamily, use a AUTO dep on operator */
01360             recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
01361 
01362             /* ... and an AUTO dep on the opfamily */
01363             referenced.classId = OperatorFamilyRelationId;
01364             referenced.objectId = opfamilyoid;
01365             referenced.objectSubId = 0;
01366             recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
01367         }
01368 
01369         /* A search operator also needs a dep on the referenced opfamily */
01370         if (OidIsValid(op->sortfamily))
01371         {
01372             referenced.classId = OperatorFamilyRelationId;
01373             referenced.objectId = op->sortfamily;
01374             referenced.objectSubId = 0;
01375             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
01376         }
01377         /* Post create hook of this access method operator */
01378         InvokeObjectPostCreateHook(AccessMethodOperatorRelationId,
01379                                    entryoid, 0);
01380     }
01381 
01382     heap_close(rel, RowExclusiveLock);
01383 }
01384 
01385 /*
01386  * Dump the procedures (support routines) to pg_amproc
01387  *
01388  * We also make dependency entries in pg_depend for the opfamily entries.
01389  * If opclassoid is valid then make an INTERNAL dependency on that opclass,
01390  * else make an AUTO dependency on the opfamily.
01391  */
01392 static void
01393 storeProcedures(List *opfamilyname, Oid amoid,
01394                 Oid opfamilyoid, Oid opclassoid,
01395                 List *procedures, bool isAdd)
01396 {
01397     Relation    rel;
01398     Datum       values[Natts_pg_amproc];
01399     bool        nulls[Natts_pg_amproc];
01400     HeapTuple   tup;
01401     Oid         entryoid;
01402     ObjectAddress myself,
01403                 referenced;
01404     ListCell   *l;
01405 
01406     rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
01407 
01408     foreach(l, procedures)
01409     {
01410         OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
01411 
01412         /*
01413          * If adding to an existing family, check for conflict with an
01414          * existing pg_amproc entry (just to give a nicer error message)
01415          */
01416         if (isAdd &&
01417             SearchSysCacheExists4(AMPROCNUM,
01418                                   ObjectIdGetDatum(opfamilyoid),
01419                                   ObjectIdGetDatum(proc->lefttype),
01420                                   ObjectIdGetDatum(proc->righttype),
01421                                   Int16GetDatum(proc->number)))
01422             ereport(ERROR,
01423                     (errcode(ERRCODE_DUPLICATE_OBJECT),
01424                      errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
01425                             proc->number,
01426                             format_type_be(proc->lefttype),
01427                             format_type_be(proc->righttype),
01428                             NameListToString(opfamilyname))));
01429 
01430         /* Create the pg_amproc entry */
01431         memset(values, 0, sizeof(values));
01432         memset(nulls, false, sizeof(nulls));
01433 
01434         values[Anum_pg_amproc_amprocfamily - 1] = ObjectIdGetDatum(opfamilyoid);
01435         values[Anum_pg_amproc_amproclefttype - 1] = ObjectIdGetDatum(proc->lefttype);
01436         values[Anum_pg_amproc_amprocrighttype - 1] = ObjectIdGetDatum(proc->righttype);
01437         values[Anum_pg_amproc_amprocnum - 1] = Int16GetDatum(proc->number);
01438         values[Anum_pg_amproc_amproc - 1] = ObjectIdGetDatum(proc->object);
01439 
01440         tup = heap_form_tuple(rel->rd_att, values, nulls);
01441 
01442         entryoid = simple_heap_insert(rel, tup);
01443 
01444         CatalogUpdateIndexes(rel, tup);
01445 
01446         heap_freetuple(tup);
01447 
01448         /* Make its dependencies */
01449         myself.classId = AccessMethodProcedureRelationId;
01450         myself.objectId = entryoid;
01451         myself.objectSubId = 0;
01452 
01453         referenced.classId = ProcedureRelationId;
01454         referenced.objectId = proc->object;
01455         referenced.objectSubId = 0;
01456 
01457         if (OidIsValid(opclassoid))
01458         {
01459             /* if contained in an opclass, use a NORMAL dep on procedure */
01460             recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
01461 
01462             /* ... and an INTERNAL dep on the opclass */
01463             referenced.classId = OperatorClassRelationId;
01464             referenced.objectId = opclassoid;
01465             referenced.objectSubId = 0;
01466             recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
01467         }
01468         else
01469         {
01470             /* if "loose" in the opfamily, use a AUTO dep on procedure */
01471             recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
01472 
01473             /* ... and an AUTO dep on the opfamily */
01474             referenced.classId = OperatorFamilyRelationId;
01475             referenced.objectId = opfamilyoid;
01476             referenced.objectSubId = 0;
01477             recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
01478         }
01479         /* Post create hook of access method procedure */
01480         InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
01481                                    entryoid, 0);
01482     }
01483 
01484     heap_close(rel, RowExclusiveLock);
01485 }
01486 
01487 
01488 /*
01489  * Remove operator entries from an opfamily.
01490  *
01491  * Note: this is only allowed for "loose" members of an opfamily, hence
01492  * behavior is always RESTRICT.
01493  */
01494 static void
01495 dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
01496               List *operators)
01497 {
01498     ListCell   *l;
01499 
01500     foreach(l, operators)
01501     {
01502         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
01503         Oid         amopid;
01504         ObjectAddress object;
01505 
01506         amopid = GetSysCacheOid4(AMOPSTRATEGY,
01507                                  ObjectIdGetDatum(opfamilyoid),
01508                                  ObjectIdGetDatum(op->lefttype),
01509                                  ObjectIdGetDatum(op->righttype),
01510                                  Int16GetDatum(op->number));
01511         if (!OidIsValid(amopid))
01512             ereport(ERROR,
01513                     (errcode(ERRCODE_UNDEFINED_OBJECT),
01514                      errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
01515                             op->number,
01516                             format_type_be(op->lefttype),
01517                             format_type_be(op->righttype),
01518                             NameListToString(opfamilyname))));
01519 
01520         object.classId = AccessMethodOperatorRelationId;
01521         object.objectId = amopid;
01522         object.objectSubId = 0;
01523 
01524         performDeletion(&object, DROP_RESTRICT, 0);
01525     }
01526 }
01527 
01528 /*
01529  * Remove procedure entries from an opfamily.
01530  *
01531  * Note: this is only allowed for "loose" members of an opfamily, hence
01532  * behavior is always RESTRICT.
01533  */
01534 static void
01535 dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
01536                List *procedures)
01537 {
01538     ListCell   *l;
01539 
01540     foreach(l, procedures)
01541     {
01542         OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
01543         Oid         amprocid;
01544         ObjectAddress object;
01545 
01546         amprocid = GetSysCacheOid4(AMPROCNUM,
01547                                    ObjectIdGetDatum(opfamilyoid),
01548                                    ObjectIdGetDatum(op->lefttype),
01549                                    ObjectIdGetDatum(op->righttype),
01550                                    Int16GetDatum(op->number));
01551         if (!OidIsValid(amprocid))
01552             ereport(ERROR,
01553                     (errcode(ERRCODE_UNDEFINED_OBJECT),
01554                      errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
01555                             op->number,
01556                             format_type_be(op->lefttype),
01557                             format_type_be(op->righttype),
01558                             NameListToString(opfamilyname))));
01559 
01560         object.classId = AccessMethodProcedureRelationId;
01561         object.objectId = amprocid;
01562         object.objectSubId = 0;
01563 
01564         performDeletion(&object, DROP_RESTRICT, 0);
01565     }
01566 }
01567 
01568 /*
01569  * Deletion subroutines for use by dependency.c.
01570  */
01571 void
01572 RemoveOpFamilyById(Oid opfamilyOid)
01573 {
01574     Relation    rel;
01575     HeapTuple   tup;
01576 
01577     rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
01578 
01579     tup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyOid));
01580     if (!HeapTupleIsValid(tup)) /* should not happen */
01581         elog(ERROR, "cache lookup failed for opfamily %u", opfamilyOid);
01582 
01583     simple_heap_delete(rel, &tup->t_self);
01584 
01585     ReleaseSysCache(tup);
01586 
01587     heap_close(rel, RowExclusiveLock);
01588 }
01589 
01590 void
01591 RemoveOpClassById(Oid opclassOid)
01592 {
01593     Relation    rel;
01594     HeapTuple   tup;
01595 
01596     rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
01597 
01598     tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
01599     if (!HeapTupleIsValid(tup)) /* should not happen */
01600         elog(ERROR, "cache lookup failed for opclass %u", opclassOid);
01601 
01602     simple_heap_delete(rel, &tup->t_self);
01603 
01604     ReleaseSysCache(tup);
01605 
01606     heap_close(rel, RowExclusiveLock);
01607 }
01608 
01609 void
01610 RemoveAmOpEntryById(Oid entryOid)
01611 {
01612     Relation    rel;
01613     HeapTuple   tup;
01614     ScanKeyData skey[1];
01615     SysScanDesc scan;
01616 
01617     ScanKeyInit(&skey[0],
01618                 ObjectIdAttributeNumber,
01619                 BTEqualStrategyNumber, F_OIDEQ,
01620                 ObjectIdGetDatum(entryOid));
01621 
01622     rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
01623 
01624     scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
01625                               SnapshotNow, 1, skey);
01626 
01627     /* we expect exactly one match */
01628     tup = systable_getnext(scan);
01629     if (!HeapTupleIsValid(tup))
01630         elog(ERROR, "could not find tuple for amop entry %u", entryOid);
01631 
01632     simple_heap_delete(rel, &tup->t_self);
01633 
01634     systable_endscan(scan);
01635     heap_close(rel, RowExclusiveLock);
01636 }
01637 
01638 void
01639 RemoveAmProcEntryById(Oid entryOid)
01640 {
01641     Relation    rel;
01642     HeapTuple   tup;
01643     ScanKeyData skey[1];
01644     SysScanDesc scan;
01645 
01646     ScanKeyInit(&skey[0],
01647                 ObjectIdAttributeNumber,
01648                 BTEqualStrategyNumber, F_OIDEQ,
01649                 ObjectIdGetDatum(entryOid));
01650 
01651     rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
01652 
01653     scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
01654                               SnapshotNow, 1, skey);
01655 
01656     /* we expect exactly one match */
01657     tup = systable_getnext(scan);
01658     if (!HeapTupleIsValid(tup))
01659         elog(ERROR, "could not find tuple for amproc entry %u", entryOid);
01660 
01661     simple_heap_delete(rel, &tup->t_self);
01662 
01663     systable_endscan(scan);
01664     heap_close(rel, RowExclusiveLock);
01665 }
01666 
01667 static char *
01668 get_am_name(Oid amOid)
01669 {
01670     HeapTuple   tup;
01671     char       *result = NULL;
01672 
01673     tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
01674     if (HeapTupleIsValid(tup))
01675     {
01676         result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
01677         ReleaseSysCache(tup);
01678     }
01679     return result;
01680 }
01681 
01682 /*
01683  * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
01684  *
01685  * Is there an operator class with the given name and signature already
01686  * in the given namespace?  If so, raise an appropriate error message.
01687  */
01688 void
01689 IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
01690                           Oid opcnamespace)
01691 {
01692     /* make sure the new name doesn't exist */
01693     if (SearchSysCacheExists3(CLAAMNAMENSP,
01694                               ObjectIdGetDatum(opcmethod),
01695                               CStringGetDatum(opcname),
01696                               ObjectIdGetDatum(opcnamespace)))
01697         ereport(ERROR,
01698                 (errcode(ERRCODE_DUPLICATE_OBJECT),
01699                  errmsg("operator class \"%s\" for access method \"%s\" already exists in schema \"%s\"",
01700                         opcname,
01701                         get_am_name(opcmethod),
01702                         get_namespace_name(opcnamespace))));
01703 }
01704 
01705 /*
01706  * Subroutine for ALTER OPERATOR FAMILY SET SCHEMA/RENAME
01707  *
01708  * Is there an operator family with the given name and signature already
01709  * in the given namespace?  If so, raise an appropriate error message.
01710  */
01711 void
01712 IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
01713                            Oid opfnamespace)
01714 {
01715     /* make sure the new name doesn't exist */
01716     if (SearchSysCacheExists3(OPFAMILYAMNAMENSP,
01717                               ObjectIdGetDatum(opfmethod),
01718                               CStringGetDatum(opfname),
01719                               ObjectIdGetDatum(opfnamespace)))
01720         ereport(ERROR,
01721                 (errcode(ERRCODE_DUPLICATE_OBJECT),
01722                  errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
01723                         opfname,
01724                         get_am_name(opfmethod),
01725                         get_namespace_name(opfnamespace))));
01726 }
01727 
01728 /*
01729  * get_am_oid - given an access method name, look up the OID
01730  *
01731  * If missing_ok is false, throw an error if access method not found.  If
01732  * true, just return InvalidOid.
01733  */
01734 Oid
01735 get_am_oid(const char *amname, bool missing_ok)
01736 {
01737     Oid         oid;
01738 
01739     oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
01740     if (!OidIsValid(oid) && !missing_ok)
01741         ereport(ERROR,
01742                 (errcode(ERRCODE_UNDEFINED_OBJECT),
01743                  errmsg("access method \"%s\" does not exist", amname)));
01744     return oid;
01745 }