]> granicus.if.org Git - postgresql/blobdiff - src/backend/catalog/pg_operator.c
Phase 2 of pgindent updates.
[postgresql] / src / backend / catalog / pg_operator.c
index 5ce7655edd47010bd9c30f8e19ff37885d15c7ca..370683823fa96c6ff150cfc4ce8f78cdbc5f5e8e 100644 (file)
@@ -3,11 +3,12 @@
  * pg_operator.c
  *       routines to support manipulation of the pg_operator relation
  *
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.44 1999/11/24 00:44:29 momjian Exp $
+ *       src/backend/catalog/pg_operator.c
  *
  * NOTES
  *       these routines moved here from commands/define.c and somewhat cleaned up.
 #include "postgres.h"
 
 #include "access/heapam.h"
-#include "catalog/catname.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_namespace.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_operator_fn.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
-#include "parser/parse_func.h"
+#include "parser/parse_oper.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
 #include "utils/syscache.h"
 
 
-static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc,
-                                                       const char *operatorName,
-                                                       Oid leftObjectId,
-                                                       Oid rightObjectId,
-                                                       bool *defined);
-
-static Oid OperatorGet(char *operatorName,
-                       char *leftTypeName,
-                       char *rightTypeName,
+static Oid OperatorGet(const char *operatorName,
+                       Oid operatorNamespace,
+                       Oid leftObjectId,
+                       Oid rightObjectId,
                        bool *defined);
 
-static Oid OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
-                                                                 char *operatorName,
-                                                                 Oid leftObjectId,
-                                                                 Oid rightObjectId);
-
-static Oid OperatorShellMake(char *operatorName,
-                                 char *leftTypeName,
-                                 char *rightTypeName);
-
-static void OperatorDef(char *operatorName,
-                       char *leftTypeName,
-                       char *rightTypeName,
-                       char *procedureName,
-                       uint16 precedence,
-                       bool isLeftAssociative,
-                       char *commutatorName,
-                       char *negatorName,
-                       char *restrictionName,
-                       char *oinName,
-                       bool canHash,
-                       char *leftSortName,
-                       char *rightSortName);
-
-static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
-
-/* ----------------------------------------------------------------
- *             OperatorGetWithOpenRelation
+static Oid OperatorLookup(List *operatorName,
+                          Oid leftObjectId,
+                          Oid rightObjectId,
+                          bool *defined);
+
+static Oid OperatorShellMake(const char *operatorName,
+                                 Oid operatorNamespace,
+                                 Oid leftTypeId,
+                                 Oid rightTypeId);
+
+static Oid get_other_operator(List *otherOp,
+                                  Oid otherLeftTypeId, Oid otherRightTypeId,
+                                  const char *operatorName, Oid operatorNamespace,
+                                  Oid leftTypeId, Oid rightTypeId,
+                                  bool isCommutator);
+
+
+/*
+ * Check whether a proposed operator name is legal
+ *
+ * This had better match the behavior of parser/scan.l!
  *
- *             preforms a scan on pg_operator for an operator tuple
- *             with given name and left/right type oids.
- * ----------------------------------------------------------------
- *       pg_operator_desc      -- reldesc for pg_operator
- *       operatorName          -- name of operator to fetch
- *       leftObjectId          -- left data type oid of operator to fetch
- *       rightObjectId         -- right data type oid of operator to fetch
- *       defined                       -- set TRUE if defined (not a shell)
+ * We need this because the parser is not smart enough to check that
+ * the arguments of CREATE OPERATOR's COMMUTATOR, NEGATOR, etc clauses
+ * are operator names rather than some other lexical entity.
  */
-static Oid
-OperatorGetWithOpenRelation(Relation pg_operator_desc,
-                                                       const char *operatorName,
-                                                       Oid leftObjectId,
-                                                       Oid rightObjectId,
-                                                       bool *defined)
+static bool
+validOperatorName(const char *name)
 {
-       HeapScanDesc pg_operator_scan;
-       Oid                     operatorObjectId;
-       HeapTuple       tup;
+       size_t          len = strlen(name);
 
-       static ScanKeyData opKey[3] = {
-               {0, Anum_pg_operator_oprname, F_NAMEEQ},
-               {0, Anum_pg_operator_oprleft, F_OIDEQ},
-               {0, Anum_pg_operator_oprright, F_OIDEQ},
-       };
-
-       fmgr_info(F_NAMEEQ, &opKey[0].sk_func);
-       fmgr_info(F_OIDEQ, &opKey[1].sk_func);
-       fmgr_info(F_OIDEQ, &opKey[2].sk_func);
-       opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
-       opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs;
-       opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs;
-
-       /* ----------------
-        *      form scan key
-        * ----------------
-        */
-       opKey[0].sk_argument = PointerGetDatum(operatorName);
-       opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId);
-       opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId);
+       /* Can't be empty or too long */
+       if (len == 0 || len >= NAMEDATALEN)
+               return false;
 
-       /* ----------------
-        *      begin the scan
-        * ----------------
-        */
-       pg_operator_scan = heap_beginscan(pg_operator_desc,
-                                                                         0,
-                                                                         SnapshotSelf,         /* no cache? */
-                                                                         3,
-                                                                         opKey);
-
-       /* ----------------
-        *      fetch the operator tuple, if it exists, and determine
-        *      the proper return oid value.
-        * ----------------
+       /* Can't contain any invalid characters */
+       /* Test string here should match op_chars in scan.l */
+       if (strspn(name, "~!@#^&|`?+-*/%<>=") != len)
+               return false;
+
+       /* Can't contain slash-star or dash-dash (comment starts) */
+       if (strstr(name, "/*") || strstr(name, "--"))
+               return false;
+
+       /*
+        * For SQL standard compatibility, '+' and '-' cannot be the last char of
+        * a multi-char operator unless the operator contains chars that are not
+        * in SQL operators. The idea is to lex '=-' as two operators, but not to
+        * forbid operator names like '?-' that could not be sequences of standard
+        * SQL operators.
         */
