From a33cf1041f99ed5f19bb7d24584db79ada07d8a4 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 23 Jan 2007 05:07:18 +0000
Subject: [PATCH] Add CREATE/ALTER/DROP OPERATOR FAMILY commands, also COMMENT
 ON OPERATOR FAMILY; and add FAMILY option to CREATE OPERATOR CLASS to allow
 adding a class to a pre-existing family.  Per previous discussion.  Man, what
 a tedious lot of cutting and pasting ...

---
 doc/src/sgml/ref/allfiles.sgml        |    5 +-
 doc/src/sgml/ref/alter_opclass.sgml   |    3 +-
 doc/src/sgml/ref/alter_opfamily.sgml  |  312 +++++++
 doc/src/sgml/ref/comment.sgml         |    6 +-
 doc/src/sgml/ref/create_opclass.sgml  |   48 +-
 doc/src/sgml/ref/create_opfamily.sgml |  125 +++
 doc/src/sgml/ref/drop_opclass.sgml    |   24 +-
 doc/src/sgml/ref/drop_opfamily.sgml   |  135 +++
 doc/src/sgml/reference.sgml           |    5 +-
 src/backend/catalog/aclchk.c          |   36 +-
 src/backend/commands/alter.c          |   10 +-
 src/backend/commands/comment.c        |   93 ++-
 src/backend/commands/opclasscmds.c    | 1088 ++++++++++++++++++++++---
 src/backend/nodes/copyfuncs.c         |   49 +-
 src/backend/nodes/equalfuncs.c        |   43 +-
 src/backend/parser/gram.y             |  192 ++++-
 src/backend/parser/keywords.c         |    3 +-
 src/backend/tcop/utility.c            |   47 +-
 src/include/commands/defrem.h         |    7 +-
 src/include/nodes/nodes.h             |    5 +-
 src/include/nodes/parsenodes.h        |   51 +-
 src/include/utils/acl.h               |    4 +-
 22 files changed, 2091 insertions(+), 200 deletions(-)
 create mode 100644 doc/src/sgml/ref/alter_opfamily.sgml
 create mode 100644 doc/src/sgml/ref/create_opfamily.sgml
 create mode 100644 doc/src/sgml/ref/drop_opfamily.sgml

diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index a481500525a..3b9b8b952ee 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.68 2006/09/18 19:54:01 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.69 2007/01/23 05:07:16 tgl Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -16,6 +16,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterLanguage      system "alter_language.sgml">
 <!entity alterOperator      system "alter_operator.sgml">
 <!entity alterOperatorClass system "alter_opclass.sgml">
+<!entity alterOperatorFamily system "alter_opfamily.sgml">
 <!entity alterRole          system "alter_role.sgml">
 <!entity alterSchema        system "alter_schema.sgml">
 <!entity alterSequence      system "alter_sequence.sgml">
@@ -45,6 +46,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createLanguage     system "create_language.sgml">
 <!entity createOperator     system "create_operator.sgml">
 <!entity createOperatorClass system "create_opclass.sgml">
+<!entity createOperatorFamily system "create_opfamily.sgml">
 <!entity createRole         system "create_role.sgml">
 <!entity createRule         system "create_rule.sgml">
 <!entity createSchema       system "create_schema.sgml">
@@ -70,6 +72,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropLanguage       system "drop_language.sgml">
 <!entity dropOperator       system "drop_operator.sgml">
 <!entity dropOperatorClass  system "drop_opclass.sgml">
+<!entity dropOperatorFamily  system "drop_opfamily.sgml">
 <!entity dropOwned          system "drop_owned.sgml">
 <!entity dropRole           system "drop_role.sgml">
 <!entity dropRule           system "drop_rule.sgml">
diff --git a/doc/src/sgml/ref/alter_opclass.sgml b/doc/src/sgml/ref/alter_opclass.sgml
index aa79f5704a9..586d54940c3 100644
--- a/doc/src/sgml/ref/alter_opclass.sgml
+++ b/doc/src/sgml/ref/alter_opclass.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_opclass.sgml,v 1.7 2006/09/16 00:30:16 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_opclass.sgml,v 1.8 2007/01/23 05:07:17 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -102,6 +102,7 @@ ALTER OPERATOR CLASS <replaceable>name</replaceable> USING <replaceable class="p
   <simplelist type="inline">
    <member><xref linkend="sql-createopclass" endterm="sql-createopclass-title"></member>
    <member><xref linkend="sql-dropopclass" endterm="sql-dropopclass-title"></member>
+   <member><xref linkend="sql-alteropfamily" endterm="sql-alteropfamily-title"></member>
   </simplelist>
  </refsect1>
 </refentry>
diff --git a/doc/src/sgml/ref/alter_opfamily.sgml b/doc/src/sgml/ref/alter_opfamily.sgml
new file mode 100644
index 00000000000..09906488e94
--- /dev/null
+++ b/doc/src/sgml/ref/alter_opfamily.sgml
@@ -0,0 +1,312 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_opfamily.sgml,v 1.1 2007/01/23 05:07:17 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTEROPFAMILY">
+ <refmeta>
+  <refentrytitle id="SQL-ALTEROPFAMILY-TITLE">ALTER OPERATOR FAMILY</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER OPERATOR FAMILY</refname>
+  <refpurpose>change the definition of an operator family</refpurpose>
+ </refnamediv>  
+  
+ <indexterm zone="sql-alteropfamily">
+  <primary>ALTER OPERATOR FAMILY</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="parameter">index_method</replaceable> ADD
+  {  OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_name</replaceable> ( <replaceable class="parameter">op_type</replaceable>, <replaceable class="parameter">op_type</replaceable> ) [ RECHECK ]
+   | FUNCTION <replaceable class="parameter">support_number</replaceable> [ ( <replaceable class="parameter">op_type</replaceable> [ , <replaceable class="parameter">op_type</replaceable> ] ) ] <replaceable class="parameter">funcname</replaceable> ( <replaceable class="parameter">argument_type</replaceable> [, ...] )
+  } [, ... ]
+ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="parameter">index_method</replaceable> DROP
+  {  OPERATOR <replaceable class="parameter">strategy_number</replaceable> ( <replaceable class="parameter">op_type</replaceable> [ , <replaceable class="parameter">op_type</replaceable> ] )
+   | FUNCTION <replaceable class="parameter">support_number</replaceable> ( <replaceable class="parameter">op_type</replaceable> [ , <replaceable class="parameter">op_type</replaceable> ] )
+  } [, ... ]
+ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="parameter">index_method</replaceable> RENAME TO <replaceable>newname</replaceable>
+ALTER OPERATOR FAMILY <replaceable>name</replaceable> USING <replaceable class="parameter">index_method</replaceable> OWNER TO <replaceable>newowner</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+  
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER OPERATOR FAMILY</command> changes the definition of
+   an operator family.  You can add operators and support functions
+   to the family, remove them from the family,
+   or change the family's name or owner.
+  </para>
+
+  <para>
+   When operators and support functions are added to a family with
+   <command>ALTER OPERATOR FAMILY</command>, they are not part of any
+   specific operator class within the family, but are just <quote>loose</>
+   within the family.  This indicates that these operators and functions
+   are compatible with the family's semantics, but are not required for
+   correct functioning of any specific index.  (Operators and functions
+   that are so required should be declared as part of an operator class,
+   instead; see <xref linkend="sql-createopclass"
+   endterm="sql-createopclass-title">.)
+   <productname>PostgreSQL</productname> will allow loose members of a
+   family to be dropped from the family at any time, but members of an
+   operator class cannot be dropped without dropping the whole class and
+   any indexes that depend on it.
+   Typically, single-data-type operators
+   and functions are part of operator classes because they are needed to
+   support an index on that specific data type, while cross-data-type
+   operators and functions are made loose members of the family.
+  </para>
+
+  <para>
+   You must be a superuser to use <command>ALTER OPERATOR FAMILY</>.
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of an existing operator
+      family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">index_method</replaceable></term>
+    <listitem>
+     <para>
+      The name of the index method this operator family is for.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">strategy_number</replaceable></term>
+    <listitem>
+     <para>
+      The index method's strategy number for an operator
+      associated with the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">operator_name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of an operator associated
+      with the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">op_type</replaceable></term>
+    <listitem>
+     <para>
+      In an <literal>OPERATOR</> clause,
+      the operand data type(s) of the operator, or <literal>NONE</> to
+      signify a left-unary or right-unary operator.  Unlike the comparable
+      syntax in <command>CREATE OPERATOR CLASS</>, the operand data types
+      must always be specified.
+     </para>
+
+     <para>
+      In an <literal>ADD FUNCTION</> clause, the operand data type(s) the
+      function is intended to support, if different from
+      the input data type(s) of the function.  For B-tree and hash indexes
+      it is not necessary to specify <replaceable
+      class="parameter">op_type</replaceable> since the function's input
+      data type(s) are always the correct ones to use.  For GIN and GiST
+      indexes it is necessary to specify the input data type the function
+      is to be used with.
+     </para>
+
+     <para>
+      In a <literal>DROP FUNCTION</> clause, the operand data type(s) the
+      function is intended to support must be specified.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RECHECK</></term>
+    <listitem>
+     <para>
+      If present, the index is <quote>lossy</> for this operator, and
+      so the rows retrieved using the index must be rechecked to
+      verify that they actually satisfy the qualification clause
+      involving this operator.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">support_number</replaceable></term>
+    <listitem>
+     <para>
+      The index method's support procedure number for a
+      function associated with the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">funcname</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of a function that is an
+      index method support procedure for the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argument_types</replaceable></term>
+    <listitem>
+     <para>
+      The parameter data type(s) of the function.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">newname</replaceable></term>
+    <listitem>
+     <para>
+      The new name of the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">newowner</replaceable></term>
+    <listitem>
+     <para>
+      The new owner of the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+ </variablelist>
+
+  <para>
+   The <literal>OPERATOR</> and <literal>FUNCTION</>
+   clauses may appear in any order.
+  </para>
+
+ </refsect1>
+  
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   Notice that the <literal>DROP</> syntax only specifies the <quote>slot</>
+   in the operator family, by strategy or support number and input data
+   type(s).  The name of the operator or function occupying the slot is not
+   mentioned.  Also, for <literal>DROP FUNCTION</> the type(s) to specify
+   are the input data type(s) the function is intended to support; for
+   GIN and GiST indexes this may have nothing to do with the actual input
+   argument types of the function.
+  </para>
+
+  <para>
+   Because the index machinery does not check access permissions on functions
+   before using them, including a function or operator in an operator family
+   is tantamount to granting public execute permission on it.  This is usually
+   not an issue for the sorts of functions that are useful in an operator
+   family.
+  </para>
+
+  <para>
+   The operators should not be defined by SQL functions.  A SQL function
+   is likely to be inlined into the calling query, which will prevent
+   the optimizer from recognizing that the query matches an index.
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   The following example command adds cross-data-type operators and
+   support functions to an operator family that already contains B-tree
+   operator classes for data types <type>int4</> and <type>int2</>.
+  </para>
+
+<programlisting>
+ALTER OPERATOR FAMILY integer_ops USING btree ADD
+
+  -- int4 vs int2
+  OPERATOR 1 &lt; (int4, int2) ,
+  OPERATOR 2 &lt;= (int4, int2) ,
+  OPERATOR 3 = (int4, int2) ,
+  OPERATOR 4 &gt;= (int4, int2) ,
+  OPERATOR 5 &gt; (int4, int2) ,
+  FUNCTION 1 btint42cmp(int4, int2) ,
+
+  -- int2 vs int4
+  OPERATOR 1 &lt; (int2, int4) ,
+  OPERATOR 2 &lt;= (int2, int4) ,
+  OPERATOR 3 = (int2, int4) ,
+  OPERATOR 4 &gt;= (int2, int4) ,
+  OPERATOR 5 &gt; (int2, int4) ,
+  FUNCTION 1 btint24cmp(int2, int4) ;
+</programlisting>  
+
+  <para>
+   To remove these entries again:
+  </para>
+
+<programlisting>
+ALTER OPERATOR FAMILY integer_ops USING btree DROP
+
+  -- int4 vs int2
+  OPERATOR 1 (int4, int2) ,
+  OPERATOR 2 (int4, int2) ,
+  OPERATOR 3 (int4, int2) ,
+  OPERATOR 4 (int4, int2) ,
+  OPERATOR 5 (int4, int2) ,
+  FUNCTION 1 (int4, int2) ,
+
+  -- int2 vs int4
+  OPERATOR 1 (int2, int4) ,
+  OPERATOR 2 (int2, int4) ,
+  OPERATOR 3 (int2, int4) ,
+  OPERATOR 4 (int2, int4) ,
+  OPERATOR 5 (int2, int4) ,
+  FUNCTION 1 (int2, int4) ;
+</programlisting>  
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   There is no <command>ALTER OPERATOR FAMILY</command> statement in
+   the SQL standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createopfamily" endterm="sql-createopfamily-title"></member>
+   <member><xref linkend="sql-dropopfamily" endterm="sql-dropopfamily-title"></member>
+   <member><xref linkend="sql-createopclass" endterm="sql-createopclass-title"></member>
+   <member><xref linkend="sql-alteropclass" endterm="sql-alteropclass-title"></member>
+   <member><xref linkend="sql-dropopclass" endterm="sql-dropopclass-title"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index d2cf39a20c4..bc3ab8f26d8 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.33 2006/10/23 18:10:32 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.34 2007/01/23 05:07:17 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -35,6 +35,7 @@ COMMENT ON
   LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> |
   OPERATOR <replaceable class="PARAMETER">op</replaceable> (<replaceable class="PARAMETER">leftoperand_type</replaceable>, <replaceable class="PARAMETER">rightoperand_type</replaceable>) |
   OPERATOR CLASS <replaceable class="PARAMETER">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> |
+  OPERATOR FAMILY <replaceable class="PARAMETER">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> |
   [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">object_name</replaceable> |
   ROLE <replaceable class="PARAMETER">object_name</replaceable> |
   RULE <replaceable class="PARAMETER">rule_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
@@ -92,7 +93,7 @@ COMMENT ON
      <para>
       The name of the object to be commented.  Names of tables,
       aggregates, domains, functions, indexes, operators, operator classes,
-      sequences, types, and views may be schema-qualified.
+      operator families, sequences, types, and views may be schema-qualified.
      </para>
     </listitem>
    </varlistentry>
@@ -247,6 +248,7 @@ COMMENT ON LARGE OBJECT 346344 IS 'Planning document';
 COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two texts';
 COMMENT ON OPERATOR - (NONE, text) IS 'This is a prefix operator on text';
 COMMENT ON OPERATOR CLASS int4ops USING btree IS '4 byte integer operators for btrees';
+COMMENT ON OPERATOR FAMILY integer_ops USING btree IS 'all integer operators for btrees';
 COMMENT ON ROLE my_role IS 'Administration group for finance tables';
 COMMENT ON RULE my_rule ON my_table IS 'Logs updates of employee records';
 COMMENT ON SCHEMA my_schema IS 'Departmental data';
diff --git a/doc/src/sgml/ref/create_opclass.sgml b/doc/src/sgml/ref/create_opclass.sgml
index 524be85f978..85a31e3b195 100644
--- a/doc/src/sgml/ref/create_opclass.sgml
+++ b/doc/src/sgml/ref/create_opclass.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_opclass.sgml,v 1.18 2006/10/16 17:28:03 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_opclass.sgml,v 1.19 2007/01/23 05:07:17 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -20,9 +20,10 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAULT ] FOR TYPE <replaceable class="parameter">data_type</replaceable> USING <replaceable class="parameter">index_method</replaceable> AS
+CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAULT ] FOR TYPE <replaceable class="parameter">data_type</replaceable>
+  USING <replaceable class="parameter">index_method</replaceable> [ FAMILY <replaceable class="parameter">family_name</replaceable> ] AS
   {  OPERATOR <replaceable class="parameter">strategy_number</replaceable> <replaceable class="parameter">operator_name</replaceable> [ ( <replaceable class="parameter">op_type</replaceable>, <replaceable class="parameter">op_type</replaceable> ) ] [ RECHECK ]
-   | FUNCTION <replaceable class="parameter">support_number</replaceable> <replaceable class="parameter">funcname</replaceable> ( <replaceable class="parameter">argument_type</replaceable> [, ...] )
+   | FUNCTION <replaceable class="parameter">support_number</replaceable> [ ( <replaceable class="parameter">op_type</replaceable> [ , <replaceable class="parameter">op_type</replaceable> ] ) ] <replaceable class="parameter">funcname</replaceable> ( <replaceable class="parameter">argument_type</replaceable> [, ...] )
    | STORAGE <replaceable class="parameter">storage_type</replaceable>
   } [, ... ]
 </synopsis>
@@ -40,7 +41,7 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
    be used by 
    the index method when the operator class is selected for an
    index column.  All the operators and functions used by an operator
-   class must be defined before the operator class is created.
+   class must be defined before the operator class can be created.
   </para>
 
   <para>
@@ -65,6 +66,15 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
    responsibility to define a valid operator class.
   </para>
 