-       tup = heap_getnext(pg_operator_scan, 0);
+       if (len > 1 &&
+               (name[len - 1] == '+' ||
+                name[len - 1] == '-'))
+       {
+               int                     ic;
 
+               for (ic = len - 2; ic >= 0; ic--)
+               {
+                       if (strchr("~!@#^&|`?%", name[ic]))
+                               break;
+               }
+               if (ic < 0)
+                       return false;           /* nope, not valid */
+       }
+
+       /* != isn't valid either, because parser will convert it to <> */
+       if (strcmp(name, "!=") == 0)
+               return false;
+
+       return true;
+}
+
+
+/*
+ * OperatorGet
+ *
+ *             finds an operator given an exact specification (name, namespace,
+ *             left and right type IDs).
+ *
+ *             *defined is set TRUE if defined (not a shell)
+ */
+static Oid
+OperatorGet(const char *operatorName,
+                       Oid operatorNamespace,
+                       Oid leftObjectId,
+                       Oid rightObjectId,
+                       bool *defined)
+{
+       HeapTuple       tup;
+       Oid                     operatorObjectId;
+
+       tup = SearchSysCache4(OPERNAMENSP,
+                                                 PointerGetDatum(operatorName),
+                                                 ObjectIdGetDatum(leftObjectId),
+                                                 ObjectIdGetDatum(rightObjectId),
+                                                 ObjectIdGetDatum(operatorNamespace));
        if (HeapTupleIsValid(tup))
        {
-               regproc         oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
+               RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
 
-               operatorObjectId = tup->t_data->t_oid;
+               operatorObjectId = HeapTupleGetOid(tup);
                *defined = RegProcedureIsValid(oprcode);
+               ReleaseSysCache(tup);
        }
        else
        {
@@ -138,247 +155,162 @@ OperatorGetWithOpenRelation(Relation pg_operator_desc,
                *defined = false;
        }
 
-       /* ----------------
-        *      close the scan and return the oid.
-        * ----------------
-        */
-       heap_endscan(pg_operator_scan);
-
        return operatorObjectId;
 }
 
-/* ----------------------------------------------------------------
- *             OperatorGet
+/*
+ * OperatorLookup
  *
- *             finds the operator associated with the specified name
- *             and left and right type names.
- * ----------------------------------------------------------------
+ *             looks up an operator given a possibly-qualified name and
+ *             left and right type IDs.
+ *
+ *             *defined is set TRUE if defined (not a shell)
  */
 static Oid
-OperatorGet(char *operatorName,
-                       char *leftTypeName,
-                       char *rightTypeName,
-                       bool *defined)
+OperatorLookup(List *operatorName,
+                          Oid leftObjectId,
+                          Oid rightObjectId,
+                          bool *defined)
 {
-       Relation        pg_operator_desc;
-
        Oid                     operatorObjectId;
-       Oid                     leftObjectId = InvalidOid;
-       Oid                     rightObjectId = InvalidOid;
-       bool            leftDefined = false;
-       bool            rightDefined = false;
-
-       /* ----------------
-        *      look up the operator data types.
-        *
-        *      Note: types must be defined before operators
-        * ----------------
-        */
-       if (leftTypeName)
-       {
-               leftObjectId = TypeGet(leftTypeName, &leftDefined);
-
-               if (!OidIsValid(leftObjectId) || !leftDefined)
-                       elog(ERROR, "OperatorGet: left type '%s' nonexistent",
-                                leftTypeName);
-       }
+       RegProcedure oprcode;
 
-       if (rightTypeName)
+       operatorObjectId = LookupOperName(NULL, operatorName,
+                                                                         leftObjectId, rightObjectId,
+                                                                         true, -1);
+       if (!OidIsValid(operatorObjectId))
        {
-               rightObjectId = TypeGet(rightTypeName, &rightDefined);
-
-               if (!OidIsValid(rightObjectId) || !rightDefined)
-                       elog(ERROR, "OperatorGet: right type '%s' nonexistent",
-                                rightTypeName);
+               *defined = false;
+               return InvalidOid;
        }
 
-       if (!((OidIsValid(leftObjectId) && leftDefined) ||
-                 (OidIsValid(rightObjectId) && rightDefined)))
-               elog(ERROR, "OperatorGet: must have at least one argument type");
-
-       /* ----------------
-        *      open the pg_operator relation
-        * ----------------
-        */
-       pg_operator_desc = heap_openr(OperatorRelationName, AccessShareLock);
-
-       /* ----------------
-        *      get the oid for the operator with the appropriate name
-        *      and left/right types.
-        * ----------------
-        */
-       operatorObjectId = OperatorGetWithOpenRelation(pg_operator_desc,
-                                                                                                  operatorName,
-                                                                                                  leftObjectId,
-                                                                                                  rightObjectId,
-                                                                                                  defined);
-
-       /* ----------------
-        *      close the relation and return the operator oid.
-        * ----------------
-        */
-       heap_close(pg_operator_desc, AccessShareLock);
+       oprcode = get_opcode(operatorObjectId);
+       *defined = RegProcedureIsValid(oprcode);
 
        return operatorObjectId;
 }
 
-/* ----------------------------------------------------------------
- *             OperatorShellMakeWithOpenRelation
- *
- * ----------------------------------------------------------------
+
+/*
+ * OperatorShellMake
+ *             Make a "shell" entry for a not-yet-existing operator.
  */
 static Oid
-OperatorShellMakeWithOpenRelation(Relation pg_operator_desc,
-                                                                 char *operatorName,
-                                                                 Oid leftObjectId,
-                                                                 Oid rightObjectId)
+OperatorShellMake(const char *operatorName,
+                                 Oid operatorNamespace,
+                                 Oid leftTypeId,
+                                 Oid rightTypeId)
 {
+       Relation        pg_operator_desc;
+       Oid                     operatorObjectId;
        int                     i;
        HeapTuple       tup;
        Datum           values[Natts_pg_operator];
-       char            nulls[Natts_pg_operator];
-       Oid                     operatorObjectId;
+       bool            nulls[Natts_pg_operator];
        NameData        oname;
        TupleDesc       tupDesc;
 
-       /* ----------------
-        *      initialize our *nulls and *values arrays
-        * ----------------
+       /*
+        * validate operator name
+        */
+       if (!validOperatorName(operatorName))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_NAME),
+                                errmsg("\"%s\" is not a valid operator name",
+                                               operatorName)));
+
+       /*
+        * initialize our *nulls and *values arrays
         */
        for (i = 0; i < Natts_pg_operator; ++i)
        {
-               nulls[i] = ' ';
-               values[i] = (Datum) NULL;               /* redundant, but safe */
+               nulls[i] = false;
+               values[i] = (Datum) NULL;       /* redundant, but safe */
        }
 
-       /* ----------------
-        *      initialize *values with the operator name and input data types.
-        *      Note that oprcode is set to InvalidOid, indicating it's a shell.
-        * ----------------
+       /*
+        * initialize values[] with the operator name and input data types. Note
+        * that oprcode is set to InvalidOid, indicating it's a shell.
         */
-       i = 0;
        namestrcpy(&oname, operatorName);
-       values[i++] = NameGetDatum(&oname);
-       values[i++] = Int32GetDatum(GetUserId());
-       values[i++] = (Datum) (uint16) 0;
-       values[i++] = (Datum) 'b';      /* assume it's binary */
-       values[i++] = (Datum) (bool) 0;
-       values[i++] = (Datum) (bool) 0;
-       values[i++] = ObjectIdGetDatum(leftObjectId);           /* <-- left oid */
-       values[i++] = ObjectIdGetDatum(rightObjectId);          /* <-- right oid */
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-       values[i++] = ObjectIdGetDatum(InvalidOid);
-
-       /* ----------------
-        *      create a new operator tuple
-        * ----------------
+       values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
+       values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
+       values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
+       values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l');
+       values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(false);
+       values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(false);
+       values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
+       values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
+       values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
+       values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
+
+       /*
+        * open pg_operator
         */
+       pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
        tupDesc = pg_operator_desc->rd_att;
 
-       tup = heap_formtuple(tupDesc,
-                                                values,
-                                                nulls);
-
-       /* ----------------
-        *      insert our "shell" operator tuple and
-        *      close the relation
-        * ----------------
+       /*
+        * create a new operator tuple
         */
-       heap_insert(pg_operator_desc, tup);
-       operatorObjectId = tup->t_data->t_oid;
-
-       if (RelationGetForm(pg_operator_desc)->relhasindex)
-       {
-               Relation        idescs[Num_pg_operator_indices];
-
-               CatalogOpenIndices(Num_pg_operator_indices, Name_pg_operator_indices, idescs);
-               CatalogIndexInsert(idescs, Num_pg_operator_indices, pg_operator_desc, tup);
-               CatalogCloseIndices(Num_pg_operator_indices, idescs);
-       }
+       tup = heap_form_tuple(tupDesc, values, nulls);
 
-       /* ----------------
-        *      free the tuple and return the operator oid
-        * ----------------
+       /*
+        * insert our "shell" operator tuple
         */