+  <para>
+   Related operator classes can be grouped into <firstterm>operator
+   families</>.  To add a new operator class to an existing family,
+   specify the <literal>FAMILY</> option in <command>CREATE OPERATOR
+   CLASS</command>.  Without this option, the new class is placed into
+   a family named the same as the new class (creating that family if
+   it doesn't already exist).
+  </para>
+
   <para>
    Refer to <xref linkend="xindex"> for further information.
   </para>
@@ -113,6 +123,17 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><replaceable class="parameter">family_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the existing operator family to add this operator class to.
+      If not specified, a family named the same as the operator class is
+      used (creating it, if it doesn't already exist).
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">strategy_number</replaceable></term>
     <listitem>
@@ -137,11 +158,24 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
     <term><replaceable class="parameter">op_type</replaceable></term>
     <listitem>
      <para>
-      The operand data type(s) of an operator, or <literal>NONE</> to
+      In an <literal>OPERATOR</> clause,
+      the operand data type(s) of the operator, or <literal>NONE</> to
       signify a left-unary or right-unary operator.  The operand data
       types may be omitted in the normal case where they are the same
       as the operator class's data type.
      </para>
+
+     <para>
+      In a <literal>FUNCTION</> clause, the operand data type(s) the
+      function is intended to support, if different from
+      the input data type(s) of the function (for B-tree and hash indexes)
+      or the class's data type (for GIN and GiST indexes).  These defaults
+      are always correct, so there is no point in specifying <replaceable
+      class="parameter">op_type</replaceable> in a <literal>FUNCTION</> clause
+      in <command>CREATE OPERATOR CLASS</>, but the option is provided
+      for consistency with the comparable syntax in
+      <command>ALTER OPERATOR FAMILY</>.
+     </para>
     </listitem>
    </varlistentry>
 
@@ -192,7 +226,7 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
      <para>
       The data type actually stored in the index.  Normally this is
       the same as the column data type, but some index methods
-      (GIN and GiST for now) allow it to be different.  The
+      (currently GIN and GiST) allow it to be different.  The
       <literal>STORAGE</> clause must be omitted unless the index
       method allows a different type to be used.
      </para>
@@ -268,6 +302,8 @@ CREATE OPERATOR CLASS gist__int_ops
   <simplelist type="inline">
    <member><xref linkend="sql-alteropclass" endterm="sql-alteropclass-title"></member>
    <member><xref linkend="sql-dropopclass" endterm="sql-dropopclass-title"></member>
+   <member><xref linkend="sql-createopfamily" endterm="sql-createopfamily-title"></member>
+   <member><xref linkend="sql-alteropfamily" endterm="sql-alteropfamily-title"></member>
   </simplelist>
  </refsect1>
 </refentry>
diff --git a/doc/src/sgml/ref/create_opfamily.sgml b/doc/src/sgml/ref/create_opfamily.sgml
new file mode 100644
index 00000000000..d8ddb683b9e
--- /dev/null
+++ b/doc/src/sgml/ref/create_opfamily.sgml
@@ -0,0 +1,125 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_opfamily.sgml,v 1.1 2007/01/23 05:07:17 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATEOPFAMILY">
+ <refmeta>
+  <refentrytitle id="sql-createopfamily-title">CREATE OPERATOR FAMILY</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE OPERATOR FAMILY</refname>
+  <refpurpose>define a new operator family</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-createopfamily">
+  <primary>CREATE OPERATOR FAMILY</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE OPERATOR FAMILY <replaceable class="parameter">name</replaceable> USING <replaceable class="parameter">index_method</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE OPERATOR FAMILY</command> creates a new operator family.
+   An operator family defines a collection of related operator classes,
+   and perhaps some additional operators and support functions that are
+   compatible with these operator classes but not essential for the
+   functioning of any individual index.  (Operators and functions that
+   are essential to indexes should be grouped within the relevant operator
+   class, rather than being <quote>loose</> in the operator family.
+   Typically, single-data-type operators are bound to operator classes,
+   while cross-data-type operators can be loose in an operator family
+   containing operator classes for both data types.)
+  </para>
+
+  <para>
+   The new operator family is initially empty.  It should be populated
+   by issuing subsequent <command>CREATE OPERATOR CLASS</command> commands
+   to add contained operator classes, and optionally
+   <command>ALTER OPERATOR FAMILY</command> commands to add <quote>loose</>
+   operators and their corresponding support functions.
+  </para>
+
+  <para>
+   If a schema name is given then the operator family is created in the
+   specified schema.  Otherwise it is created in the current schema.
+   Two operator families in the same schema can have the same name only if they
+   are for different index methods.
+  </para>
+
+  <para>
+   The user who defines an operator family becomes its owner.  Presently,
+   the creating user must be a superuser.  (This restriction is made because
+   an erroneous operator family definition could confuse or even crash the
+   server.)
+  </para>
+
+  <para>
+   <command>CREATE OPERATOR FAMILY</command> does not presently check
+   whether the operator family definition includes all the operators and
+   functions required by the index method, nor whether the operators and
+   functions form a self-consistent set.  It is the user's
+   responsibility to define a valid operator family.
+  </para>
+
+  <para>
+   Refer to <xref linkend="xindex"> for further information.
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the operator family to be created.  The name may be
+      schema-qualified.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">index_method</replaceable></term>
+    <listitem>
+     <para>
+      The name of the index method this operator family is for.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+ 
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CREATE OPERATOR FAMILY</command> is a
+   <productname>PostgreSQL</productname> extension.  There is no
+   <command>CREATE OPERATOR FAMILY</command> statement in the SQL
+   standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alteropfamily" endterm="sql-alteropfamily-title"></member>
+   <member><xref linkend="sql-dropopfamily" endterm="sql-dropopfamily-title"></member>
+   <member><xref linkend="sql-createopclass" endterm="sql-createopclass-title"></member>
+   <member><xref linkend="sql-alteropclass" endterm="sql-alteropclass-title"></member>
+   <member><xref linkend="sql-dropopclass" endterm="sql-dropopclass-title"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/drop_opclass.sgml b/doc/src/sgml/ref/drop_opclass.sgml
index 42cc1cd0f8d..ce00ff40fd5 100644
--- a/doc/src/sgml/ref/drop_opclass.sgml
+++ b/doc/src/sgml/ref/drop_opclass.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/drop_opclass.sgml,v 1.10 2006/09/16 00:30:18 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_opclass.sgml,v 1.11 2007/01/23 05:07:17 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -31,6 +31,13 @@ DROP OPERATOR CLASS [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
    <command>DROP OPERATOR CLASS</command> drops an existing operator class.
    To execute this command you must be the owner of the operator class.
   </para>
+
+  <para>
+   <command>DROP OPERATOR CLASS</command> does not drop any of the operators
+   or functions referenced by the class.  If there are any indexes depending
+   on the operator class, you will need to specify
+   <literal>CASCADE</> for the drop to complete.
+  </para>
  </refsect1>
   
  <refsect1>
@@ -86,6 +93,20 @@ DROP OPERATOR CLASS [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab
    </varlistentry>
   </variablelist>
  </refsect1>
+  
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   <command>DROP OPERATOR CLASS</> will not drop the operator family
+   containing the class, even if there is nothing else left in the
+   family (in particular, in the case where the family was implicitly
+   created by <command>CREATE OPERATOR CLASS</>).  An empty operator
+   family is harmless, but for the sake of tidiness you may wish to
+   remove the family with <command>DROP OPERATOR FAMILY</>; or perhaps
+   better, use <command>DROP OPERATOR FAMILY</> in the first place.
+  </para>
+ </refsect1>
 
  <refsect1>
   <title>Examples</title>
@@ -118,6 +139,7 @@ DROP OPERATOR CLASS widget_ops USING btree;
   <simplelist type="inline">
    <member><xref linkend="sql-alteropclass" endterm="sql-alteropclass-title"></member>
    <member><xref linkend="sql-createopclass" endterm="sql-createopclass-title"></member>
+   <member><xref linkend="sql-dropopfamily" endterm="sql-dropopfamily-title"></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/drop_opfamily.sgml b/doc/src/sgml/ref/drop_opfamily.sgml
new file mode 100644
index 00000000000..e2dcaf72af1
--- /dev/null
+++ b/doc/src/sgml/ref/drop_opfamily.sgml
@@ -0,0 +1,135 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/drop_opfamily.sgml,v 1.1 2007/01/23 05:07:17 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPOPFAMILY">
+ <refmeta>
+  <refentrytitle id="SQL-DROPOPFAMILY-TITLE">DROP OPERATOR FAMILY</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP OPERATOR FAMILY</refname>
+  <refpurpose>remove an operator family</refpurpose>
+ </refnamediv>
+  
+ <indexterm zone="sql-dropopfamily">
+  <primary>DROP OPERATOR FAMILY</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP OPERATOR FAMILY [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> USING <replaceable class="PARAMETER">index_method</replaceable> [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP OPERATOR FAMILY</command> drops an existing operator family.
+   To execute this command you must be the owner of the operator family.
+  </para>
+
+  <para>
+   <command>DROP OPERATOR FAMILY</command> includes dropping any operator
+   classes contained in the family, but it does not drop any of the operators
+   or functions referenced by the family.  If there are any indexes depending
+   on operator classes within the family, you will need to specify
+   <literal>CASCADE</> for the drop to complete.
+  </para>
+ </refsect1>
+  
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the operator family does not exist.
+      A notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of an existing operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">index_method</replaceable></term>
+    <listitem>
+     <para>
+      The name of the index access method the operator family is for.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the operator family.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the operator family if any objects depend on it.
+      This is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   Remove the B-tree operator family <literal>float_ops</literal>:
+
+<programlisting>
+DROP OPERATOR FAMILY float_ops USING btree;
+</programlisting>
+
+   This command will not succeed if there are any existing indexes
+   that use operator classes within the family.  Add <literal>CASCADE</> to
+   drop such indexes along with the operator family.
+  </para>
+ </refsect1>
+ 
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   There is no <command>DROP OPERATOR FAMILY</command> statement in the
+   SQL standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alteropfamily" endterm="sql-alteropfamily-title"></member>
+   <member><xref linkend="sql-createopfamily" endterm="sql-createopfamily-title"></member>
+   <member><xref linkend="sql-alteropclass" endterm="sql-alteropclass-title"></member>
+   <member><xref linkend="sql-createopclass" endterm="sql-createopclass-title"></member>
+   <member><xref linkend="sql-dropopclass" endterm="sql-dropopclass-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 64330928c4e..9b3cbda575e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.60 2006/09/18 19:54:01 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.61 2007/01/23 05:07:17 tgl Exp $ -->
 
 <part id="reference">
  <title>Reference</title>
@@ -44,6 +44,7 @@
    &alterLanguage;
    &alterOperator;
    &alterOperatorClass;
+   &alterOperatorFamily;
    &alterRole;
    &alterSchema;
    &alterSequence;
@@ -73,6 +74,7 @@
    &createLanguage;
    &createOperator;
    &createOperatorClass;
+   &createOperatorFamily;
    &createRole;
    &createRule;
    &createSchema;
@@ -98,6 +100,7 @@
    &dropLanguage;
    &dropOperator;
    &dropOperatorClass;
+   &dropOperatorFamily;
    &dropOwned;
    &dropRole;
    &dropRule;
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index fcef0593b0a..328f8ccfda7 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.134 2007/01/05 22:19:24 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.135 2007/01/23 05:07:17 tgl Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -30,6 +30,7 @@
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_opfamily.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
@@ -1413,6 +1414,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
 	gettext_noop("permission denied for schema %s"),
 	/* ACL_KIND_OPCLASS */
 	gettext_noop("permission denied for operator class %s"),
+	/* ACL_KIND_OPFAMILY */
+	gettext_noop("permission denied for operator family %s"),
 	/* ACL_KIND_CONVERSION */
 	gettext_noop("permission denied for conversion %s"),
 	/* ACL_KIND_TABLESPACE */
@@ -1439,6 +1442,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
 	gettext_noop("must be owner of schema %s"),
 	/* ACL_KIND_OPCLASS */
 	gettext_noop("must be owner of operator class %s"),
+	/* ACL_KIND_OPFAMILY */
+	gettext_noop("must be owner of operator family %s"),
 	/* ACL_KIND_CONVERSION */
 	gettext_noop("must be owner of conversion %s"),
 	/* ACL_KIND_TABLESPACE */
@@ -2239,6 +2244,35 @@ pg_opclass_ownercheck(Oid opc_oid, Oid roleid)
 	return has_privs_of_role(roleid, ownerId);
 }
 
+/*
+ * Ownership check for an operator family (specified by OID).
+ */
+bool
+pg_opfamily_ownercheck(Oid opf_oid, Oid roleid)
+{
+	HeapTuple	tuple;
+	Oid			ownerId;
+
+	/* Superusers bypass all permission checking. */
+	if (superuser_arg(roleid))
+		return true;
+
+	tuple = SearchSysCache(OPFAMILYOID,
+						   ObjectIdGetDatum(opf_oid),
+						   0, 0, 0);
+	if (!HeapTupleIsValid(tuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("operator family with OID %u does not exist",
+						opf_oid)));
+
+	ownerId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfowner;
+
+	ReleaseSysCache(tuple);
+
+	return has_privs_of_role(roleid, ownerId);
+}
+
 /*
  * Ownership check for a database (specified by OID).
  */
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index bf806f76fc2..30b7ebde002 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.21 2007/01/05 22:19:25 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.22 2007/01/23 05:07:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -66,6 +66,10 @@ ExecRenameStmt(RenameStmt *stmt)
 			RenameOpClass(stmt->object, stmt->subname, stmt->newname);
 			break;
 
+		case OBJECT_OPFAMILY:
+			RenameOpFamily(stmt->object, stmt->subname, stmt->newname);
+			break;
+
 		case OBJECT_ROLE:
 			RenameRole(stmt->subname, stmt->newname);
 			break;
@@ -211,6 +215,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
 			AlterOpClassOwner(stmt->object, stmt->addname, newowner);
 			break;
 
+		case OBJECT_OPFAMILY:
+			AlterOpFamilyOwner(stmt->object, stmt->addname, newowner);
+			break;
+
 		case OBJECT_SCHEMA:
 			AlterSchemaOwner((char *) linitial(stmt->object), newowner);
 			break;
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 6d6eff7f48d..2f6a38d42d2 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
  * Copyright (c) 1996-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.94 2007/01/05 22:19:25 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.95 2007/01/23 05:07:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_opfamily.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_rewrite.h"
 #include "catalog/pg_shdescription.h"
@@ -72,6 +73,7 @@ static void CommentConstraint(List *qualname, char *comment);
 static void CommentConversion(List *qualname, char *comment);
 static void CommentLanguage(List *qualname, char *comment);
 static void CommentOpClass(List *qualname, List *arguments, char *comment);
+static void CommentOpFamily(List *qualname, List *arguments, char *comment);
 static void CommentLargeObject(List *qualname, char *comment);
 static void CommentCast(List *qualname, List *arguments, char *comment);
 static void CommentTablespace(List *qualname, char *comment);
@@ -134,6 +136,9 @@ CommentObject(CommentStmt *stmt)
 		case OBJECT_OPCLASS:
 			CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
 			break;
+		case OBJECT_OPFAMILY:
+			CommentOpFamily(stmt->objname, stmt->objargs, stmt->comment);
+			break;
 		case OBJECT_LARGEOBJECT:
 			CommentLargeObject(stmt->objname, stmt->comment);
 			break;
@@ -1263,6 +1268,92 @@ CommentOpClass(List *qualname, List *arguments, char *comment)
 	CreateComments(opcID, OperatorClassRelationId, 0, comment);
 }
 
+/*
+ * CommentOpFamily --
+ *
+ * This routine is used to allow a user to provide comments on an
+ * operator family. The operator family for commenting is determined by both
+ * its name and its argument list which defines the index method
+ * the operator family is used for. The argument list is expected to contain
+ * a single name (represented as a string Value node).
+ */
+static void
+CommentOpFamily(List *qualname, List *arguments, char *comment)
+{
+	char	   *amname;
+	char	   *schemaname;
+	char	   *opfname;
+	Oid			amID;
+	Oid			opfID;
+	HeapTuple	tuple;
+
+	Assert(list_length(arguments) == 1);
+	amname = strVal(linitial(arguments));
+
+	/*
+	 * Get the access method's OID.
+	 */
+	amID = GetSysCacheOid(AMNAME,
+						  CStringGetDatum(amname),
+						  0, 0, 0);
+	if (!OidIsValid(amID))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+						amname)));
+
+	/*
+	 * Look up the opfamily.
+	 */
+
+	/* deconstruct the name list */
+	DeconstructQualifiedName(qualname, &schemaname, &opfname);
+
+	if (schemaname)
+	{
+		/* Look in specific schema only */
+		Oid			namespaceId;
+
+		namespaceId = LookupExplicitNamespace(schemaname);
+		tuple = SearchSysCache(OPFAMILYAMNAMENSP,
+							   ObjectIdGetDatum(amID),
+							   PointerGetDatum(opfname),
+							   ObjectIdGetDatum(namespaceId),
+							   0);
+	}
+	else
+	{
+		/* Unqualified opfamily name, so search the search path */
+		opfID = OpfamilynameGetOpfid(amID, opfname);
+		if (!OidIsValid(opfID))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+							opfname, amname)));
+		tuple = SearchSysCache(OPFAMILYOID,
+							   ObjectIdGetDatum(opfID),
+							   0, 0, 0);
+	}
+
+	if (!HeapTupleIsValid(tuple))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+						NameListToString(qualname), amname)));
+
+	opfID = HeapTupleGetOid(tuple);
+
+	/* Permission check: must own opfamily */
+	if (!pg_opfamily_ownercheck(opfID, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+					   NameListToString(qualname));
+
+	ReleaseSysCache(tuple);
+
+	/* Call CreateComments() to create/drop the comments */
+	CreateComments(opfID, OperatorFamilyRelationId, 0, comment);
+}
+
 /*
  * CommentLargeObject --
  *
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 24e4f832c42..802b6909525 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.52 2007/01/05 22:19:26 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.53 2007/01/23 05:07:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -55,15 +55,30 @@ typedef struct
 } OpFamilyMember;
 
 
+static void AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+				 int maxOpNumber, int maxProcNumber,
+				 List *items);
+static void AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+				 int maxOpNumber, int maxProcNumber,
+				 List *items);
+static void processTypesSpec(List *args, Oid *lefttype, Oid *righttype);
 static void assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
 static void assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid);
 static void addFamilyMember(List **list, OpFamilyMember *member, bool isProc);
-static void storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid,
-						   List *operators);
-static void storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid,
-							List *procedures);
+static void storeOperators(List *opfamilyname, Oid amoid,
+						   Oid opfamilyoid, Oid opclassoid,
+						   List *operators, bool isAdd);
+static void storeProcedures(List *opfamilyname, Oid amoid,
+							Oid opfamilyoid, Oid opclassoid,
+							List *procedures, bool isAdd);
+static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+						  List *operators);
+static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+						   List *procedures);
 static void AlterOpClassOwner_internal(Relation rel, HeapTuple tuple,
 						   Oid newOwnerId);
+static void AlterOpFamilyOwner_internal(Relation rel, HeapTuple tuple,
+						   Oid newOwnerId);
 
 
 /*
@@ -452,6 +467,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
 				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
 				member->object = funcOid;
 				member->number = item->number;
+
+				/* allow overriding of the function's actual arg types */
+				if (item->class_args)
+					processTypesSpec(item->class_args,
+									 &member->lefttype, &member->righttype);
+
 				assignProcTypes(member, amoid, typeoid);
 				addFamilyMember(&procedures, member, true);
 				break;
@@ -570,8 +591,10 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	 * Now add tuples to pg_amop and pg_amproc tying in the operators and
 	 * functions.  Dependencies on them are inserted, too.
 	 */
-	storeOperators(amoid, opfamilyoid, opclassoid, operators);
-	storeProcedures(amoid, opfamilyoid, opclassoid, procedures);
+	storeOperators(stmt->opfamilyname, amoid, opfamilyoid,
+				   opclassoid, operators, false);
+	storeProcedures(stmt->opfamilyname, amoid, opfamilyoid,
+					opclassoid, procedures, false);
 
 	/*
 	 * Create dependencies for the opclass proper.  Note: we do not create a
@@ -615,151 +638,565 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	heap_close(rel, RowExclusiveLock);
 }
 
+
 /*
- * Determine the lefttype/righttype to assign to an operator,
- * and do any validity checking we can manage.
+ * DefineOpFamily
+ *		Define a new index operator family.
  */
-static void
-assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+void
+DefineOpFamily(CreateOpFamilyStmt *stmt)
 {
-	Operator	optup;
-	Form_pg_operator opform;
+	char	   *opfname;		/* name of opfamily we're creating */
+	Oid			amoid,			/* our AM's oid */
+				namespaceoid,	/* namespace to create opfamily in */
+				opfamilyoid;	/* oid of opfamily we create */
+	Relation	rel;
+	HeapTuple	tup;
+	Datum		values[Natts_pg_opfamily];
+	char		nulls[Natts_pg_opfamily];
+	AclResult	aclresult;
+	NameData	opfName;
+	ObjectAddress myself,
+				referenced;
 
-	/* Fetch the operator definition */
-	optup = SearchSysCache(OPEROID,
-						   ObjectIdGetDatum(member->object),
-						   0, 0, 0);
-	if (optup == NULL)
-		elog(ERROR, "cache lookup failed for operator %u", member->object);
-	opform = (Form_pg_operator) GETSTRUCT(optup);
+	/* Convert list of names to a name and namespace */
+	namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
+													 &opfname);
+
+	/* Check we have creation rights in target namespace */
+	aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+					   get_namespace_name(namespaceoid));
+
+	/* Get necessary info about access method */
+	tup = SearchSysCache(AMNAME,
+						 CStringGetDatum(stmt->amname),
+						 0, 0, 0);
+	if (!HeapTupleIsValid(tup))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+						stmt->amname)));
+
+	amoid = HeapTupleGetOid(tup);
+
+	/* XXX Should we make any privilege check against the AM? */
+
+	ReleaseSysCache(tup);
 
 	/*
-	 * Opfamily operators must be binary ops returning boolean.
+	 * Currently, we require superuser privileges to create an opfamily.
+	 * See comments in DefineOpClass.
+	 *
+	 * XXX re-enable NOT_USED code sections below if you remove this test.
 	 */
-	if (opform->oprkind != 'b')
+	if (!superuser())
 		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("index operators must be binary")));
-	if (opform->oprresult != BOOLOID)
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to create an operator family")));
+
+	rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+	/*
+	 * Make sure there is no existing opfamily of this name (this is just to
+	 * give a more friendly error message than "duplicate key").
+	 */
+	if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
+							 ObjectIdGetDatum(amoid),
+							 CStringGetDatum(opfname),
+							 ObjectIdGetDatum(namespaceoid),
+							 0))
 		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-				 errmsg("index operators must return boolean")));
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("operator family \"%s\" for access method \"%s\" already exists",
+						opfname, stmt->amname)));
 
 	/*
-	 * If lefttype/righttype isn't specified, use the operator's input types
+	 * Okay, let's create the pg_opfamily entry.
 	 */
-	if (!OidIsValid(member->lefttype))
-		member->lefttype = opform->oprleft;
-	if (!OidIsValid(member->righttype))
-		member->righttype = opform->oprright;
+	memset(values, 0, sizeof(values));
+	memset(nulls, ' ', sizeof(nulls));
 
-	ReleaseSysCache(optup);
+	values[Anum_pg_opfamily_opfmethod - 1] = ObjectIdGetDatum(amoid);
+	namestrcpy(&opfName, opfname);
+	values[Anum_pg_opfamily_opfname - 1] = NameGetDatum(&opfName);
+	values[Anum_pg_opfamily_opfnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
+
+	tup = heap_formtuple(rel->rd_att, values, nulls);
+
+	opfamilyoid = simple_heap_insert(rel, tup);
+
+	CatalogUpdateIndexes(rel, tup);
+
+	heap_freetuple(tup);
+
+	/*
+	 * Create dependencies for the opfamily proper.  Note: we do not create a
+	 * dependency link to the AM, because we don't currently support DROP
+	 * ACCESS METHOD.
+	 */
+	myself.classId = OperatorFamilyRelationId;
+	myself.objectId = opfamilyoid;
+	myself.objectSubId = 0;
+
+	/* dependency on namespace */
+	referenced.classId = NamespaceRelationId;
+	referenced.objectId = namespaceoid;
+	referenced.objectSubId = 0;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	/* dependency on owner */
+	recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId());
+
+	heap_close(rel, RowExclusiveLock);
 }
 
+
 /*
- * Determine the lefttype/righttype to assign to a support procedure,
- * and do any validity checking we can manage.
+ * AlterOpFamily
+ *		Add or remove operators/procedures within an existing operator family.
+ *
+ * Note: this implements only ALTER OPERATOR FAMILY ... ADD/DROP.  Some
+ * other commands called ALTER OPERATOR FAMILY exist, but go through
+ * different code paths.
  */
-static void
-assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+void
+AlterOpFamily(AlterOpFamilyStmt *stmt)
 {
-	HeapTuple	proctup;
-	Form_pg_proc procform;
+	Oid			amoid,			/* our AM's oid */
+				opfamilyoid;	/* oid of opfamily */
+	int			maxOpNumber,	/* amstrategies value */
+				maxProcNumber;	/* amsupport value */
+	HeapTuple	tup;
+	Form_pg_am	pg_am;
 
-	/* Fetch the procedure definition */
-	proctup = SearchSysCache(PROCOID,
-							 ObjectIdGetDatum(member->object),
-							 0, 0, 0);
-	if (proctup == NULL)
-		elog(ERROR, "cache lookup failed for function %u", member->object);
-	procform = (Form_pg_proc) GETSTRUCT(proctup);
+	/* Get necessary info about access method */
+	tup = SearchSysCache(AMNAME,
+						 CStringGetDatum(stmt->amname),
+						 0, 0, 0);
+	if (!HeapTupleIsValid(tup))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+						stmt->amname)));
+
+	amoid = HeapTupleGetOid(tup);
+	pg_am = (Form_pg_am) GETSTRUCT(tup);
+	maxOpNumber = pg_am->amstrategies;
+	/* if amstrategies is zero, just enforce that op numbers fit in int16 */
+	if (maxOpNumber <= 0)
+		maxOpNumber = SHRT_MAX;
+	maxProcNumber = pg_am->amsupport;
+
+	/* XXX Should we make any privilege check against the AM? */
+
+	ReleaseSysCache(tup);
+
+	/* Look up the opfamily */
+	tup = OpFamilyCacheLookup(amoid, stmt->opfamilyname);
+	if (!HeapTupleIsValid(tup))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+						NameListToString(stmt->opfamilyname), stmt->amname)));
+	opfamilyoid = HeapTupleGetOid(tup);
+	ReleaseSysCache(tup);
 
 	/*
-	 * btree support procs must be 2-arg procs returning int4; hash support
-	 * procs must be 1-arg procs returning int4; otherwise we don't know.
+	 * Currently, we require superuser privileges to alter an opfamily.
+	 *
+	 * XXX re-enable NOT_USED code sections below if you remove this test.
 	 */
-	if (amoid == BTREE_AM_OID)
-	{
-		if (procform->pronargs != 2)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("btree procedures must have two arguments")));
-		if (procform->prorettype != INT4OID)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("btree procedures must return integer")));
-
-		/*
-		 * If lefttype/righttype isn't specified, use the proc's input types
-		 */
-		if (!OidIsValid(member->lefttype))
-			member->lefttype = procform->proargtypes.values[0];
-		if (!OidIsValid(member->righttype))
-			member->righttype = procform->proargtypes.values[1];
-	}
-	else if (amoid == HASH_AM_OID)
-	{
-		if (procform->pronargs != 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("hash procedures must have one argument")));
-		if (procform->prorettype != INT4OID)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("hash procedures must return integer")));
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to alter an operator family")));
 
-		/*
-		 * If lefttype/righttype isn't specified, use the proc's input type
-		 */
-		if (!OidIsValid(member->lefttype))
-			member->lefttype = procform->proargtypes.values[0];
-		if (!OidIsValid(member->righttype))
-			member->righttype = procform->proargtypes.values[0];
-	}
+	/*
+	 * ADD and DROP cases need separate code from here on down.
+	 */
+	if (stmt->isDrop)
+		AlterOpFamilyDrop(stmt->opfamilyname, amoid, opfamilyoid,
+						  maxOpNumber, maxProcNumber,
+						  stmt->items);
 	else
-	{
-		/*
-		 * The default for GiST and GIN in CREATE OPERATOR CLASS is to use
-		 * the class' opcintype as lefttype and righttype.  In CREATE or
-		 * ALTER OPERATOR FAMILY, opcintype isn't available, so make the
-		 * user specify the types.
-		 */
-		if (!OidIsValid(member->lefttype))
-			member->lefttype = typeoid;
-		if (!OidIsValid(member->righttype))
-			member->righttype = typeoid;
-		if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("associated data types must be specified for index support procedure")));
-	}
-
-	ReleaseSysCache(proctup);
+		AlterOpFamilyAdd(stmt->opfamilyname, amoid, opfamilyoid,
+						 maxOpNumber, maxProcNumber,
+						 stmt->items);
 }
 
 /*
- * Add a new family member to the appropriate list, after checking for
- * duplicated strategy or proc number.
+ * ADD part of ALTER OP FAMILY
  */
 static void
-addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
+AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+				 int maxOpNumber, int maxProcNumber,
+				 List *items)
 {
+	List	   *operators;		/* OpFamilyMember list for operators */
+	List	   *procedures;		/* OpFamilyMember list for support procs */
 	ListCell   *l;
 
-	foreach(l, *list)
+	operators = NIL;
+	procedures = NIL;
+
+	/*
+	 * Scan the "items" list to obtain additional info.
+	 */
+	foreach(l, items)
 	{
-		OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
+		CreateOpClassItem *item = lfirst(l);
+		Oid			operOid;
+		Oid			funcOid;
+		OpFamilyMember *member;
 
-		if (old->number == member->number &&
-			old->lefttype == member->lefttype &&
-			old->righttype == member->righttype)
+		Assert(IsA(item, CreateOpClassItem));
+		switch (item->itemtype)
 		{
-			if (isProc)
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-						 errmsg("procedure number %d for (%s,%s) appears more than once",
-								member->number,
-								format_type_be(member->lefttype),
+			case OPCLASS_ITEM_OPERATOR:
+				if (item->number <= 0 || item->number > maxOpNumber)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("invalid operator number %d,"
+									" must be between 1 and %d",
+									item->number, maxOpNumber)));
+				if (item->args != NIL)
+				{
+					TypeName   *typeName1 = (TypeName *) linitial(item->args);
+					TypeName   *typeName2 = (TypeName *) lsecond(item->args);
+
+					operOid = LookupOperNameTypeNames(NULL, item->name,
+													  typeName1, typeName2,
+													  false, -1);
+				}
+				else
+				{
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("operator argument types must be specified in ALTER OPERATOR FAMILY")));
+					operOid = InvalidOid; /* keep compiler quiet */
+				}
+
+#ifdef NOT_USED
+				/* XXX this is unnecessary given the superuser check above */
+				/* Caller must own operator and its underlying function */
+				if (!pg_oper_ownercheck(operOid, GetUserId()))
+					aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
+								   get_opname(operOid));
+				funcOid = get_opcode(operOid);
+				if (!pg_proc_ownercheck(funcOid, GetUserId()))
+					aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+								   get_func_name(funcOid));
+#endif
+
+				/* Save the info */
+				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+				member->object = operOid;
+				member->number = item->number;
+				member->recheck = item->recheck;
+				assignOperTypes(member, amoid, InvalidOid);
+				addFamilyMember(&operators, member, false);
+				break;
+			case OPCLASS_ITEM_FUNCTION:
+				if (item->number <= 0 || item->number > maxProcNumber)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("invalid procedure number %d,"
+									" must be between 1 and %d",
+									item->number, maxProcNumber)));
+				funcOid = LookupFuncNameTypeNames(item->name, item->args,
+												  false);
+#ifdef NOT_USED
+				/* XXX this is unnecessary given the superuser check above */
+				/* Caller must own function */
+				if (!pg_proc_ownercheck(funcOid, GetUserId()))
+					aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+								   get_func_name(funcOid));
+#endif
+
+				/* Save the info */
+				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+				member->object = funcOid;
+				member->number = item->number;
+
+				/* allow overriding of the function's actual arg types */
+				if (item->class_args)
+					processTypesSpec(item->class_args,
+									 &member->lefttype, &member->righttype);
+
+				assignProcTypes(member, amoid, InvalidOid);
+				addFamilyMember(&procedures, member, true);
+				break;
+			case OPCLASS_ITEM_STORAGETYPE:
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						   errmsg("STORAGE may not be specified in ALTER OPERATOR FAMILY")));
+				break;
+			default:
+				elog(ERROR, "unrecognized item type: %d", item->itemtype);
+				break;
+		}
+	}
+
+	/*
+	 * Add tuples to pg_amop and pg_amproc tying in the operators and
+	 * functions.  Dependencies on them are inserted, too.
+	 */
+	storeOperators(opfamilyname, amoid, opfamilyoid,
+				   InvalidOid, operators, true);
+	storeProcedures(opfamilyname, amoid, opfamilyoid,
+					InvalidOid, procedures, true);
+}
+
+/*
+ * DROP part of ALTER OP FAMILY
+ */
+static void
+AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+				  int maxOpNumber, int maxProcNumber,
+				  List *items)
+{
+	List	   *operators;		/* OpFamilyMember list for operators */
+	List	   *procedures;		/* OpFamilyMember list for support procs */
+	ListCell   *l;
+
+	operators = NIL;
+	procedures = NIL;
+
+	/*
+	 * Scan the "items" list to obtain additional info.
+	 */
+	foreach(l, items)
+	{
+		CreateOpClassItem *item = lfirst(l);
+		Oid			lefttype,
+					righttype;
+		OpFamilyMember *member;
+
+		Assert(IsA(item, CreateOpClassItem));
+		switch (item->itemtype)
+		{
+			case OPCLASS_ITEM_OPERATOR:
+				if (item->number <= 0 || item->number > maxOpNumber)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("invalid operator number %d,"
+									" must be between 1 and %d",
+									item->number, maxOpNumber)));
+				processTypesSpec(item->args, &lefttype, &righttype);
+				/* Save the info */
+				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+				member->number = item->number;
+				member->lefttype = lefttype;
+				member->righttype = righttype;
+				addFamilyMember(&operators, member, false);
+				break;
+			case OPCLASS_ITEM_FUNCTION:
+				if (item->number <= 0 || item->number > maxProcNumber)
+					ereport(ERROR,
+							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+							 errmsg("invalid procedure number %d,"
+									" must be between 1 and %d",
+									item->number, maxProcNumber)));
+				processTypesSpec(item->args, &lefttype, &righttype);
+				/* Save the info */
+				member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+				member->number = item->number;
+				member->lefttype = lefttype;
+				member->righttype = righttype;
+				addFamilyMember(&procedures, member, true);
+				break;
+			case OPCLASS_ITEM_STORAGETYPE:
+				/* grammar prevents this from appearing */
+			default:
+				elog(ERROR, "unrecognized item type: %d", item->itemtype);
+				break;
+		}
+	}
+
+	/*
+	 * Remove tuples from pg_amop and pg_amproc.
+	 */
+	dropOperators(opfamilyname, amoid, opfamilyoid, operators);
+	dropProcedures(opfamilyname, amoid, opfamilyoid, procedures);
+}
+
+
+/*
+ * Deal with explicit arg types used in ALTER ADD/DROP
+ */
+static void
+processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
+{
+	TypeName   *typeName;
+
+	Assert(args != NIL);
+
+	typeName = (TypeName *) linitial(args);
+	*lefttype = typenameTypeId(NULL, typeName);
+
+	if (list_length(args) > 1)
+	{
+		typeName = (TypeName *) lsecond(args);
+		*righttype = typenameTypeId(NULL, typeName);
+	}
+	else
+		*righttype = *lefttype;
+
+	if (list_length(args) > 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("one or two argument types must be specified")));
+}
+
+
+/*
+ * Determine the lefttype/righttype to assign to an operator,
+ * and do any validity checking we can manage.
+ */
+static void
+assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+{
+	Operator	optup;
+	Form_pg_operator opform;
+
+	/* Fetch the operator definition */
+	optup = SearchSysCache(OPEROID,
+						   ObjectIdGetDatum(member->object),
+						   0, 0, 0);
+	if (optup == NULL)
+		elog(ERROR, "cache lookup failed for operator %u", member->object);
+	opform = (Form_pg_operator) GETSTRUCT(optup);
+
+	/*
+	 * Opfamily operators must be binary ops returning boolean.
+	 */
+	if (opform->oprkind != 'b')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("index operators must be binary")));
+	if (opform->oprresult != BOOLOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("index operators must return boolean")));
+
+	/*
+	 * If lefttype/righttype isn't specified, use the operator's input types
+	 */
+	if (!OidIsValid(member->lefttype))
+		member->lefttype = opform->oprleft;
+	if (!OidIsValid(member->righttype))
+		member->righttype = opform->oprright;
+
+	ReleaseSysCache(optup);
+}
+
+/*
+ * Determine the lefttype/righttype to assign to a support procedure,
+ * and do any validity checking we can manage.
+ */
+static void
+assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
+{
+	HeapTuple	proctup;
+	Form_pg_proc procform;
+
+	/* Fetch the procedure definition */
+	proctup = SearchSysCache(PROCOID,
+							 ObjectIdGetDatum(member->object),
+							 0, 0, 0);
+	if (proctup == NULL)
+		elog(ERROR, "cache lookup failed for function %u", member->object);
+	procform = (Form_pg_proc) GETSTRUCT(proctup);
+
+	/*
+	 * btree support procs must be 2-arg procs returning int4; hash support
+	 * procs must be 1-arg procs returning int4; otherwise we don't know.
+	 */
+	if (amoid == BTREE_AM_OID)
+	{
+		if (procform->pronargs != 2)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("btree procedures must have two arguments")));
+		if (procform->prorettype != INT4OID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("btree procedures must return integer")));
+
+		/*
+		 * If lefttype/righttype isn't specified, use the proc's input types
+		 */
+		if (!OidIsValid(member->lefttype))
+			member->lefttype = procform->proargtypes.values[0];
+		if (!OidIsValid(member->righttype))
+			member->righttype = procform->proargtypes.values[1];
+	}
+	else if (amoid == HASH_AM_OID)
+	{
+		if (procform->pronargs != 1)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("hash procedures must have one argument")));
+		if (procform->prorettype != INT4OID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("hash procedures must return integer")));
+
+		/*
+		 * If lefttype/righttype isn't specified, use the proc's input type
+		 */
+		if (!OidIsValid(member->lefttype))
+			member->lefttype = procform->proargtypes.values[0];
+		if (!OidIsValid(member->righttype))
+			member->righttype = procform->proargtypes.values[0];
+	}
+	else
+	{
+		/*
+		 * The default for GiST and GIN in CREATE OPERATOR CLASS is to use
+		 * the class' opcintype as lefttype and righttype.  In CREATE or
+		 * ALTER OPERATOR FAMILY, opcintype isn't available, so make the
+		 * user specify the types.
+		 */
+		if (!OidIsValid(member->lefttype))
+			member->lefttype = typeoid;
+		if (!OidIsValid(member->righttype))
+			member->righttype = typeoid;
+		if (!OidIsValid(member->lefttype) || !OidIsValid(member->righttype))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("associated data types must be specified for index support procedure")));
+	}
+
+	ReleaseSysCache(proctup);
+}
+
+/*
+ * Add a new family member to the appropriate list, after checking for
+ * duplicated strategy or proc number.
+ */
+static void
+addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
+{
+	ListCell   *l;
+
+	foreach(l, *list)
+	{
+		OpFamilyMember *old = (OpFamilyMember *) lfirst(l);
+
+		if (old->number == member->number &&
+			old->lefttype == member->lefttype &&
+			old->righttype == member->righttype)
+		{
+			if (isProc)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("procedure number %d for (%s,%s) appears more than once",
+								member->number,
+								format_type_be(member->lefttype),
 								format_type_be(member->righttype))));
 			else
 				ereport(ERROR,
@@ -781,7 +1218,9 @@ addFamilyMember(List **list, OpFamilyMember *member, bool isProc)
  * else make an AUTO dependency on the opfamily.
  */
 static void
-storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators)
+storeOperators(List *opfamilyname, Oid amoid,
+			   Oid opfamilyoid, Oid opclassoid,
+			   List *operators, bool isAdd)
 {
 	Relation	rel;
 	Datum		values[Natts_pg_amop];
@@ -798,6 +1237,24 @@ storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators)
 	{
 		OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
 
+		/*
+		 * If adding to an existing family, check for conflict with an
+		 * existing pg_amop entry (just to give a nicer error message)
+		 */
+		if (isAdd &&
+			SearchSysCacheExists(AMOPSTRATEGY,
+								 ObjectIdGetDatum(opfamilyoid),
+								 ObjectIdGetDatum(op->lefttype),
+								 ObjectIdGetDatum(op->righttype),
+								 Int16GetDatum(op->number)))
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_OBJECT),
+					 errmsg("operator %d(%s,%s) already exists in operator family \"%s\"",
+							op->number,
+							format_type_be(op->lefttype),
+							format_type_be(op->righttype),
+							NameListToString(opfamilyname))));
+
 		/* Create the pg_amop entry */
 		memset(values, 0, sizeof(values));
 		memset(nulls, ' ', sizeof(nulls));
@@ -862,7 +1319,9 @@ storeOperators(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *operators)
  * else make an AUTO dependency on the opfamily.
  */
 static void
-storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures)
+storeProcedures(List *opfamilyname, Oid amoid,
+				Oid opfamilyoid, Oid opclassoid,
+				List *procedures, bool isAdd)
 {
 	Relation	rel;
 	Datum		values[Natts_pg_amproc];
@@ -879,6 +1338,24 @@ storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures)
 	{
 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
 
+		/*
+		 * If adding to an existing family, check for conflict with an
+		 * existing pg_amproc entry (just to give a nicer error message)
+		 */
+		if (isAdd &&
+			SearchSysCacheExists(AMPROCNUM,
+								 ObjectIdGetDatum(opfamilyoid),
+								 ObjectIdGetDatum(proc->lefttype),
+								 ObjectIdGetDatum(proc->righttype),
+								 Int16GetDatum(proc->number)))
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_OBJECT),
+					 errmsg("function %d(%s,%s) already exists in operator family \"%s\"",
+							proc->number,
+							format_type_be(proc->lefttype),
+							format_type_be(proc->righttype),
+							NameListToString(opfamilyname))));
+
 		/* Create the pg_amproc entry */
 		memset(values, 0, sizeof(values));
 		memset(nulls, ' ', sizeof(nulls));
@@ -934,6 +1411,87 @@ storeProcedures(Oid amoid, Oid opfamilyoid, Oid opclassoid, List *procedures)
 }
 
 