-       pfree(tup);
-
-       return operatorObjectId;
-}
-
-/* ----------------------------------------------------------------
- *             OperatorShellMake
- *
- *             Specify operator name and left and right type names,
- *             fill an operator struct with this info and NULL's,
- *             call heap_insert and return the Oid
- *             to the caller.
- * ----------------------------------------------------------------
- */
-static Oid
-OperatorShellMake(char *operatorName,
-                                 char *leftTypeName,
-                                 char *rightTypeName)
-{
-       Relation        pg_operator_desc;
-       Oid                     operatorObjectId;
+       operatorObjectId = CatalogTupleInsert(pg_operator_desc, tup);
 
-       Oid                     leftObjectId = InvalidOid;
-       Oid                     rightObjectId = InvalidOid;
-       bool            leftDefined = false;
-       bool            rightDefined = false;
+       /* Add dependencies for the entry */
+       makeOperatorDependencies(tup, false);
 
-       /* ----------------
-        *      get the left and right type oid's for this operator
-        * ----------------
-        */
-       if (leftTypeName)
-               leftObjectId = TypeGet(leftTypeName, &leftDefined);
-
-       if (rightTypeName)
-               rightObjectId = TypeGet(rightTypeName, &rightDefined);
+       heap_freetuple(tup);
 
-       if (!((OidIsValid(leftObjectId) && leftDefined) ||
-                 (OidIsValid(rightObjectId) && rightDefined)))
-               elog(ERROR, "OperatorShellMake: no valid argument types??");
+       /* Post creation hook for new shell operator */
+       InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
 
-       /* ----------------
-        *      open pg_operator
-        * ----------------
+       /*
+        * Make sure the tuple is visible for subsequent lookups/updates.
         */
-       pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
+       CommandCounterIncrement();
 
-       /* ----------------
-        *      add a "shell" operator tuple to the operator relation
-        *      and recover the shell tuple's oid.
-        * ----------------
-        */
-       operatorObjectId = OperatorShellMakeWithOpenRelation(pg_operator_desc,
-                                                                                                                operatorName,
-                                                                                                                leftObjectId,
-                                                                                                                rightObjectId);
-       /* ----------------
-        *      close the operator relation and return the oid.
-        * ----------------
+       /*
+        * close the operator relation and return the oid.
         */
        heap_close(pg_operator_desc, RowExclusiveLock);
 
        return operatorObjectId;
 }
 
-/* --------------------------------
- * OperatorDef
+/*
+ * OperatorCreate
+ *
+ * "X" indicates an optional argument (i.e. one that can be NULL or 0)
+ *             operatorName                    name for new operator
+ *             operatorNamespace               namespace for new operator
+ *             leftTypeId                              X left type ID
+ *             rightTypeId                             X right type ID
+ *             procedureId                             procedure ID for operator
+ *             commutatorName                  X commutator operator
+ *             negatorName                             X negator operator
+ *             restrictionId                   X restriction selectivity procedure ID
+ *             joinId                                  X join selectivity procedure ID
+ *             canMerge                                merge join can be used with this operator
+ *             canHash                                 hash join can be used with this operator
+ *
+ * The caller should have validated properties and permissions for the
+ * objects passed as OID references.  We must handle the commutator and
+ * negator operator references specially, however, since those need not
+ * exist beforehand.
  *
  * This routine gets complicated because it allows the user to
  * specify operators that do not exist.  For example, if operator
  * "op" is being defined, the negator operator "negop" and the
  * commutator "commop" can also be defined without specifying
- * any information other than their names.     Since in order to
+ * any information other than their names.  Since in order to
  * add "op" to the PG_OPERATOR catalog, all the Oid's for these
  * operators must be placed in the fields of "op", a forward
  * declaration is done on the commutator and negator operators.
@@ -387,704 +319,546 @@ OperatorShellMake(char *operatorName,
  * information about the operator (just its name and types).
  * Forward declaration is used only for this purpose, it is
  * not available to the user as it is for type definition.
- *
- * Algorithm:
- *
- * check if operator already defined
- *       if so, but oprcode is null, save the Oid -- we are filling in a shell
- *       otherwise error
- * get the attribute types from relation descriptor for pg_operator
- * assign values to the fields of the operator:
- *      operatorName
- *      owner id (simply the user id of the caller)
- *      precedence
- *      operator "kind" either "b" for binary or "l" for left unary
- *      isLeftAssociative boolean
- *      canHash boolean
- *      leftTypeObjectId -- type must already be defined
- *      rightTypeObjectId -- this is optional, enter ObjectId=0 if none specified
- *      resultType -- defer this, since it must be determined from
- *                                the pg_procedure catalog
- *      commutatorObjectId -- if this is NULL, enter ObjectId=0
- *                                       else if this already exists, enter it's ObjectId
- *                                       else if this does not yet exist, and is not
- *                                             the same as the main operatorName, then create
- *                                             a shell and enter the new ObjectId
- *                                       else if this does not exist but IS the same
- *                                             name & types as the main operator, set the ObjectId=0.
- *                                             (We are creating a self-commutating operator.)
- *                                             The link will be fixed later by OperatorUpd.
- *      negatorObjectId   -- same as for commutatorObjectId
- *      leftSortObjectId  -- same as for commutatorObjectId
- *      rightSortObjectId -- same as for commutatorObjectId
- *      operatorProcedure -- must access the pg_procedure catalog to get the
- *                                ObjectId of the procedure that actually does the operator
- *                                actions this is required.  Do an amgetattr to find out the
- *                                return type of the procedure
- *      restrictionProcedure -- must access the pg_procedure catalog to get
- *                                the ObjectId but this is optional
- *      joinProcedure -- same as restrictionProcedure
- * now either insert or replace the operator into the pg_operator catalog
- * if the operator shell is being filled in
- *      access the catalog in order to get a valid buffer
- *      create a tuple using ModifyHeapTuple
- *      get the t_self from the modified tuple and call RelationReplaceHeapTuple
- * else if a new operator is being created
- *      create a tuple using heap_formtuple
- *      call heap_insert
- * --------------------------------
- *             "X" indicates an optional argument (i.e. one that can be NULL)
- *             operatorName;                   -- operator name
- *             leftTypeName;                   -- X left type name
- *             rightTypeName;                  -- X right type name
- *             procedureName;                  -- procedure name for operator code
- *             precedence;                             -- operator precedence
- *             isLeftAssociative;              -- operator is left associative?
- *             commutatorName;                 -- X commutator operator name
- *             negatorName;                    -- X negator operator name
- *             restrictionName;                -- X restriction sel. procedure name
- *             joinName;                               -- X join sel. procedure name
- *             canHash;                                -- can hash join be used with operator?
- *             leftSortName;                   -- X left sort operator (for merge join)
- *             rightSortName;                  -- X right sort operator (for merge join)
  */
-static void
-OperatorDef(char *operatorName,
-                       char *leftTypeName,
-                       char *rightTypeName,
-                       char *procedureName,
-                       uint16 precedence,
-                       bool isLeftAssociative,
-                       char *commutatorName,
-                       char *negatorName,
-                       char *restrictionName,
-                       char *joinName,
-                       bool canHash,
-                       char *leftSortName,
-                       char *rightSortName)
+ObjectAddress
+OperatorCreate(const char *operatorName,
+                          Oid operatorNamespace,
+                          Oid leftTypeId,
+                          Oid rightTypeId,
+                          Oid procedureId,
+                          List *commutatorName,
+                          List *negatorName,
+                          Oid restrictionId,
+                          Oid joinId,
+                          bool canMerge,
+                          bool canHash)
 {
-       int                     i,
-                               j;
        Relation        pg_operator_desc;
-
-       HeapScanDesc pg_operator_scan;
        HeapTuple       tup;
-       char            nulls[Natts_pg_operator];
-       char            replaces[Natts_pg_operator];
+       bool            isUpdate;
+       bool            nulls[Natts_pg_operator];
+       bool            replaces[Natts_pg_operator];
        Datum           values[Natts_pg_operator];
        Oid                     operatorObjectId;
        bool            operatorAlreadyDefined;
-       Oid                     leftTypeId = InvalidOid;
-       Oid                     rightTypeId = InvalidOid;
-       Oid                     commutatorId = InvalidOid;
-       Oid                     negatorId = InvalidOid;
-       bool            leftDefined = false;
-       bool            rightDefined = false;
+       Oid                     operResultType;
+       Oid                     commutatorId,
+                               negatorId;
        bool            selfCommutator = false;
-       char       *name[4];
-       Oid                     typeId[8];
-       int                     nargs;
        NameData        oname;
-       TupleDesc       tupDesc;
-
-       static ScanKeyData opKey[3] = {
-               {0, Anum_pg_operator_oprname, F_NAMEEQ},
-               {0, Anum_pg_operator_oprleft, F_OIDEQ},
-               {0, Anum_pg_operator_oprright, F_OIDEQ},
-       };
-
-       fmgr_info(F_NAMEEQ, &opKey[0].sk_func);
-       fmgr_info(F_OIDEQ, &opKey[1].sk_func);
-       fmgr_info(F_OIDEQ, &opKey[2].sk_func);
-       opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
-       opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs;
-       opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs;
-
-       operatorObjectId = OperatorGet(operatorName,
-                                                                  leftTypeName,
-                                                                  rightTypeName,
-                                                                  &operatorAlreadyDefined);
-
-       if (operatorAlreadyDefined)
-               elog(ERROR, "OperatorDef: operator \"%s\" already defined",
-                        operatorName);
+       int                     i;
+       ObjectAddress address;
 
        /*
-        * At this point, if operatorObjectId is not InvalidOid then we are
-        * filling in a previously-created shell.
+        * Sanity checks
         */
+       if (!validOperatorName(operatorName))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_NAME),
+                                errmsg("\"%s\" is not a valid operator name",
+                                               operatorName)));
 
-       /* ----------------
-        *      look up the operator data types.
-        *
-        *      Note: types must be defined before operators
-        * ----------------
-        */
-       if (leftTypeName)
+       if (!(OidIsValid(leftTypeId) && OidIsValid(rightTypeId)))
        {
-               leftTypeId = TypeGet(leftTypeName, &leftDefined);
-
-               if (!OidIsValid(leftTypeId) || !leftDefined)
-                       elog(ERROR, "OperatorDef: left type '%s' nonexistent",
-                                leftTypeName);
+               /* If it's not a binary op, these things mustn't be set: */
+               if (commutatorName)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only binary operators can have commutators")));
+               if (OidIsValid(joinId))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                errmsg("only binary operators can have join selectivity")));
+               if (canMerge)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only binary operators can merge join")));
+               if (canHash)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only binary operators can hash")));
        }
 
-       if (rightTypeName)
-       {
-               rightTypeId = TypeGet(rightTypeName, &rightDefined);
+       operResultType = get_func_rettype(procedureId);
 
-               if (!OidIsValid(rightTypeId) || !rightDefined)
-                       elog(ERROR, "OperatorDef: right type '%s' nonexistent",
-                                rightTypeName);
+       if (operResultType != BOOLOID)
+       {
+               /* If it's not a boolean op, these things mustn't be set: */
+               if (negatorName)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only boolean operators can have negators")));
+               if (OidIsValid(restrictionId))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only boolean operators can have restriction selectivity")));
+               if (OidIsValid(joinId))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                               errmsg("only boolean operators can have join selectivity")));
+               if (canMerge)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only boolean operators can merge join")));
+               if (canHash)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                        errmsg("only boolean operators can hash")));
        }
 
-       if (!((OidIsValid(leftTypeId) && leftDefined) ||
-                 (OidIsValid(rightTypeId) && rightDefined)))
-               elog(ERROR, "OperatorDef: must have at least one argument type");
+       operatorObjectId = OperatorGet(operatorName,
+                                                                  operatorNamespace,
+                                                                  leftTypeId,
+                                                                  rightTypeId,
+                                                                  &operatorAlreadyDefined);
 
-       for (i = 0; i < Natts_pg_operator; ++i)
-       {
-               values[i] = (Datum) NULL;
-               replaces[i] = 'r';
-               nulls[i] = ' ';
-       }
+       if (operatorAlreadyDefined)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_FUNCTION),
+                                errmsg("operator %s already exists",
+                                               operatorName)));
 
-       /* ----------------
-        * Look up registered procedures -- find the return type
-        * of procedureName to place in "result" field.
-        * Do this before shells are created so we don't
-        * have to worry about deleting them later.
-        * ----------------
+       /*
+        * At this point, if operatorObjectId is not InvalidOid then we are
+        * filling in a previously-created shell.  Insist that the user own any
+        * such shell.
         */
-       MemSet(typeId, 0, 8 * sizeof(Oid));
-       if (!leftTypeName)
-       {
-               typeId[0] = rightTypeId;
-               nargs = 1;
-       }
-       else if (!rightTypeName)
-       {
-               typeId[0] = leftTypeId;
-               nargs = 1;
-       }
-       else
-       {
-               typeId[0] = leftTypeId;
-               typeId[1] = rightTypeId;
-               nargs = 2;
-       }
-       tup = SearchSysCacheTuple(PROCNAME,
-                                                         PointerGetDatum(procedureName),
-                                                         Int32GetDatum(nargs),
-                                                         PointerGetDatum(typeId),
-                                                         0);
-
-       if (!HeapTupleIsValid(tup))
-               func_error("OperatorDef", procedureName, nargs, typeId, NULL);
-
-       values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(tup->t_data->t_oid);
-       values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(((Form_pg_proc)
-                                                                                       GETSTRUCT(tup))->prorettype);
-
-       /* ----------------
-        *      find restriction
-        * ----------------
+       if (OidIsValid(operatorObjectId) &&
+               !pg_oper_ownercheck(operatorObjectId, GetUserId()))
+               aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+                                          operatorName);
+
+       /*
+        * Set up the other operators.  If they do not currently exist, create
+        * shells in order to get ObjectId's.
         */
-       if (restrictionName)
-       {                                                       /* optional */
-               MemSet(typeId, 0, 8 * sizeof(Oid));
-               typeId[0] = OIDOID;             /* operator OID */
-               typeId[1] = OIDOID;             /* relation OID */
-               typeId[2] = INT2OID;    /* attribute number */
-               typeId[3] = 0;                  /* value - can be any type      */
-               typeId[4] = INT4OID;    /* flags - left or right selectivity */
-               tup = SearchSysCacheTuple(PROCNAME,
-                                                                 PointerGetDatum(restrictionName),
-                                                                 Int32GetDatum(5),
-                                                                 PointerGetDatum(typeId),
-                                                                 0);
-               if (!HeapTupleIsValid(tup))
-                       func_error("OperatorDef", restrictionName, 5, typeId, NULL);
 
-               values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(tup->t_data->t_oid);
+       if (commutatorName)
+       {
+               /* commutator has reversed arg types */
+               commutatorId = get_other_operator(commutatorName,
+                                                                                 rightTypeId, leftTypeId,
+                                                                                 operatorName, operatorNamespace,
+                                                                                 leftTypeId, rightTypeId,
+                                                                                 true);
+
+               /* Permission check: must own other operator */
+               if (OidIsValid(commutatorId) &&
+                       !pg_oper_ownercheck(commutatorId, GetUserId()))
+                       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+                                                  NameListToString(commutatorName));
+
+               /*
+                * self-linkage to this operator; will fix below. Note that only
+                * self-linkage for commutation makes sense.
+                */
+               if (!OidIsValid(commutatorId))
+                       selfCommutator = true;
        }
        else