+/*
+ * Remove operator entries from an opfamily.
+ *
+ * Note: this is only allowed for "loose" members of an opfamily, hence
+ * behavior is always RESTRICT.
+ */
+static void
+dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+			  List *operators)
+{
+	ListCell   *l;
+
+	foreach(l, operators)
+	{
+		OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
+		Oid			amopid;
+		ObjectAddress object;
+
+		amopid = GetSysCacheOid(AMOPSTRATEGY,
+								ObjectIdGetDatum(opfamilyoid),
+								ObjectIdGetDatum(op->lefttype),
+								ObjectIdGetDatum(op->righttype),
+								Int16GetDatum(op->number));
+		if (!OidIsValid(amopid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator %d(%s,%s) does not exist in operator family \"%s\"",
+							op->number,
+							format_type_be(op->lefttype),
+							format_type_be(op->righttype),
+							NameListToString(opfamilyname))));
+
+		object.classId = AccessMethodOperatorRelationId;
+		object.objectId = amopid;
+		object.objectSubId = 0;
+
+		performDeletion(&object, DROP_RESTRICT);
+	}
+}
+
+/*
+ * Remove procedure entries from an opfamily.
+ *
+ * Note: this is only allowed for "loose" members of an opfamily, hence
+ * behavior is always RESTRICT.
+ */
+static void
+dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
+			   List *procedures)
+{
+	ListCell   *l;
+
+	foreach(l, procedures)
+	{
+		OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
+		Oid			amprocid;
+		ObjectAddress object;
+
+		amprocid = GetSysCacheOid(AMPROCNUM,
+								  ObjectIdGetDatum(opfamilyoid),
+								  ObjectIdGetDatum(op->lefttype),
+								  ObjectIdGetDatum(op->righttype),
+								  Int16GetDatum(op->number));
+		if (!OidIsValid(amprocid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("function %d(%s,%s) does not exist in operator family \"%s\"",
+							op->number,
+							format_type_be(op->lefttype),
+							format_type_be(op->righttype),
+							NameListToString(opfamilyname))));
+
+		object.classId = AccessMethodProcedureRelationId;
+		object.objectId = amprocid;
+		object.objectSubId = 0;
+
+		performDeletion(&object, DROP_RESTRICT);
+	}
+}
+
+
 /*
  * RemoveOpClass
  *		Deletes an opclass.
@@ -997,6 +1555,70 @@ RemoveOpClass(RemoveOpClassStmt *stmt)
 	performDeletion(&object, stmt->behavior);
 }
 
+/*
+ * RemoveOpFamily
+ *		Deletes an opfamily.
+ */
+void
+RemoveOpFamily(RemoveOpFamilyStmt *stmt)
+{
+	Oid			amID,
+				opfID;
+	HeapTuple	tuple;
+	ObjectAddress object;
+
+	/*
+	 * Get the access method's OID.
+	 */
+	amID = GetSysCacheOid(AMNAME,
+						  CStringGetDatum(stmt->amname),
+						  0, 0, 0);
+	if (!OidIsValid(amID))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+						stmt->amname)));
+
+	/*
+	 * Look up the opfamily.
+	 */
+	tuple = OpFamilyCacheLookup(amID, stmt->opfamilyname);
+	if (!HeapTupleIsValid(tuple))
+	{
+		if (!stmt->missing_ok)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+						NameListToString(stmt->opfamilyname), stmt->amname)));
+		else
+			ereport(NOTICE,
+					(errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+						NameListToString(stmt->opfamilyname), stmt->amname)));
+		return;
+	}
+
+	opfID = HeapTupleGetOid(tuple);
+
+	/* Permission check: must own opfamily or its namespace */
+	if (!pg_opfamily_ownercheck(opfID, GetUserId()) &&
+		!pg_namespace_ownercheck(((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace,
+								 GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+					   NameListToString(stmt->opfamilyname));
+
+	ReleaseSysCache(tuple);
+
+	/*
+	 * Do the deletion
+	 */
+	object.classId = OperatorFamilyRelationId;
+	object.objectId = opfID;
+	object.objectSubId = 0;
+
+	performDeletion(&object, stmt->behavior);
+}
+
+
 /*
  * Deletion subroutines for use by dependency.c.
  */
@@ -1202,29 +1824,104 @@ RenameOpClass(List *name, const char *access_method, const char *newname)
 }
 
 /*
- * Change opclass owner by oid
+ * Rename opfamily
  */
-#ifdef NOT_USED
 void
-AlterOpClassOwner_oid(Oid opcOid, Oid newOwnerId)
+RenameOpFamily(List *name, const char *access_method, const char *newname)
 {
-	Relation	rel;
+	Oid			opfOid;
+	Oid			amOid;
+	Oid			namespaceOid;
+	char	   *schemaname;
+	char	   *opfname;
 	HeapTuple	tup;
+	Relation	rel;
+	AclResult	aclresult;
 
-	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
+	amOid = GetSysCacheOid(AMNAME,
+						   CStringGetDatum(access_method),
+						   0, 0, 0);
+	if (!OidIsValid(amOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+						access_method)));
 
-	tup = SearchSysCacheCopy(CLAOID,
-							 ObjectIdGetDatum(opcOid),
-							 0, 0, 0);
-	if (!HeapTupleIsValid(tup)) /* shouldn't happen */
-		elog(ERROR, "cache lookup failed for opclass %u", opcOid);
+	rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
 
-	AlterOpClassOwner_internal(rel, tup, newOwnerId);
+	/*
+	 * Look up the opfamily
+	 */
+	DeconstructQualifiedName(name, &schemaname, &opfname);
+
+	if (schemaname)
+	{
+		namespaceOid = LookupExplicitNamespace(schemaname);
+
+		tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP,
+								 ObjectIdGetDatum(amOid),
+								 PointerGetDatum(opfname),
+								 ObjectIdGetDatum(namespaceOid),
+								 0);
+		if (!HeapTupleIsValid(tup))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+							opfname, access_method)));
+
+		opfOid = HeapTupleGetOid(tup);
+	}
+	else
+	{
+		opfOid = OpfamilynameGetOpfid(amOid, opfname);
+		if (!OidIsValid(opfOid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+							opfname, access_method)));
+
+		tup = SearchSysCacheCopy(OPFAMILYOID,
+								 ObjectIdGetDatum(opfOid),
+								 0, 0, 0);
+		if (!HeapTupleIsValid(tup))		/* should not happen */
+			elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
+
+		namespaceOid = ((Form_pg_opfamily) GETSTRUCT(tup))->opfnamespace;
+	}
+
+	/* make sure the new name doesn't exist */
+	if (SearchSysCacheExists(OPFAMILYAMNAMENSP,
+							 ObjectIdGetDatum(amOid),
+							 CStringGetDatum(newname),
+							 ObjectIdGetDatum(namespaceOid),
+							 0))
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("operator family \"%s\" for access method \"%s\" already exists in schema \"%s\"",
+						newname, access_method,
+						get_namespace_name(namespaceOid))));
+	}
+
+	/* must be owner */
+	if (!pg_opfamily_ownercheck(opfOid, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+					   NameListToString(name));
+
+	/* must have CREATE privilege on namespace */
+	aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+					   get_namespace_name(namespaceOid));
+
+	/* rename */
+	namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname);
+	simple_heap_update(rel, &tup->t_self, tup);
+	CatalogUpdateIndexes(rel, tup);
 
-	heap_freetuple(tup);
 	heap_close(rel, NoLock);
+	heap_freetuple(tup);
 }
-#endif
 
 /*
  * Change opclass owner by name
@@ -1352,3 +2049,130 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 								newOwnerId);
 	}
 }
+
+/*
+ * Change opfamily owner by name
+ */
+void
+AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId)
+{
+	Oid			amOid;
+	Relation	rel;
+	HeapTuple	tup;
+	char	   *opfname;
+	char	   *schemaname;
+
+	amOid = GetSysCacheOid(AMNAME,
+						   CStringGetDatum(access_method),
+						   0, 0, 0);
+	if (!OidIsValid(amOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist",
+						access_method)));
+
+	rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
+
+	/*
+	 * Look up the opfamily
+	 */
+	DeconstructQualifiedName(name, &schemaname, &opfname);
+
+	if (schemaname)
+	{
+		Oid			namespaceOid;
+
+		namespaceOid = LookupExplicitNamespace(schemaname);
+
+		tup = SearchSysCacheCopy(OPFAMILYAMNAMENSP,
+								 ObjectIdGetDatum(amOid),
+								 PointerGetDatum(opfname),
+								 ObjectIdGetDatum(namespaceOid),
+								 0);
+		if (!HeapTupleIsValid(tup))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+							opfname, access_method)));
+	}
+	else
+	{
+		Oid			opfOid;
+
+		opfOid = OpfamilynameGetOpfid(amOid, opfname);
+		if (!OidIsValid(opfOid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("operator family \"%s\" does not exist for access method \"%s\"",
+							opfname, access_method)));
+
+		tup = SearchSysCacheCopy(OPFAMILYOID,
+								 ObjectIdGetDatum(opfOid),
+								 0, 0, 0);
+		if (!HeapTupleIsValid(tup))		/* should not happen */
+			elog(ERROR, "cache lookup failed for opfamily %u", opfOid);
+	}
+
+	AlterOpFamilyOwner_internal(rel, tup, newOwnerId);
+
+	heap_freetuple(tup);
+	heap_close(rel, NoLock);
+}
+
+/*
+ * The first parameter is pg_opfamily, opened and suitably locked.  The second
+ * parameter is a copy of the tuple from pg_opfamily we want to modify.
+ */
+static void
+AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
+{
+	Oid			namespaceOid;
+	AclResult	aclresult;
+	Form_pg_opfamily opfForm;
+
+	Assert(tup->t_tableOid == OperatorFamilyRelationId);
+	Assert(RelationGetRelid(rel) == OperatorFamilyRelationId);
+
+	opfForm = (Form_pg_opfamily) GETSTRUCT(tup);
+
+	namespaceOid = opfForm->opfnamespace;
+
+	/*
+	 * If the new owner is the same as the existing owner, consider the
+	 * command to have succeeded.  This is for dump restoration purposes.
+	 */
+	if (opfForm->opfowner != newOwnerId)
+	{
+		/* Superusers can always do it */
+		if (!superuser())
+		{
+			/* Otherwise, must be owner of the existing object */
+			if (!pg_opfamily_ownercheck(HeapTupleGetOid(tup), GetUserId()))
+				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+							   NameStr(opfForm->opfname));
+
+			/* Must be able to become new owner */
+			check_is_member_of_role(GetUserId(), newOwnerId);
+
+			/* New owner must have CREATE privilege on namespace */
+			aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId,
+											  ACL_CREATE);
+			if (aclresult != ACLCHECK_OK)
+				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+							   get_namespace_name(namespaceOid));
+		}
+
+		/*
+		 * Modify the owner --- okay to scribble on tup because it's a copy
+		 */
+		opfForm->opfowner = newOwnerId;
+
+		simple_heap_update(rel, &tup->t_self, tup);
+
+		CatalogUpdateIndexes(rel, tup);
+
+		/* Update owner dependency reference */
+		changeDependencyOnOwner(OperatorFamilyRelationId, HeapTupleGetOid(tup),
+								newOwnerId);
+	}
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1237dc7fe64..f213f216de4 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.363 2007/01/22 20:00:39 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.364 2007/01/23 05:07:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2158,6 +2158,19 @@ _copyRemoveOpClassStmt(RemoveOpClassStmt *from)
 	return newnode;
 }
 
+static RemoveOpFamilyStmt *
+_copyRemoveOpFamilyStmt(RemoveOpFamilyStmt *from)
+{
+	RemoveOpFamilyStmt *newnode = makeNode(RemoveOpFamilyStmt);
+
+	COPY_NODE_FIELD(opfamilyname);
+	COPY_STRING_FIELD(amname);
+	COPY_SCALAR_FIELD(behavior);
+	COPY_SCALAR_FIELD(missing_ok);
+
+	return newnode;
+}
+
 static RenameStmt *
 _copyRenameStmt(RenameStmt *from)
 {
@@ -2332,11 +2345,36 @@ _copyCreateOpClassItem(CreateOpClassItem *from)
 	COPY_NODE_FIELD(args);
 	COPY_SCALAR_FIELD(number);
 	COPY_SCALAR_FIELD(recheck);
+	COPY_NODE_FIELD(class_args);
 	COPY_NODE_FIELD(storedtype);
 
 	return newnode;
 }
 
+static CreateOpFamilyStmt *
+_copyCreateOpFamilyStmt(CreateOpFamilyStmt *from)
+{
+	CreateOpFamilyStmt *newnode = makeNode(CreateOpFamilyStmt);
+
+	COPY_NODE_FIELD(opfamilyname);
+	COPY_STRING_FIELD(amname);
+
+	return newnode;
+}
+
+static AlterOpFamilyStmt *
+_copyAlterOpFamilyStmt(AlterOpFamilyStmt *from)
+{
+	AlterOpFamilyStmt *newnode = makeNode(AlterOpFamilyStmt);
+
+	COPY_NODE_FIELD(opfamilyname);
+	COPY_STRING_FIELD(amname);
+	COPY_SCALAR_FIELD(isDrop);
+	COPY_NODE_FIELD(items);
+
+	return newnode;
+}
+
 static CreatedbStmt *
 _copyCreatedbStmt(CreatedbStmt *from)
 {
@@ -3163,6 +3201,9 @@ copyObject(void *from)
 		case T_RemoveOpClassStmt:
 			retval = _copyRemoveOpClassStmt(from);
 			break;
+		case T_RemoveOpFamilyStmt:
+			retval = _copyRemoveOpFamilyStmt(from);
+			break;
 		case T_RenameStmt:
 			retval = _copyRenameStmt(from);
 			break;
@@ -3205,6 +3246,12 @@ copyObject(void *from)
 		case T_CreateOpClassItem:
 			retval = _copyCreateOpClassItem(from);
 			break;
+		case T_CreateOpFamilyStmt:
+			retval = _copyCreateOpFamilyStmt(from);
+			break;
+		case T_AlterOpFamilyStmt:
+			retval = _copyAlterOpFamilyStmt(from);
+			break;
 		case T_CreatedbStmt:
 			retval = _copyCreatedbStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 40a91a2b0ed..31754a7bc02 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.296 2007/01/20 20:45:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.297 2007/01/23 05:07:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1053,6 +1053,17 @@ _equalRemoveOpClassStmt(RemoveOpClassStmt *a, RemoveOpClassStmt *b)
 	return true;
 }
 
+static bool
+_equalRemoveOpFamilyStmt(RemoveOpFamilyStmt *a, RemoveOpFamilyStmt *b)
+{
+	COMPARE_NODE_FIELD(opfamilyname);
+	COMPARE_STRING_FIELD(amname);
+	COMPARE_SCALAR_FIELD(behavior);
+	COMPARE_SCALAR_FIELD(missing_ok);
+
+	return true;
+}
+
 static bool
 _equalRenameStmt(RenameStmt *a, RenameStmt *b)
 {
@@ -1199,11 +1210,32 @@ _equalCreateOpClassItem(CreateOpClassItem *a, CreateOpClassItem *b)
 	COMPARE_NODE_FIELD(args);
 	COMPARE_SCALAR_FIELD(number);
 	COMPARE_SCALAR_FIELD(recheck);
+	COMPARE_NODE_FIELD(class_args);
 	COMPARE_NODE_FIELD(storedtype);
 
 	return true;
 }
 