-               values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
+               commutatorId = InvalidOid;
 
-       /* ----------------
-        *      find join - only valid for binary operators
-        * ----------------
-        */
-       if (joinName)
-       {                                                       /* optional */
-               MemSet(typeId, 0, 8 * sizeof(Oid));
-               typeId[0] = OIDOID;             /* operator OID */
-               typeId[1] = OIDOID;             /* relation OID 1 */
-               typeId[2] = INT2OID;    /* attribute number 1 */
-               typeId[3] = OIDOID;             /* relation OID 2 */
-               typeId[4] = INT2OID;    /* attribute number 2 */
-
-               tup = SearchSysCacheTuple(PROCNAME,
-                                                                 PointerGetDatum(joinName),
-                                                                 Int32GetDatum(5),
-                                                                 PointerGetDatum(typeId),
-                                                                 0);
-               if (!HeapTupleIsValid(tup))
-                       func_error("OperatorDef", joinName, 5, typeId, NULL);
-
-               values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(tup->t_data->t_oid);
+       if (negatorName)
+       {
+               /* negator has same arg types */
+               negatorId = get_other_operator(negatorName,
+                                                                          leftTypeId, rightTypeId,
+                                                                          operatorName, operatorNamespace,
+                                                                          leftTypeId, rightTypeId,
+                                                                          false);
+
+               /* Permission check: must own other operator */
+               if (OidIsValid(negatorId) &&
+                       !pg_oper_ownercheck(negatorId, GetUserId()))
+                       aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+                                                  NameListToString(negatorName));
        }
        else
-               values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
-
-       /* ----------------
-        * set up values in the operator tuple
-        * ----------------
-        */
-       i = 0;
-       namestrcpy(&oname, operatorName);
-       values[i++] = NameGetDatum(&oname);
-       values[i++] = Int32GetDatum(GetUserId());
-       values[i++] = UInt16GetDatum(precedence);
-       values[i++] = leftTypeName ? (rightTypeName ? 'b' : 'r') : 'l';
-       values[i++] = Int8GetDatum(isLeftAssociative);
-       values[i++] = Int8GetDatum(canHash);
-       values[i++] = ObjectIdGetDatum(leftTypeId);
-       values[i++] = ObjectIdGetDatum(rightTypeId);
-
-       ++i;                                            /* Skip "oprresult", it was filled in
-                                                                * above */
+               negatorId = InvalidOid;
 
        /*
-        * Set up the other operators.  If they do not currently exist, create
-        * shells in order to get ObjectId's.
+        * set up values in the operator tuple
         */
-       name[0] = commutatorName;
-       name[1] = negatorName;
-       name[2] = leftSortName;
-       name[3] = rightSortName;
 
-       for (j = 0; j < 4; ++j)
+       for (i = 0; i < Natts_pg_operator; ++i)
        {
-               if (name[j])
-               {
-                       char       *otherLeftTypeName = NULL;
-                       char       *otherRightTypeName = NULL;
-                       Oid                     otherLeftTypeId = InvalidOid;
-                       Oid                     otherRightTypeId = InvalidOid;
-                       Oid                     other_oid = InvalidOid;
-                       bool            otherDefined = false;
-
-                       switch (j)
-                       {
-                               case 0: /* commutator has reversed arg types */
-                                       otherLeftTypeName = rightTypeName;
-                                       otherRightTypeName = leftTypeName;
-                                       otherLeftTypeId = rightTypeId;
-                                       otherRightTypeId = leftTypeId;
-                                       other_oid = OperatorGet(name[j],
-                                                                                       otherLeftTypeName,
-                                                                                       otherRightTypeName,
-                                                                                       &otherDefined);
-                                       commutatorId = other_oid;
-                                       break;
-                               case 1: /* negator has same arg types */
-                                       otherLeftTypeName = leftTypeName;
-                                       otherRightTypeName = rightTypeName;
-                                       otherLeftTypeId = leftTypeId;
-                                       otherRightTypeId = rightTypeId;
-                                       other_oid = OperatorGet(name[j],
-                                                                                       otherLeftTypeName,
-                                                                                       otherRightTypeName,
-                                                                                       &otherDefined);
-                                       negatorId = other_oid;
-                                       break;
-                               case 2: /* left sort op takes left-side data type */
-                                       otherLeftTypeName = leftTypeName;
-                                       otherRightTypeName = leftTypeName;
-                                       otherLeftTypeId = leftTypeId;
-                                       otherRightTypeId = leftTypeId;
-                                       other_oid = OperatorGet(name[j],
-                                                                                       otherLeftTypeName,
-                                                                                       otherRightTypeName,
-                                                                                       &otherDefined);
-                                       break;
-                               case 3: /* right sort op takes right-side data
-                                                                * type */
-                                       otherLeftTypeName = rightTypeName;
-                                       otherRightTypeName = rightTypeName;
-                                       otherLeftTypeId = rightTypeId;
-                                       otherRightTypeId = rightTypeId;
-                                       other_oid = OperatorGet(name[j],
-                                                                                       otherLeftTypeName,
-                                                                                       otherRightTypeName,
-                                                                                       &otherDefined);
-                                       break;
-                       }
-
-                       if (OidIsValid(other_oid))
-                       {
-                               /* other op already in catalogs */
-                               values[i++] = ObjectIdGetDatum(other_oid);
-                       }
-                       else if (strcmp(operatorName, name[j]) != 0 ||
-                                        otherLeftTypeId != leftTypeId ||
-                                        otherRightTypeId != rightTypeId)
-                       {
-                               /* not in catalogs, different from operator */
-                               other_oid = OperatorShellMake(name[j],
-                                                                                         otherLeftTypeName,
-                                                                                         otherRightTypeName);
-                               if (!OidIsValid(other_oid))
-                                       elog(ERROR,
-                                                "OperatorDef: can't create operator shell '%s'",
-                                                name[j]);
-                               values[i++] = ObjectIdGetDatum(other_oid);
-                       }
-                       else
-                       {
-
-                               /*
-                                * self-linkage to this operator; will fix below. Note
-                                * that only self-linkage for commutation makes sense.
-                                */
-                               if (j != 0)
-                                       elog(ERROR,
-                                                "OperatorDef: operator can't be its own negator or sort op");
-                               selfCommutator = true;
-                               values[i++] = ObjectIdGetDatum(InvalidOid);
-                       }
-               }
-               else
-               {
-                       /* other operator is omitted */
-                       values[i++] = ObjectIdGetDatum(InvalidOid);
-               }
+               values[i] = (Datum) NULL;
+               replaces[i] = true;
+               nulls[i] = false;
        }
 
-       /* last three fields were filled in above */
-
-       pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
+       namestrcpy(&oname, operatorName);
+       values[Anum_pg_operator_oprname - 1] = NameGetDatum(&oname);
+       values[Anum_pg_operator_oprnamespace - 1] = ObjectIdGetDatum(operatorNamespace);
+       values[Anum_pg_operator_oprowner - 1] = ObjectIdGetDatum(GetUserId());
+       values[Anum_pg_operator_oprkind - 1] = CharGetDatum(leftTypeId ? (rightTypeId ? 'b' : 'r') : 'l');
+       values[Anum_pg_operator_oprcanmerge - 1] = BoolGetDatum(canMerge);
+       values[Anum_pg_operator_oprcanhash - 1] = BoolGetDatum(canHash);
+       values[Anum_pg_operator_oprleft - 1] = ObjectIdGetDatum(leftTypeId);
+       values[Anum_pg_operator_oprright - 1] = ObjectIdGetDatum(rightTypeId);
+       values[Anum_pg_operator_oprresult - 1] = ObjectIdGetDatum(operResultType);
+       values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(commutatorId);
+       values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(negatorId);
+       values[Anum_pg_operator_oprcode - 1] = ObjectIdGetDatum(procedureId);
+       values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionId);
+       values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinId);
+
+       pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
 
        /*
-        * If we are adding to an operator shell, get its t_self
+        * If we are replacing an operator shell, update; else insert
         */
        if (operatorObjectId)
        {
-               opKey[0].sk_argument = PointerGetDatum(operatorName);
-               opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId);
-               opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId);
-
-               pg_operator_scan = heap_beginscan(pg_operator_desc,
-                                                                                 0,
-                                                                                 SnapshotSelf, /* no cache? */
-                                                                                 3,
-                                                                                 opKey);
-
-               tup = heap_getnext(pg_operator_scan, 0);
-               if (HeapTupleIsValid(tup))
-               {
-                       tup = heap_modifytuple(tup,
-                                                                  pg_operator_desc,
-                                                                  values,
-                                                                  nulls,
-                                                                  replaces);
-
-                       setheapoverride(true);
-                       heap_update(pg_operator_desc, &tup->t_self, tup, NULL);
-                       setheapoverride(false);
-               }
-               else
-                       elog(ERROR, "OperatorDef: no operator %u", operatorObjectId);
+               isUpdate = true;
+
+               tup = SearchSysCacheCopy1(OPEROID,
+                                                                 ObjectIdGetDatum(operatorObjectId));
+               if (!HeapTupleIsValid(tup))
+                       elog(ERROR, "cache lookup failed for operator %u",
+                                operatorObjectId);
+
+               tup = heap_modify_tuple(tup,
+                                                               RelationGetDescr(pg_operator_desc),
+                                                               values,
+                                                               nulls,
+                                                               replaces);
 
-               heap_endscan(pg_operator_scan);
+               CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
        }
        else
        {
-               tupDesc = pg_operator_desc->rd_att;
-               tup = heap_formtuple(tupDesc, values, nulls);
+               isUpdate = false;
 
-               heap_insert(pg_operator_desc, tup);
-               operatorObjectId = tup->t_data->t_oid;
+               tup = heap_form_tuple(RelationGetDescr(pg_operator_desc),
+                                                         values, nulls);
 
+               operatorObjectId = CatalogTupleInsert(pg_operator_desc, tup);
        }
 
-       if (RelationGetForm(pg_operator_desc)->relhasindex)
-       {
-               Relation        idescs[Num_pg_operator_indices];
+       /* Add dependencies for the entry */
+       address = makeOperatorDependencies(tup, isUpdate);
 
-               CatalogOpenIndices(Num_pg_operator_indices, Name_pg_operator_indices, idescs);
-               CatalogIndexInsert(idescs, Num_pg_operator_indices, pg_operator_desc, tup);
-               CatalogCloseIndices(Num_pg_operator_indices, idescs);
-       }
+       /* Post creation hook for new operator */
+       InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
 
        heap_close(pg_operator_desc, RowExclusiveLock);
 
        /*
         * If a commutator and/or negator link is provided, update the other
-        * operator(s) to point at this one, if they don't already have a
-        * link. This supports an alternate style of operator definition
-        * wherein the user first defines one operator without giving negator
-        * or commutator, then defines the other operator of the pair with the
-        * proper commutator or negator attribute.      That style doesn't require
-        * creation of a shell, and it's the only style that worked right
-        * before Postgres version 6.5. This code also takes care of the
-        * situation where the new operator is its own commutator.
+        * operator(s) to point at this one, if they don't already have a link.
+        * This supports an alternative style of operator definition wherein the
+        * user first defines one operator without giving negator or commutator,
+        * then defines the other operator of the pair with the proper commutator
+        * or negator attribute.  That style doesn't require creation of a shell,
+        * and it's the only style that worked right before Postgres version 6.5.
+        * This code also takes care of the situation where the new operator is
+        * its own commutator.
         */
        if (selfCommutator)
                commutatorId = operatorObjectId;
 
        if (OidIsValid(commutatorId) || OidIsValid(negatorId))
-               OperatorUpd(operatorObjectId, commutatorId, negatorId);
+               OperatorUpd(operatorObjectId, commutatorId, negatorId, false);
+
+       return address;
 }
 
-/* ----------------------------------------------------------------
- * OperatorUpd
+/*
+ * Try to lookup another operator (commutator, etc)
  *
- *     For a given operator, look up its negator and commutator operators.
- *     If they are defined, but their negator and commutator fields
- *     (respectively) are empty, then use the new operator for neg or comm.
- *     This solves a problem for users who need to insert two new operators
- *     which are the negator or commutator of each other.
- * ----------------------------------------------------------------
+ * If not found, check to see if it is exactly the operator we are trying
+ * to define; if so, return InvalidOid.  (Note that this case is only
+ * sensible for a commutator, so we error out otherwise.)  If it is not
+ * the same operator, create a shell operator.
  */
-static void
-OperatorUpd(Oid baseId, Oid commId, Oid negId)
+static Oid
+get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
+                                  const char *operatorName, Oid operatorNamespace,
+                                  Oid leftTypeId, Oid rightTypeId, bool isCommutator)
 {
-       int                     i;
-       Relation        pg_operator_desc;
-       HeapScanDesc pg_operator_scan;
-       HeapTuple       tup;
-       char            nulls[Natts_pg_operator];
-       char            replaces[Natts_pg_operator];
-       Datum           values[Natts_pg_operator];
-
-       static ScanKeyData opKey[1] = {
-               {0, ObjectIdAttributeNumber, F_OIDEQ},
-       };
+       Oid                     other_oid;
+       bool            otherDefined;
+       char       *otherName;
+       Oid                     otherNamespace;
+       AclResult       aclresult;
+
+       other_oid = OperatorLookup(otherOp,
+                                                          otherLeftTypeId,
+                                                          otherRightTypeId,
+                                                          &otherDefined);
+
+       if (OidIsValid(other_oid))
+       {
+               /* other op already in catalogs */
+               return other_oid;
+       }
 
-       fmgr_info(F_OIDEQ, &opKey[0].sk_func);
-       opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs;
+       otherNamespace = QualifiedNameGetCreationNamespace(otherOp,
+                                                                                                          &otherName);
 
-       for (i = 0; i < Natts_pg_operator; ++i)
+       if (strcmp(otherName, operatorName) == 0 &&
+               otherNamespace == operatorNamespace &&
+               otherLeftTypeId == leftTypeId &&
+               otherRightTypeId == rightTypeId)
        {
-               values[i] = (Datum) NULL;
-               replaces[i] = ' ';
-               nulls[i] = ' ';
+               /*
+                * self-linkage to this operator; caller will fix later. Note that
+                * only self-linkage for commutation makes sense.
+                */
+               if (!isCommutator)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                        errmsg("operator cannot be its own negator or sort operator")));
+               return InvalidOid;
        }
 