+static bool
+_equalCreateOpFamilyStmt(CreateOpFamilyStmt *a, CreateOpFamilyStmt *b)
+{
+	COMPARE_NODE_FIELD(opfamilyname);
+	COMPARE_STRING_FIELD(amname);
+
+	return true;
+}
+
+static bool
+_equalAlterOpFamilyStmt(AlterOpFamilyStmt *a, AlterOpFamilyStmt *b)
+{
+	COMPARE_NODE_FIELD(opfamilyname);
+	COMPARE_STRING_FIELD(amname);
+	COMPARE_SCALAR_FIELD(isDrop);
+	COMPARE_NODE_FIELD(items);
+
+	return true;
+}
+
 static bool
 _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b)
 {
@@ -2148,6 +2180,9 @@ equal(void *a, void *b)
 		case T_RemoveOpClassStmt:
 			retval = _equalRemoveOpClassStmt(a, b);
 			break;
+		case T_RemoveOpFamilyStmt:
+			retval = _equalRemoveOpFamilyStmt(a, b);
+			break;
 		case T_RenameStmt:
 			retval = _equalRenameStmt(a, b);
 			break;
@@ -2190,6 +2225,12 @@ equal(void *a, void *b)
 		case T_CreateOpClassItem:
 			retval = _equalCreateOpClassItem(a, b);
 			break;
+		case T_CreateOpFamilyStmt:
+			retval = _equalCreateOpFamilyStmt(a, b);
+			break;
+		case T_AlterOpFamilyStmt:
+			retval = _equalAlterOpFamilyStmt(a, b);
+			break;
 		case T_CreatedbStmt:
 			retval = _equalCreatedbStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cf32e912539..217b1a04659 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.575 2007/01/22 01:35:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.576 2007/01/23 05:07:17 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -152,11 +152,12 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 		AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
 		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
 		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
-		CreateDomainStmt CreateGroupStmt CreateOpClassStmt CreatePLangStmt
+		CreateDomainStmt CreateGroupStmt CreateOpClassStmt
+		CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
 		CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt
 		CreateAssertStmt CreateTrigStmt CreateUserStmt CreateRoleStmt
 		CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt
-		DropGroupStmt DropOpClassStmt DropPLangStmt DropStmt
+		DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt
 		DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt
 		DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt
 		GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt
@@ -174,7 +175,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 %type <node>	select_no_parens select_with_parens select_clause
 				simple_select values_clause
 
-%type <node>	alter_column_default opclass_item alter_using
+%type <node>	alter_column_default opclass_item opclass_drop alter_using
 %type <ival>	add_drop opt_asc_desc opt_nulls_order
 
 %type <node>	alter_table_cmd alter_rel_cmd
@@ -229,7 +230,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 				OptTableElementList TableElementList OptInherit definition
 				OptWith opt_distinct opt_definition func_args func_args_list
 				func_as createfunc_opt_list alterfunc_opt_list
-				aggr_args aggr_args_list old_aggr_definition old_aggr_list
+				aggr_args old_aggr_definition old_aggr_list
 				oper_argtypes RuleActionList RuleActionMulti
 				opt_column_list columnList opt_name_list
 				sort_clause opt_sort_clause sortby_list index_params
@@ -240,10 +241,10 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 				set_clause_list set_clause multiple_set_clause
 				ctext_expr_list ctext_row def_list indirection opt_indirection
 				group_clause TriggerFuncArgs select_limit
-				opt_select_limit opclass_item_list
-				transaction_mode_list_or_empty
+				opt_select_limit opclass_item_list opclass_drop_list
+				opt_opfamily transaction_mode_list_or_empty
 				TableFuncElementList opt_type_modifiers
-				prep_type_clause prep_type_list
+				prep_type_clause
 				execute_param_clause using_clause returning_clause
 
 %type <range>	into_clause OptTempTableName
@@ -381,7 +382,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
 	EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
 
-	FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
+	FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
 	FREEZE FROM FULL FUNCTION
 
 	GLOBAL GRANT GRANTED GREATEST GROUP_P
@@ -548,6 +549,8 @@ stmt :
 			| CreateFunctionStmt
 			| CreateGroupStmt
 			| CreateOpClassStmt
+			| CreateOpFamilyStmt
+			| AlterOpFamilyStmt
 			| CreatePLangStmt
 			| CreateSchemaStmt
 			| CreateSeqStmt
@@ -565,6 +568,7 @@ stmt :
 			| DropCastStmt
 			| DropGroupStmt
 			| DropOpClassStmt
+			| DropOpFamilyStmt
 			| DropOwnedStmt
 			| DropPLangStmt
 			| DropRuleStmt
@@ -929,7 +933,7 @@ AlterGroupStmt:
 				}
 		;
 
-add_drop:	ADD_P										{ $$ = +1; }
+add_drop:	ADD_P									{ $$ = +1; }
 			| DROP									{ $$ = -1; }
 		;
 
@@ -2879,15 +2883,10 @@ def_arg:	func_type						{ $$ = (Node *)$1; }
 			| Sconst						{ $$ = (Node *)makeString($1); }
 		;
 
-aggr_args:	'(' aggr_args_list ')'					{ $$ = $2; }
+aggr_args:	'(' type_list ')'						{ $$ = $2; }
 			| '(' '*' ')'							{ $$ = NIL; }
 		;
 
-aggr_args_list:
-			Typename								{ $$ = list_make1($1); }
-			| aggr_args_list ',' Typename			{ $$ = lappend($1, $3); }
-		;
-
 old_aggr_definition: '(' old_aggr_list ')'			{ $$ = $2; }
 		;
 
@@ -2906,20 +2905,24 @@ old_aggr_elem:  IDENT '=' def_arg
  *
  *		QUERIES :
  *				CREATE OPERATOR CLASS ...
+ *				CREATE OPERATOR FAMILY ...
+ *				ALTER OPERATOR FAMILY ...
  *				DROP OPERATOR CLASS ...
+ *				DROP OPERATOR FAMILY ...
  *
  *****************************************************************************/
 
 CreateOpClassStmt:
 			CREATE OPERATOR CLASS any_name opt_default FOR TYPE_P Typename
-			USING access_method AS opclass_item_list
+			USING access_method opt_opfamily AS opclass_item_list
 				{
 					CreateOpClassStmt *n = makeNode(CreateOpClassStmt);
 					n->opclassname = $4;
 					n->isDefault = $5;
 					n->datatype = $8;
 					n->amname = $10;
-					n->items = $12;
+					n->opfamilyname = $11;
+					n->items = $13;
 					$$ = (Node *) n;
 				}
 		;
@@ -2959,6 +2962,16 @@ opclass_item:
 					n->number = $2;
 					$$ = (Node *) n;
 				}
+			| FUNCTION Iconst '(' type_list ')' func_name func_args
+				{
+					CreateOpClassItem *n = makeNode(CreateOpClassItem);
+					n->itemtype = OPCLASS_ITEM_FUNCTION;
+					n->name = $6;
+					n->args = extractArgTypes($7);
+					n->number = $2;
+					n->class_args = $4;
+					$$ = (Node *) n;
+				}
 			| STORAGE Typename
 				{
 					CreateOpClassItem *n = makeNode(CreateOpClassItem);
@@ -2968,12 +2981,72 @@ opclass_item:
 				}
 		;
 
-opt_default:	DEFAULT	{ $$ = TRUE; }
-			| /*EMPTY*/	{ $$ = FALSE; }
+opt_default:	DEFAULT						{ $$ = TRUE; }
+			| /*EMPTY*/						{ $$ = FALSE; }
+		;
+
+opt_opfamily:	FAMILY any_name				{ $$ = $2; }
+			| /*EMPTY*/						{ $$ = NIL; }
+		;
+
+opt_recheck:	RECHECK						{ $$ = TRUE; }
+			| /*EMPTY*/						{ $$ = FALSE; }
+		;
+
+
+CreateOpFamilyStmt:
+			CREATE OPERATOR FAMILY any_name USING access_method
+				{
+					CreateOpFamilyStmt *n = makeNode(CreateOpFamilyStmt);
+					n->opfamilyname = $4;
+					n->amname = $6;
+					$$ = (Node *) n;
+				}
+		;
+
+AlterOpFamilyStmt:
+			ALTER OPERATOR FAMILY any_name USING access_method ADD_P opclass_item_list
+				{
+					AlterOpFamilyStmt *n = makeNode(AlterOpFamilyStmt);
+					n->opfamilyname = $4;
+					n->amname = $6;
+					n->isDrop = false;
+					n->items = $8;
+					$$ = (Node *) n;
+				}
+			| ALTER OPERATOR FAMILY any_name USING access_method DROP opclass_drop_list
+				{
+					AlterOpFamilyStmt *n = makeNode(AlterOpFamilyStmt);
+					n->opfamilyname = $4;
+					n->amname = $6;
+					n->isDrop = true;
+					n->items = $8;
+					$$ = (Node *) n;
+				}
+		;
+
+opclass_drop_list:
+			opclass_drop							{ $$ = list_make1($1); }
+			| opclass_drop_list ',' opclass_drop	{ $$ = lappend($1, $3); }
 		;
 
-opt_recheck:	RECHECK	{ $$ = TRUE; }
-			| /*EMPTY*/	{ $$ = FALSE; }
+opclass_drop:
+			OPERATOR Iconst '(' type_list ')'
+				{
+					CreateOpClassItem *n = makeNode(CreateOpClassItem);
+					n->itemtype = OPCLASS_ITEM_OPERATOR;
+					n->number = $2;
+					n->args = $4;
+					$$ = (Node *) n;
+				}
+			| FUNCTION Iconst '(' type_list ')'
+				{
+					CreateOpClassItem *n = makeNode(CreateOpClassItem);
+					n->itemtype = OPCLASS_ITEM_FUNCTION;
+					n->number = $2;
+					n->args = $4;
+					$$ = (Node *) n;
+				}
 		;
 
 
@@ -2998,6 +3071,28 @@ DropOpClassStmt:
 				}
 		;
 
+DropOpFamilyStmt:
+			DROP OPERATOR FAMILY any_name USING access_method opt_drop_behavior
+				{
+					RemoveOpFamilyStmt *n = makeNode(RemoveOpFamilyStmt);
+					n->opfamilyname = $4;
+					n->amname = $6;
+					n->behavior = $7;
+					n->missing_ok = false;
+					$$ = (Node *) n;
+				}
+			| DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior
+				{
+					RemoveOpFamilyStmt *n = makeNode(RemoveOpFamilyStmt);
+					n->opfamilyname = $6;
+					n->amname = $8;
+					n->behavior = $9;
+					n->missing_ok = true;
+					$$ = (Node *) n;
+				}
+		;
+
+
 /*****************************************************************************
  *
  *		QUERY:
@@ -3201,6 +3296,15 @@ CommentStmt:
 					n->comment = $9;
 					$$ = (Node *) n;
 				}
+			| COMMENT ON OPERATOR FAMILY any_name USING access_method IS comment_text
+				{
+					CommentStmt *n = makeNode(CommentStmt);
+					n->objtype = OBJECT_OPFAMILY;
+					n->objname = $5;
+					n->objargs = list_make1(makeString($7));
+					n->comment = $9;
+					$$ = (Node *) n;
+				}
 			| COMMENT ON LARGE_P OBJECT_P NumericOnly IS comment_text
 				{
 					CommentStmt *n = makeNode(CommentStmt);
@@ -4115,9 +4219,9 @@ oper_argtypes:
 				}
 			| Typename ',' Typename
 					{ $$ = list_make2($1, $3); }
-			| NONE ',' Typename /* left unary */
+			| NONE ',' Typename							/* left unary */
 					{ $$ = list_make2(NULL, $3); }
-			| Typename ',' NONE /* right unary */
+			| Typename ',' NONE							/* right unary */
 					{ $$ = list_make2($1, NULL); }
 		;
 
@@ -4174,8 +4278,8 @@ DropCastStmt: DROP CAST opt_if_exists '(' Typename AS Typename ')' opt_drop_beha
 				}
 		;
 
-opt_if_exists: IF_P EXISTS						{ $$ = true; }
-		| /*EMPTY*/								{ $$ = false; }
+opt_if_exists: IF_P EXISTS						{ $$ = TRUE; }
+		| /*EMPTY*/								{ $$ = FALSE; }
 		;
 
 
@@ -4294,6 +4398,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 					n->newname = $9;
 					$$ = (Node *)n;
 				}
+			| ALTER OPERATOR FAMILY any_name USING access_method RENAME TO name
+				{
+					RenameStmt *n = makeNode(RenameStmt);
+					n->renameType = OBJECT_OPFAMILY;
+					n->object = $4;
+					n->subname = $6;
+					n->newname = $9;
+					$$ = (Node *)n;
+				}
 			| ALTER SCHEMA name RENAME TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
@@ -4493,6 +4606,15 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
 					n->newowner = $9;
 					$$ = (Node *)n;
 				}
+			| ALTER OPERATOR FAMILY any_name USING access_method OWNER TO RoleId
+				{
+					AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+					n->objectType = OBJECT_OPFAMILY;
+					n->object = $4;
+					n->addname = $6;
+					n->newowner = $9;
+					$$ = (Node *)n;
+				}
 			| ALTER SCHEMA name OWNER TO RoleId
 				{
 					AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
@@ -5302,15 +5424,10 @@ PrepareStmt: PREPARE name prep_type_clause AS PreparableStmt
 				}
 		;
 
-prep_type_clause: '(' prep_type_list ')'	{ $$ = $2; }
+prep_type_clause: '(' type_list ')'			{ $$ = $2; }
 				| /* EMPTY */				{ $$ = NIL; }
 		;
 
-prep_type_list: Typename			{ $$ = list_make1($1); }
-			  | prep_type_list ',' Typename
-									{ $$ = lappend($1, $3); }
-		;
-
 PreparableStmt:
 			SelectStmt
 			| InsertStmt
@@ -7968,14 +8085,8 @@ extract_list:
 			| /*EMPTY*/								{ $$ = NIL; }
 		;
 
-type_list:  type_list ',' Typename
-				{
-					$$ = lappend($1, $3);
-				}
-			| Typename
-				{
-					$$ = list_make1($1);
-				}
+type_list:	Typename								{ $$ = list_make1($1); }
+			| type_list ',' Typename				{ $$ = lappend($1, $3); }
 		;
 
 array_expr_list: array_expr
@@ -8604,6 +8715,7 @@ unreserved_keyword:
 			| EXECUTE
 			| EXPLAIN
 			| EXTERNAL
+			| FAMILY
 			| FETCH
 			| FIRST_P
 			| FORCE
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 5e858d10b0a..b8607c7c002 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.182 2007/01/22 01:35:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.183 2007/01/23 05:07:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -145,6 +145,7 @@ static const ScanKeyword ScanKeywords[] = {
 	{"external", EXTERNAL},
 	{"extract", EXTRACT},
 	{"false", FALSE_P},
+	{"family", FAMILY},
 	{"fetch", FETCH},
 	{"first", FIRST_P},
 	{"float", FLOAT_P},
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 19b89916bf1..613ef653d19 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.270 2007/01/05 22:19:39 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.271 2007/01/23 05:07:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -322,6 +322,8 @@ check_xact_readonly(Node *parsetree)
 		case T_IndexStmt:
 		case T_CreatePLangStmt:
 		case T_CreateOpClassStmt:
+		case T_CreateOpFamilyStmt:
+		case T_AlterOpFamilyStmt:
 		case T_RuleStmt:
 		case T_CreateSchemaStmt:
 		case T_CreateSeqStmt:
@@ -338,6 +340,7 @@ check_xact_readonly(Node *parsetree)
 		case T_DropRoleStmt:
 		case T_DropPLangStmt:
 		case T_RemoveOpClassStmt:
+		case T_RemoveOpFamilyStmt:
 		case T_DropPropertyStmt:
 		case T_GrantStmt:
 		case T_GrantRoleStmt:
@@ -1099,10 +1102,22 @@ ProcessUtility(Node *parsetree,
 			DefineOpClass((CreateOpClassStmt *) parsetree);
 			break;
 
+		case T_CreateOpFamilyStmt:
+			DefineOpFamily((CreateOpFamilyStmt *) parsetree);
+			break;
+
+		case T_AlterOpFamilyStmt:
+			AlterOpFamily((AlterOpFamilyStmt *) parsetree);
+			break;
+
 		case T_RemoveOpClassStmt:
 			RemoveOpClass((RemoveOpClassStmt *) parsetree);
 			break;
 
+		case T_RemoveOpFamilyStmt:
+			RemoveOpFamily((RemoveOpFamilyStmt *) parsetree);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(parsetree));
@@ -1445,6 +1460,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_OPCLASS:
 					tag = "ALTER OPERATOR CLASS";
 					break;
+				case OBJECT_OPFAMILY:
+					tag = "ALTER OPERATOR FAMILY";
+					break;
 				case OBJECT_ROLE:
 					tag = "ALTER ROLE";
 					break;
@@ -1518,6 +1536,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_OPCLASS:
 					tag = "ALTER OPERATOR CLASS";
 					break;
+				case OBJECT_OPFAMILY:
+					tag = "ALTER OPERATOR FAMILY";
+					break;
 				case OBJECT_SCHEMA:
 					tag = "ALTER SCHEMA";
 					break;
@@ -1777,10 +1798,22 @@ CreateCommandTag(Node *parsetree)
 			tag = "CREATE OPERATOR CLASS";
 			break;
 
+		case T_CreateOpFamilyStmt:
+			tag = "CREATE OPERATOR FAMILY";
+			break;
+
+		case T_AlterOpFamilyStmt:
+			tag = "ALTER OPERATOR FAMILY";
+			break;
+
 		case T_RemoveOpClassStmt:
 			tag = "DROP OPERATOR CLASS";
 			break;
 
+		case T_RemoveOpFamilyStmt:
+			tag = "DROP OPERATOR FAMILY";
+			break;
+
 		case T_PrepareStmt:
 			tag = "PREPARE";
 			break;
@@ -2147,10 +2180,22 @@ GetCommandLogLevel(Node *parsetree)
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_CreateOpFamilyStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
+		case T_AlterOpFamilyStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_RemoveOpClassStmt:
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_RemoveOpFamilyStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_PrepareStmt:
 			{
 				PrepareStmt *stmt = (PrepareStmt *) parsetree;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 9c254b0b481..3d665ff5c2c 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.79 2007/01/05 22:19:53 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.80 2007/01/23 05:07:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -79,13 +79,18 @@ extern void AlterAggregateOwner(List *name, List *args, Oid newOwnerId);
 
 /* commands/opclasscmds.c */
 extern void DefineOpClass(CreateOpClassStmt *stmt);
+extern void DefineOpFamily(CreateOpFamilyStmt *stmt);
+extern void AlterOpFamily(AlterOpFamilyStmt *stmt);
 extern void RemoveOpClass(RemoveOpClassStmt *stmt);
+extern void RemoveOpFamily(RemoveOpFamilyStmt *stmt);
 extern void RemoveOpClassById(Oid opclassOid);
 extern void RemoveOpFamilyById(Oid opfamilyOid);
 extern void RemoveAmOpEntryById(Oid entryOid);
 extern void RemoveAmProcEntryById(Oid entryOid);
 extern void RenameOpClass(List *name, const char *access_method, const char *newname);
+extern void RenameOpFamily(List *name, const char *access_method, const char *newname);
 extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId);
+extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId);
 
 /* support routines in commands/define.c */
 
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d3e84bdf69a..f3762facdd6 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.192 2007/01/20 20:45:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.193 2007/01/23 05:07:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -286,7 +286,10 @@ typedef enum NodeTag
 	T_CreateCastStmt,
 	T_DropCastStmt,
 	T_CreateOpClassStmt,
+	T_CreateOpFamilyStmt,
+	T_AlterOpFamilyStmt,
 	T_RemoveOpClassStmt,
+	T_RemoveOpFamilyStmt,
 	T_PrepareStmt,
 	T_ExecuteStmt,
 	T_DeallocateStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index d11f9ae6ead..a252308bdb2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.338 2007/01/09 02:14:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.339 2007/01/23 05:07:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -852,6 +852,7 @@ typedef enum ObjectType
 	OBJECT_LARGEOBJECT,
 	OBJECT_OPCLASS,
 	OBJECT_OPERATOR,
+	OBJECT_OPFAMILY,
 	OBJECT_ROLE,
 	OBJECT_RULE,
 	OBJECT_SCHEMA,
@@ -1194,7 +1195,7 @@ typedef struct DropTableSpaceStmt
 {
 	NodeTag		type;
 	char	   *tablespacename;
-	bool		missing_ok;		/* skip error if a missing? */
+	bool		missing_ok;		/* skip error if missing? */
 } DropTableSpaceStmt;
 
 /* ----------------------
@@ -1362,10 +1363,35 @@ typedef struct CreateOpClassItem
 	List	   *args;			/* argument types */
 	int			number;			/* strategy num or support proc num */
 	bool		recheck;		/* only used for operators */
+	List	   *class_args;		/* only used for functions */
 	/* fields used for a storagetype item: */
 	TypeName   *storedtype;		/* datatype stored in index */
 } CreateOpClassItem;
 
+/* ----------------------
+ *		Create Operator Family Statement
+ * ----------------------
+ */
+typedef struct CreateOpFamilyStmt
+{
+	NodeTag		type;
+	List	   *opfamilyname;	/* qualified name (list of Value strings) */
+	char	   *amname;			/* name of index AM opfamily is for */
+} CreateOpFamilyStmt;
+
+/* ----------------------
+ *		Alter Operator Family Statement
+ * ----------------------
+ */
+typedef struct AlterOpFamilyStmt
+{
+	NodeTag		type;
+	List	   *opfamilyname;	/* qualified name (list of Value strings) */
+	char	   *amname;			/* name of index AM opfamily is for */
+	bool		isDrop;			/* ADD or DROP the items? */
+	List	   *items;			/* List of CreateOpClassItem nodes */
+} AlterOpFamilyStmt;
+
 /* ----------------------
  *		Drop Table|Sequence|View|Index|Type|Domain|Conversion|Schema Statement
  * ----------------------
@@ -1395,7 +1421,7 @@ typedef struct DropPropertyStmt
 	char	   *property;		/* name of rule, trigger, etc */
 	ObjectType	removeType;		/* OBJECT_RULE or OBJECT_TRIGGER */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
-	bool		missing_ok;		/* skip error if a missing? */
+	bool		missing_ok;		/* skip error if missing? */
 } DropPropertyStmt;
 
 /* ----------------------
@@ -1546,7 +1572,7 @@ typedef struct RemoveFuncStmt
 	List	   *name;			/* qualified name of object to drop */
 	List	   *args;			/* types of the arguments */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
-	bool		missing_ok;		/* skip error if a missing? */
+	bool		missing_ok;		/* skip error if missing? */
 } RemoveFuncStmt;
 
 /* ----------------------
@@ -1559,9 +1585,22 @@ typedef struct RemoveOpClassStmt
 	List	   *opclassname;	/* qualified name (list of Value strings) */
 	char	   *amname;			/* name of index AM opclass is for */
 	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
-	bool		missing_ok;		/* skip error if a missing? */
+	bool		missing_ok;		/* skip error if missing? */
 } RemoveOpClassStmt;
 
+/* ----------------------
+ *		Drop Operator Family Statement
+ * ----------------------
+ */
+typedef struct RemoveOpFamilyStmt
+{
+	NodeTag		type;
+	List	   *opfamilyname;	/* qualified name (list of Value strings) */
+	char	   *amname;			/* name of index AM opfamily is for */
+	DropBehavior behavior;		/* RESTRICT or CASCADE behavior */
+	bool		missing_ok;		/* skip error if missing? */
+} RemoveOpFamilyStmt;
+
 /* ----------------------
  *		Alter Object Rename Statement
  * ----------------------
@@ -1917,7 +1956,7 @@ typedef struct DropCastStmt
 	TypeName   *sourcetype;
 	TypeName   *targettype;
 	DropBehavior behavior;
-	bool		missing_ok;		/* skip error if a missing? */
+	bool		missing_ok;		/* skip error if missing? */
 } DropCastStmt;
 
 
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 2076a69f3de..14522bc6fb1 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.99 2007/01/05 22:19:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.100 2007/01/23 05:07:18 tgl Exp $
  *
  * NOTES
  *	  An ACL array is simply an array of AclItems, representing the union
@@ -178,6 +178,7 @@ typedef enum AclObjectKind
 	ACL_KIND_LANGUAGE,			/* pg_language */
 	ACL_KIND_NAMESPACE,			/* pg_namespace */
 	ACL_KIND_OPCLASS,			/* pg_opclass */
+	ACL_KIND_OPFAMILY,			/* pg_opfamily */
 	ACL_KIND_CONVERSION,		/* pg_conversion */
 	ACL_KIND_TABLESPACE,		/* pg_tablespace */
 	MAX_ACL_KIND				/* MUST BE LAST */
@@ -276,6 +277,7 @@ extern bool pg_proc_ownercheck(Oid proc_oid, Oid roleid);
 extern bool pg_namespace_ownercheck(Oid nsp_oid, Oid roleid);
 extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid);
 extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid);
+extern bool pg_opfamily_ownercheck(Oid opf_oid, Oid roleid);
 extern bool pg_database_ownercheck(Oid db_oid, Oid roleid);
 extern bool pg_conversion_ownercheck(Oid conv_oid, Oid roleid);
 
-- 
GitLab