-       pg_operator_desc = heap_openr(OperatorRelationName, RowExclusiveLock);
+       /* not in catalogs, different from operator, so make shell */
 
-       /* check and update the commutator, if necessary */
-       opKey[0].sk_argument = ObjectIdGetDatum(commId);
+       aclresult = pg_namespace_aclcheck(otherNamespace, GetUserId(),
+                                                                         ACL_CREATE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                                          get_namespace_name(otherNamespace));
 
-       pg_operator_scan = heap_beginscan(pg_operator_desc,
-                                                                         0,
-                                                                         SnapshotSelf,         /* no cache? */
-                                                                         1,
-                                                                         opKey);
+       other_oid = OperatorShellMake(otherName,
+                                                                 otherNamespace,
+                                                                 otherLeftTypeId,
+                                                                 otherRightTypeId);
+       return other_oid;
+}
 
-       tup = heap_getnext(pg_operator_scan, 0);
+/*
+ * OperatorUpd
+ *
+ *     For a given operator, look up its negator and commutator operators.
+ *     When isDelete is false, update their negator and commutator fields to
+ *     point back to the given operator; when isDelete is true, update those
+ *     fields to no longer point back to the given operator.
+ *
+ *     The !isDelete case solves a problem for users who need to insert two new
+ *     operators that are the negator or commutator of each other, while the
+ *     isDelete case is needed so as not to leave dangling OID links behind
+ *     after dropping an operator.
+ */
+void
+OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
+{
+       Relation        pg_operator_desc;
+       HeapTuple       tup;
 
        /*
-        * if the commutator and negator are the same operator, do one update.
-        * XXX this is probably useless code --- I doubt it ever makes sense
-        * for commutator and negator to be the same thing...
+        * If we're making an operator into its own commutator, then we need a
+        * command-counter increment here, since we've just inserted the tuple
+        * we're about to update.  But when we're dropping an operator, we can
+        * skip this because we're at the beginning of the command.
         */
-       if (commId == negId)
+       if (!isDelete)
+               CommandCounterIncrement();
+
+       /* Open the relation. */
+       pg_operator_desc = heap_open(OperatorRelationId, RowExclusiveLock);
+
+       /* Get a writable copy of the commutator's tuple. */
+       if (OidIsValid(commId))
+               tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(commId));
+       else
+               tup = NULL;
+
+       /* Update the commutator's tuple if need be. */
+       if (HeapTupleIsValid(tup))
        {
-               if (HeapTupleIsValid(tup))
+               Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
+               bool            update_commutator = false;
+
+               /*
+                * Out of due caution, we only change the commutator's oprcom field if
+                * it has the exact value we expected: InvalidOid when creating an
+                * operator, or baseId when dropping one.
+                */
+               if (isDelete && t->oprcom == baseId)
                {
-                       Form_pg_operator t;
-
-                       t = (Form_pg_operator) GETSTRUCT(tup);
-                       if (!OidIsValid(t->oprcom)
-                               || !OidIsValid(t->oprnegate))
-                       {
-
-                               if (!OidIsValid(t->oprnegate))
-                               {
-                                       values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
-                                       replaces[Anum_pg_operator_oprnegate - 1] = 'r';
-                               }
-
-                               if (!OidIsValid(t->oprcom))
-                               {
-                                       values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
-                                       replaces[Anum_pg_operator_oprcom - 1] = 'r';
-                               }
-
-                               tup = heap_modifytuple(tup,
-                                                                          pg_operator_desc,
-                                                                          values,
-                                                                          nulls,
-                                                                          replaces);
-
-                               setheapoverride(true);
-                               heap_update(pg_operator_desc, &tup->t_self, tup, NULL);
-                               setheapoverride(false);
-               
-                               if (RelationGetForm(pg_operator_desc)->relhasindex)
-                               {
-                                       Relation        idescs[Num_pg_operator_indices];
-                       
-                                       CatalogOpenIndices(Num_pg_operator_indices, Name_pg_operator_indices, idescs);
-                                       CatalogIndexInsert(idescs, Num_pg_operator_indices, pg_operator_desc, tup);
-                                       CatalogCloseIndices(Num_pg_operator_indices, idescs);
-                               }
-                       }
+                       t->oprcom = InvalidOid;
+                       update_commutator = true;
+               }
+               else if (!isDelete && !OidIsValid(t->oprcom))
+               {
+                       t->oprcom = baseId;
+                       update_commutator = true;
                }
-               heap_endscan(pg_operator_scan);
-
-               heap_close(pg_operator_desc, RowExclusiveLock);
 
-               return;
+               /* If any columns were found to need modification, update tuple. */
+               if (update_commutator)
+               {
+                       CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
+
+                       /*
+                        * Do CCI to make the updated tuple visible.  We must do this in
+                        * case the commutator is also the negator.  (Which would be a
+                        * logic error on the operator definer's part, but that's not a
+                        * good reason to fail here.)  We would need a CCI anyway in the
+                        * deletion case for a self-commutator with no negator.
+                        */
+                       CommandCounterIncrement();
+               }
        }
 
-       /* if commutator and negator are different, do two updates */
+       /*
+        * Similarly find and update the negator, if any.
+        */
+       if (OidIsValid(negId))
+               tup = SearchSysCacheCopy1(OPEROID, ObjectIdGetDatum(negId));
+       else
+               tup = NULL;
 
-       if (HeapTupleIsValid(tup) &&
-               !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprcom)))
+       if (HeapTupleIsValid(tup))
        {
-               values[Anum_pg_operator_oprcom - 1] = ObjectIdGetDatum(baseId);
-               replaces[Anum_pg_operator_oprcom - 1] = 'r';
-               tup = heap_modifytuple(tup,
-                                                          pg_operator_desc,
-                                                          values,
-                                                          nulls,
-                                                          replaces);
-
-               setheapoverride(true);
-               heap_update(pg_operator_desc, &tup->t_self, tup, NULL);
-               setheapoverride(false);
-
-               if (RelationGetForm(pg_operator_desc)->relhasindex)
+               Form_pg_operator t = (Form_pg_operator) GETSTRUCT(tup);
+               bool            update_negator = false;
+
+               /*
+                * Out of due caution, we only change the negator's oprnegate field if
+                * it has the exact value we expected: InvalidOid when creating an
+                * operator, or baseId when dropping one.
+                */
+               if (isDelete && t->oprnegate == baseId)
                {
-                       Relation        idescs[Num_pg_operator_indices];
-       
-                       CatalogOpenIndices(Num_pg_operator_indices, Name_pg_operator_indices, idescs);
-                       CatalogIndexInsert(idescs, Num_pg_operator_indices, pg_operator_desc, tup);
-                       CatalogCloseIndices(Num_pg_operator_indices, idescs);
+                       t->oprnegate = InvalidOid;
+                       update_negator = true;
+               }
+               else if (!isDelete && !OidIsValid(t->oprnegate))
+               {
+                       t->oprnegate = baseId;
+                       update_negator = true;
                }
 
-               values[Anum_pg_operator_oprcom - 1] = (Datum) NULL;
-               replaces[Anum_pg_operator_oprcom - 1] = ' ';
+               /* If any columns were found to need modification, update tuple. */
+               if (update_negator)
+               {
+                       CatalogTupleUpdate(pg_operator_desc, &tup->t_self, tup);
+
+                       /*
+                        * In the deletion case, do CCI to make the updated tuple visible.
+                        * We must do this in case the operator is its own negator. (Which
+                        * would be a logic error on the operator definer's part, but
+                        * that's not a good reason to fail here.)
+                        */
+                       if (isDelete)
+                               CommandCounterIncrement();
+               }
        }
 
-       heap_endscan(pg_operator_scan);
+       /* Close relation and release catalog lock. */
+       heap_close(pg_operator_desc, RowExclusiveLock);
+}
 
-       /* check and update the negator, if necessary */
-       opKey[0].sk_argument = ObjectIdGetDatum(negId);
+/*
+ * Create dependencies for an operator (either a freshly inserted
+ * complete operator, a new shell operator, a just-updated shell,
+ * or an operator that's being modified by ALTER OPERATOR).
+ *
+ * NB: the OidIsValid tests in this routine are necessary, in case
+ * the given operator is a shell.
+ */
+ObjectAddress
+makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
+{
+       Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
+       ObjectAddress myself,
+                               referenced;
 
-       pg_operator_scan = heap_beginscan(pg_operator_desc,
-                                                                         0,
-                                                                         SnapshotSelf,         /* no cache? */
-                                                                         1,
-                                                                         opKey);
+       myself.classId = OperatorRelationId;
+       myself.objectId = HeapTupleGetOid(tuple);
+       myself.objectSubId = 0;
 
-       tup = heap_getnext(pg_operator_scan, 0);
-       if (HeapTupleIsValid(tup) &&
-               !(OidIsValid(((Form_pg_operator) GETSTRUCT(tup))->oprnegate)))
+       /*
+        * If we are updating the operator, delete any existing entries, except
+        * for extension membership which should remain the same.
+        */
+       if (isUpdate)
        {
-               values[Anum_pg_operator_oprnegate - 1] = ObjectIdGetDatum(baseId);
-               replaces[Anum_pg_operator_oprnegate - 1] = 'r';
-               tup = heap_modifytuple(tup,
-                                                          pg_operator_desc,
-                                                          values,
-                                                          nulls,
-                                                          replaces);
-
-               setheapoverride(true);
-               heap_update(pg_operator_desc, &tup->t_self, tup, NULL);
-               setheapoverride(false);
-
-               if (RelationGetForm(pg_operator_desc)->relhasindex)
-               {
-                       Relation        idescs[Num_pg_operator_indices];
-       
-                       CatalogOpenIndices(Num_pg_operator_indices, Name_pg_operator_indices, idescs);
-                       CatalogIndexInsert(idescs, Num_pg_operator_indices, pg_operator_desc, tup);
-                       CatalogCloseIndices(Num_pg_operator_indices, idescs);
-               }
+               deleteDependencyRecordsFor(myself.classId, myself.objectId, true);
+               deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0);
+       }
+
+       /* Dependency on namespace */
+       if (OidIsValid(oper->oprnamespace))
+       {
+               referenced.classId = NamespaceRelationId;
+               referenced.objectId = oper->oprnamespace;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
        }
 
-       heap_endscan(pg_operator_scan);
+       /* Dependency on left type */
+       if (OidIsValid(oper->oprleft))
+       {
+               referenced.classId = TypeRelationId;
+               referenced.objectId = oper->oprleft;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
 
+       /* Dependency on right type */
+       if (OidIsValid(oper->oprright))
+       {
+               referenced.classId = TypeRelationId;
+               referenced.objectId = oper->oprright;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
 
-       heap_close(pg_operator_desc, RowExclusiveLock);
-}
+       /* Dependency on result type */
+       if (OidIsValid(oper->oprresult))
+       {
+               referenced.classId = TypeRelationId;
+               referenced.objectId = oper->oprresult;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
 
+       /*
+        * NOTE: we do not consider the operator to depend on the associated
+        * operators oprcom and oprnegate. We would not want to delete this
+        * operator if those go away, but only reset the link fields; which is not
+        * a function that the dependency code can presently handle.  (Something
+        * could perhaps be done with objectSubId though.)      For now, it's okay to
+        * let those links dangle if a referenced operator is removed.
+        */
 
-/* ----------------------------------------------------------------
- * OperatorCreate
- *
- * This is now just an interface procedure for OperatorDef ...
- *
- * "X" indicates an optional argument (i.e. one that can be NULL)
- *             operatorName;                   -- operator name
- *             leftTypeName;                   -- X left type name
- *             rightTypeName;                  -- X right type name
- *             procedureName;                  -- procedure for operator
- *             precedence;                             -- operator precedence
- *             isLeftAssociative;              -- operator is left associative
- *             commutatorName;                 -- X commutator operator name
- *             negatorName;                    -- X negator operator name
- *             restrictionName;                -- X restriction sel. procedure
- *             joinName;                               -- X join sel. procedure
- *             canHash;                                -- hash join can be used with this operator
- *             leftSortName;                   -- X left sort operator (for merge join)
- *             rightSortName;                  -- X right sort operator (for merge join)
- */
-void
-OperatorCreate(char *operatorName,
-                          char *leftTypeName,
-                          char *rightTypeName,
-                          char *procedureName,
-                          uint16 precedence,
-                          bool isLeftAssociative,
-                          char *commutatorName,
-                          char *negatorName,
-                          char *restrictionName,
-                          char *joinName,
-                          bool canHash,
-                          char *leftSortName,
-                          char *rightSortName)
-{
-       if (!leftTypeName && !rightTypeName)
-               elog(ERROR, "OperatorCreate: at least one of leftarg or rightarg must be defined");
+       /* Dependency on implementation function */
+       if (OidIsValid(oper->oprcode))
+       {
+               referenced.classId = ProcedureRelationId;
+               referenced.objectId = oper->oprcode;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
 
-       if (!(leftTypeName && rightTypeName))
+       /* Dependency on restriction selectivity function */
+       if (OidIsValid(oper->oprrest))
        {
-               /* If it's not a binary op, these things mustn't be set: */
-               if (commutatorName)
-                       elog(ERROR, "OperatorCreate: only binary operators can have commutators");
-               if (negatorName)
-                       elog(ERROR, "OperatorCreate: only binary operators can have negators");
-               if (restrictionName || joinName)
-                       elog(ERROR, "OperatorCreate: only binary operators can have selectivity");
-               if (canHash)
-                       elog(ERROR, "OperatorCreate: only binary operators can hash");
-               if (leftSortName || rightSortName)
-                       elog(ERROR, "OperatorCreate: only binary operators can have sort links");
+               referenced.classId = ProcedureRelationId;
+               referenced.objectId = oper->oprrest;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
        }
 
-       /* ----------------
-        *      Use OperatorDef() to define the specified operator and
-        *      also create shells for the operator's associated operators
-        *      if they don't already exist.
-        * ----------------
-        */
-       OperatorDef(operatorName,
-                               leftTypeName,
-                               rightTypeName,
-                               procedureName,
-                               precedence,
-                               isLeftAssociative,
-                               commutatorName,
-                               negatorName,
-                               restrictionName,
-                               joinName,
-                               canHash,
-                               leftSortName,
-                               rightSortName);
+       /* Dependency on join selectivity function */
+       if (OidIsValid(oper->oprjoin))
+       {
+               referenced.classId = ProcedureRelationId;
+               referenced.objectId = oper->oprjoin;
+               referenced.objectSubId = 0;
+               recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       }
+
+       /* Dependency on owner */
+       recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple),
+                                                       oper->oprowner);
+
+       /* Dependency on extension */
+       recordDependencyOnCurrentExtension(&myself, true);
+
+       return myself;
 }