diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
new file mode 100644
index 97ef618..48f208a
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 549,762 ****
       </row>
  
       <row>
!       <entry><structfield>amstrategies</structfield></entry>
!       <entry><type>int2</type></entry>
!       <entry></entry>
!       <entry>Number of operator strategies for this access method,
!        or zero if access method does not have a fixed set of operator
!        strategies</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amsupport</structfield></entry>
!       <entry><type>int2</type></entry>
!       <entry></entry>
!       <entry>Number of support routines for this access method</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcanorder</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support ordered scans sorted by the
!        indexed column's value?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcanorderbyop</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support ordered scans sorted by the result
!        of an operator on the indexed column?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcanbackward</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support backward scanning?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcanunique</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support unique indexes?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcanmulticol</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support multicolumn indexes?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amoptionalkey</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support a scan without any constraint
!        for the first index column?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amsearcharray</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support <literal>ScalarArrayOpExpr</> searches?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amsearchnulls</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does the access method support <literal>IS NULL</>/<literal>NOT NULL</> searches?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amstorage</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Can index storage data type differ from column data type?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amclusterable</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Can an index of this type be clustered on?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>ampredlocks</structfield></entry>
!       <entry><type>bool</type></entry>
!       <entry></entry>
!       <entry>Does an index of this type manage fine-grained predicate locks?</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amkeytype</structfield></entry>
        <entry><type>oid</type></entry>
-       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
-       <entry>Type of data stored in index, or zero if not a fixed type</entry>
-      </row>
- 
-      <row>
-       <entry><structfield>aminsert</structfield></entry>
-       <entry><type>regproc</type></entry>
        <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Insert this tuple</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>ambeginscan</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Prepare for index scan</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amgettuple</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Next valid tuple</quote> function, or zero if none</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amgetbitmap</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Fetch all valid tuples</quote> function, or zero if none</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amrescan</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>(Re)start index scan</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amendscan</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Clean up after index scan</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>ammarkpos</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Mark current scan position</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amrestrpos</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Restore marked scan position</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>ambuild</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Build new index</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>ambuildempty</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry><quote>Build empty index</quote> function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>ambulkdelete</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry>Bulk-delete function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amvacuumcleanup</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry>Post-<command>VACUUM</command> cleanup function</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcanreturn</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry>Function to check whether an index column supports index-only
!        scans. Can be zero if index-only scans are never supported.</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amcostestimate</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry>Function to estimate cost of an index scan</entry>
!      </row>
! 
!      <row>
!       <entry><structfield>amoptions</structfield></entry>
!       <entry><type>regproc</type></entry>
!       <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry>Function to parse and validate <structfield>reloptions</> for an index</entry>
       </row>
- 
      </tbody>
     </tgroup>
    </table>
--- 549,562 ----
       </row>
  
       <row>
!       <entry><structfield>amhandler</structfield></entry>
        <entry><type>oid</type></entry>
        <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
!       <entry>
!        References a handler function that is responsible for
!        supplying execution routines for the access method.
!       </entry>
       </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 1c09bae..3ff3651
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 49,61 ****
    <para>
     Each index access method is described by a row in the
     <structname>pg_am</structname> system catalog (see
!    <xref linkend="catalog-pg-am">).  The principal contents of a
!    <structname>pg_am</structname> row are references to
!    <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>
!    entries that identify the index access
!    functions supplied by the access method.  The APIs for these functions
!    are defined later in this chapter.  In addition, the
!    <structname>pg_am</structname> row specifies a few fixed properties of
     the access method, such as whether it can support multicolumn indexes.
     There is not currently any special support
     for creating or deleting <structname>pg_am</structname> entries;
--- 49,63 ----
    <para>
     Each index access method is described by a row in the
     <structname>pg_am</structname> system catalog (see
!    <xref linkend="catalog-pg-am">).  In the <structname>pg_am</structname> row
!    the handler function is specified by the reference to
!    <link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.
!    Handler function must be written in a compiled language such as C, using
!    the version-1 interface.  The handler function simply returns a
!    <structname>IndexAmRoutine</structname> struct which contains function
!    pointers to callback functions that will be called by the planner, executor,
!    and various maintenance commands.  In addition, the
!    <structname>IndexAmRoutine</structname> contains a few fixed properties of
     the access method, such as whether it can support multicolumn indexes.
     There is not currently any special support
     for creating or deleting <structname>pg_am</structname> entries;
***************
*** 96,126 ****
    </para>
  
    <para>
!    Some of the flag columns of <structname>pg_am</structname> have nonobvious
!    implications.  The requirements of <structfield>amcanunique</structfield>
!    are discussed in <xref linkend="index-unique-checks">.
!    The <structfield>amcanmulticol</structfield> flag asserts that the
!    access method supports multicolumn indexes, while
!    <structfield>amoptionalkey</structfield> asserts that it allows scans
     where no indexable restriction clause is given for the first index column.
!    When <structfield>amcanmulticol</structfield> is false,
!    <structfield>amoptionalkey</structfield> essentially says whether the
     access method supports full-index scans without any restriction clause.
     Access methods that support multiple index columns <emphasis>must</>
     support scans that omit restrictions on any or all of the columns after
     the first; however they are permitted to require some restriction to
     appear for the first index column, and this is signaled by setting
!    <structfield>amoptionalkey</structfield> false.
!    One reason that an index AM might set
!    <structfield>amoptionalkey</structfield> false is if it doesn't index
!    null values.  Since most indexable operators are
     strict and hence cannot return true for null inputs,
     it is at first sight attractive to not store index entries for null values:
     they could never be returned by an index scan anyway.  However, this
     argument fails when an index scan has no restriction clause for a given
     index column.  In practice this means that
!    indexes that have <structfield>amoptionalkey</structfield> true must
!    index nulls, since the planner might decide to use such an index
     with no scan keys at all.  A related restriction is that an index
     access method that supports multiple index columns <emphasis>must</>
     support indexing null values in columns after the first, because the planner
--- 98,133 ----
    </para>
  
    <para>
!    Some of the flag fields of <structname>IndexAmRoutine</structname> have nonobvious
!    implications.  The requirements of
!    <structname>IndexAmRoutine</structname>.<structfield>amcanunique</structfield>
!    are discussed in <xref linkend="index-unique-checks">. The
!    <structname>IndexAmRoutine</structname>.<structfield>amcanmulticol</structfield>
!    flag asserts that the access method supports multicolumn indexes, while
!    <structname>IndexAmRoutine</structname>.<structfield>amoptionalkey</structfield>
!    asserts that it allows scans
     where no indexable restriction clause is given for the first index column.
!    When <structname>IndexAmRoutine</structname>.<structfield>amcanmulticol</structfield>
!    is false,
!    <structname>IndexAmRoutine</structname>.<structfield>amoptionalkey</structfield>
!    essentially says whether the
     access method supports full-index scans without any restriction clause.
     Access methods that support multiple index columns <emphasis>must</>
     support scans that omit restrictions on any or all of the columns after
     the first; however they are permitted to require some restriction to
     appear for the first index column, and this is signaled by setting
!    <structname>IndexAmRoutine</structname>.<structfield>amoptionalkey</structfield>
!    false.  One reason that an index AM might set
!    <structname>IndexAmRoutine</structname>.<structfield>amoptionalkey</structfield>
!    false is if it doesn't index null values.  Since most indexable operators are
     strict and hence cannot return true for null inputs,
     it is at first sight attractive to not store index entries for null values:
     they could never be returned by an index scan anyway.  However, this
     argument fails when an index scan has no restriction clause for a given
     index column.  In practice this means that
!    indexes that have
!    <structname>IndexAmRoutine</structname>.<structfield>amoptionalkey</structfield>
!    true must index nulls, since the planner might decide to use such an index
     with no scan keys at all.  A related restriction is that an index
     access method that supports multiple index columns <emphasis>must</>
     support indexing null values in columns after the first, because the planner
***************
*** 131,139 ****
     index omits rows where <literal>b</> is null.
     It is, however, OK to omit rows where the first indexed column is null.
     An index access method that does index nulls may also set
!    <structfield>amsearchnulls</structfield>, indicating that it supports
!    <literal>IS NULL</> and <literal>IS NOT NULL</> clauses as search
!    conditions.
    </para>
  
   </sect1>
--- 138,146 ----
     index omits rows where <literal>b</> is null.
     It is, however, OK to omit rows where the first indexed column is null.
     An index access method that does index nulls may also set
!    <structname>IndexAmRoutine</structname>.<structfield>amsearchnulls</structfield>,
!    indicating that it supports <literal>IS NULL</> and <literal>IS NOT NULL</>
!    clauses as search conditions.
    </para>
  
   </sect1>
***************
*** 143,149 ****
  
    <para>
     The index construction and maintenance functions that an index access
!    method must provide are:
    </para>
  
    <para>
--- 150,156 ----
  
    <para>
     The index construction and maintenance functions that an index access
!    method must provide in <structname>IndexAmRoutine</structname> are:
    </para>
  
    <para>
*************** aminsert (Relation indexRelation,
*** 188,194 ****
     <literal>isnull</> arrays give the key values to be indexed, and
     <literal>heap_tid</> is the TID to be indexed.
     If the access method supports unique indexes (its
!    <structname>pg_am</>.<structfield>amcanunique</> flag is true) then
     <literal>checkUnique</> indicates the type of uniqueness check to
     perform.  This varies depending on whether the unique constraint is
     deferrable; see <xref linkend="index-unique-checks"> for details.
--- 195,201 ----
     <literal>isnull</> arrays give the key values to be indexed, and
     <literal>heap_tid</> is the TID to be indexed.
     If the access method supports unique indexes (its
!    <structfield>amcanunique</> flag is true) then
     <literal>checkUnique</> indicates the type of uniqueness check to
     perform.  This varies depending on whether the unique constraint is
     deferrable; see <xref linkend="index-unique-checks"> for details.
*************** amcanreturn (Relation indexRelation, int
*** 281,288 ****
     the form of an <structname>IndexTuple</structname>.  The attribute number
     is 1-based, i.e. the first columns attno is 1. Returns TRUE if supported,
     else FALSE.  If the access method does not support index-only scans at all,
!    the <structfield>amcanreturn</> field in its <structname>pg_am</> row can
!    be set to zero.
    </para>
  
    <para>
--- 288,295 ----
     the form of an <structname>IndexTuple</structname>.  The attribute number
     is 1-based, i.e. the first columns attno is 1. Returns TRUE if supported,
     else FALSE.  If the access method does not support index-only scans at all,
!    the <structfield>amcanreturn</> field in its <structname>IndexAmRoutine</>
!    struct can be set to NULL.
    </para>
  
    <para>
*************** amgettuple (IndexScanDesc scan,
*** 414,421 ****
    <para>
     The <function>amgettuple</> function need only be provided if the access
     method supports <quote>plain</> index scans.  If it doesn't, the
!    <structfield>amgettuple</> field in its <structname>pg_am</> row must
!    be set to zero.
    </para>
  
    <para>
--- 421,428 ----
    <para>
     The <function>amgettuple</> function need only be provided if the access
     method supports <quote>plain</> index scans.  If it doesn't, the
!    <structfield>amgettuple</> field in its <structname>IndexAmRoutine</>
!    struct must be set to NULL.
    </para>
  
    <para>
*************** amgetbitmap (IndexScanDesc scan,
*** 445,452 ****
    <para>
     The <function>amgetbitmap</> function need only be provided if the access
     method supports <quote>bitmap</> index scans.  If it doesn't, the
!    <structfield>amgetbitmap</> field in its <structname>pg_am</> row must
!    be set to zero.
    </para>
  
    <para>
--- 452,459 ----
    <para>
     The <function>amgetbitmap</> function need only be provided if the access
     method supports <quote>bitmap</> index scans.  If it doesn't, the
!    <structfield>amgetbitmap</> field in its <structname>IndexAmRoutine</>
!    struct must be set to NULL.
    </para>
  
    <para>
*************** amrestrpos (IndexScanDesc scan);
*** 477,494 ****
    </para>
  
    <para>
!    By convention, the <literal>pg_proc</literal> entry for an index
!    access method function should show the correct number of arguments,
!    but declare them all as type <type>internal</> (since most of the arguments
!    have types that are not known to SQL, and we don't want users calling
!    the functions directly anyway).  The return type is declared as
!    <type>void</>, <type>internal</>, or <type>boolean</> as appropriate.
!    The only exception is <function>amoptions</>, which should be correctly
!    declared as taking <type>text[]</> and <type>bool</> and returning
!    <type>bytea</>.  This provision allows client code to execute
!    <function>amoptions</> to test validity of options settings.
    </para>
- 
   </sect1>
  
   <sect1 id="index-scanning">
--- 484,496 ----
    </para>
  
    <para>
! <programlisting>
! void
! amvalidate (OpClassInfo *opclass);
! </programlisting>
!    Validates given operator class by access method. The <function>amvalidate</>
!    must throw an error if opclass is invalid.
    </para>
   </sect1>
  
   <sect1 id="index-scanning">
*************** amrestrpos (IndexScanDesc scan);
*** 548,554 ****
        <para>
         Access methods that always return entries in the natural ordering
         of their data (such as btree) should set
!        <structname>pg_am</>.<structfield>amcanorder</> to true.
         Currently, such access methods must use btree-compatible strategy
         numbers for their equality and ordering operators.
        </para>
--- 550,556 ----
        <para>
         Access methods that always return entries in the natural ordering
         of their data (such as btree) should set
!        <structfield>amcanorder</> to true.
         Currently, such access methods must use btree-compatible strategy
         numbers for their equality and ordering operators.
        </para>
*************** amrestrpos (IndexScanDesc scan);
*** 556,562 ****
       <listitem>
        <para>
         Access methods that support ordering operators should set
!        <structname>pg_am</>.<structfield>amcanorderbyop</> to true.
         This indicates that the index is capable of returning entries in
         an order satisfying <literal>ORDER BY</> <replaceable>index_key</>
         <replaceable>operator</> <replaceable>constant</>.  Scan modifiers
--- 558,564 ----
       <listitem>
        <para>
         Access methods that support ordering operators should set
!        <structfield>amcanorderbyop</> to true.
         This indicates that the index is capable of returning entries in
         an order satisfying <literal>ORDER BY</> <replaceable>index_key</>
         <replaceable>operator</> <replaceable>constant</>.  Scan modifiers
*************** amrestrpos (IndexScanDesc scan);
*** 579,585 ****
     methods that set <structfield>amcanorder</> to true.)  After the
     first call, <function>amgettuple</> must be prepared to advance the scan in
     either direction from the most recently returned entry.  (But if
!    <structname>pg_am</>.<structfield>amcanbackward</> is false, all subsequent
     calls will have the same direction as the first one.)
    </para>
  
--- 581,587 ----
     methods that set <structfield>amcanorder</> to true.)  After the
     first call, <function>amgettuple</> must be prepared to advance the scan in
     either direction from the most recently returned entry.  (But if
!    <structfield>amcanbackward</> is false, all subsequent
     calls will have the same direction as the first one.)
    </para>
  
*************** amrestrpos (IndexScanDesc scan);
*** 590,597 ****
     be remembered per scan; a new <function>ammarkpos</> call overrides the
     previously marked position.  An access method that does not support
     ordered scans should still provide mark and restore functions in
!    <structname>pg_am</>, but it is sufficient to have them throw errors if
!    called.
    </para>
  
    <para>
--- 592,599 ----
     be remembered per scan; a new <function>ammarkpos</> call overrides the
     previously marked position.  An access method that does not support
     ordered scans should still provide mark and restore functions in
!    <structname>IndexAmRoutine</>, but it is sufficient to have them throw
!    errors if called.
    </para>
  
    <para>
*************** amrestrpos (IndexScanDesc scan);
*** 766,772 ****
     <productname>PostgreSQL</productname> enforces SQL uniqueness constraints
     using <firstterm>unique indexes</>, which are indexes that disallow
     multiple entries with identical keys.  An access method that supports this
!    feature sets <structname>pg_am</>.<structfield>amcanunique</> true.
     (At present, only b-tree supports it.)
    </para>
  
--- 768,774 ----
     <productname>PostgreSQL</productname> enforces SQL uniqueness constraints
     using <firstterm>unique indexes</>, which are indexes that disallow
     multiple entries with identical keys.  An access method that supports this
!    feature sets <structfield>amcanunique</> true.
     (At present, only b-tree supports it.)
    </para>
  
diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
new file mode 100644
index 4fac018..a2d3fd8
*** a/doc/src/sgml/xindex.sgml
--- b/doc/src/sgml/xindex.sgml
***************
*** 34,40 ****
     regular access to tables is built into
     <productname>PostgreSQL</productname>, but all index methods are
     described in <classname>pg_am</classname>.  It is possible to add a
!    new index method by defining the required interface routines and
     then creating a row in <classname>pg_am</classname> &mdash; but that is
     beyond the scope of this chapter (see <xref linkend="indexam">).
    </para>
--- 34,41 ----
     regular access to tables is built into
     <productname>PostgreSQL</productname>, but all index methods are
     described in <classname>pg_am</classname>.  It is possible to add a
!    new index access method by defining the handler function, which returns
!    C-struct with required interface routines and parameters, and
     then creating a row in <classname>pg_am</classname> &mdash; but that is
     beyond the scope of this chapter (see <xref linkend="indexam">).
    </para>
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
new file mode 100644
index 99337b0..a029eb1
*** a/src/backend/access/brin/brin.c
--- b/src/backend/access/brin/brin.c
***************
*** 35,40 ****
--- 35,143 ----
  
  
  /*
+  * BRIN handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ brinhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 0;
+ 	amroutine->amsupport = 15;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = true;
+ 	amroutine->amstorage = true;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = brininsert;
+ 	amroutine->ambeginscan = brinbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = bringetbitmap;
+ 	amroutine->amrescan = brinrescan;
+ 	amroutine->amendscan = brinendscan;
+ 	amroutine->ammarkpos = brinmarkpos;
+ 	amroutine->amrestrpos = brinrestrpos;
+ 	amroutine->ambuild = brinbuild;
+ 	amroutine->ambuildempty = brinbuildempty;
+ 	amroutine->ambulkdelete = brinbulkdelete;
+ 	amroutine->amvacuumcleanup = brinvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = brincostestimate;
+ 	amroutine->amoptions = brinoptions;
+ 	amroutine->amvalidate = brinvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void
+ brinvalidate(OpClassInfo *opclass)
+ {
+ 	ListCell   *l;
+ 	bool		procsPresent[BRIN_MANDATORY_NPROCS];
+ 	int			i;
+ 
+ 	memset(procsPresent, 0, sizeof(procsPresent));
+ 
+ 	foreach(l, opclass->procedures)
+ 	{
+ 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (proc->lefttype != opclass->intype ||
+ 			proc->righttype != opclass->intype)
+ 			continue;
+ 
+ 		if (proc->number < 1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("invalid support number for BRIN: %u",
+ 							proc->number)));
+ 
+ 		/* BRIN opclasses could contain any number of non-mandatory procedures */
+ 		if (proc->number > BRIN_MANDATORY_NPROCS)
+ 			continue;
+ 
+ 		if (procsPresent[proc->number - 1])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("BRIN support number %u was defined more than once",
+ 							proc->number)));
+ 
+ 		procsPresent[proc->number - 1] = true;
+ 	}
+ 
+ 	/*
+ 	 * Don't use BRIN_NPROC because some procnumbers
+ 	 * are reserved for future use
+ 	 */
+ 	for (i = 0; i < BRIN_MANDATORY_NPROCS; i++)
+ 	{
+ 		if (!procsPresent[i])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("BRIN support number %u is required", i + 1)));
+ 	}
+ 
+ 	foreach(l, opclass->operators)
+ 	{
+ 		OpFamilyMember *opr = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (OidIsValid(opr->sortfamily))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("BRIN doesn't support ORDER BY operators")));
+ 	}
+ }
+ 
+ /*
   * We use a BrinBuildState during initial construction of a BRIN index.
   * The running state is kept in a BrinMemTuple.
   */
*************** static void brin_vacuum_scan(Relation id
*** 80,94 ****
   * If the range is not currently summarized (i.e. the revmap returns NULL for
   * it), there's nothing to do.
   */
! Datum
! brininsert(PG_FUNCTION_ARGS)
  {
- 	Relation	idxRel = (Relation) PG_GETARG_POINTER(0);
- 	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
- 	bool	   *nulls = (bool *) PG_GETARG_POINTER(2);
- 	ItemPointer heaptid = (ItemPointer) PG_GETARG_POINTER(3);
- 
- 	/* we ignore the rest of our arguments */
  	BlockNumber pagesPerRange;
  	BrinDesc   *bdesc = NULL;
  	BrinRevmap *revmap;
--- 183,193 ----
   * If the range is not currently summarized (i.e. the revmap returns NULL for
   * it), there's nothing to do.
   */
! bool
! brininsert(Relation idxRel, Datum *values, bool *nulls,
! 		   ItemPointer heaptid, Relation heapRel,
! 		   IndexUniqueCheck checkUnique)
  {
  	BlockNumber pagesPerRange;
  	BrinDesc   *bdesc = NULL;
  	BrinRevmap *revmap;
*************** brininsert(PG_FUNCTION_ARGS)
*** 226,232 ****
  		MemoryContextDelete(tupcxt);
  	}
  
! 	return BoolGetDatum(false);
  }
  
  /*
--- 325,331 ----
  		MemoryContextDelete(tupcxt);
  	}
  
! 	return false;
  }
  
  /*
*************** brininsert(PG_FUNCTION_ARGS)
*** 236,247 ****
   * index was built with.  Note that since this cannot be changed while we're
   * holding lock on index, it's not necessary to recompute it during brinrescan.
   */
! Datum
! brinbeginscan(PG_FUNCTION_ARGS)
  {
- 	Relation	r = (Relation) PG_GETARG_POINTER(0);
- 	int			nkeys = PG_GETARG_INT32(1);
- 	int			norderbys = PG_GETARG_INT32(2);
  	IndexScanDesc scan;
  	BrinOpaque *opaque;
  
--- 335,343 ----
   * index was built with.  Note that since this cannot be changed while we're
   * holding lock on index, it's not necessary to recompute it during brinrescan.
   */
! IndexScanDesc
! brinbeginscan(Relation r, int nkeys, int norderbys)
  {
  	IndexScanDesc scan;
  	BrinOpaque *opaque;
  
*************** brinbeginscan(PG_FUNCTION_ARGS)
*** 252,258 ****
  	opaque->bo_bdesc = brin_build_desc(r);
  	scan->opaque = opaque;
  
! 	PG_RETURN_POINTER(scan);
  }
  
  /*
--- 348,354 ----
  	opaque->bo_bdesc = brin_build_desc(r);
  	scan->opaque = opaque;
  
! 	return scan;
  }
  
  /*
*************** brinbeginscan(PG_FUNCTION_ARGS)
*** 267,277 ****
   * unsummarized.  Pages in those ranges need to be returned regardless of scan
   * keys.
   */
! Datum
! bringetbitmap(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
  	Relation	idxRel = scan->indexRelation;
  	Buffer		buf = InvalidBuffer;
  	BrinDesc   *bdesc;
--- 363,371 ----
   * unsummarized.  Pages in those ranges need to be returned regardless of scan
   * keys.
   */
! int64
! bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  {
  	Relation	idxRel = scan->indexRelation;
  	Buffer		buf = InvalidBuffer;
  	BrinDesc   *bdesc;
*************** bringetbitmap(PG_FUNCTION_ARGS)
*** 451,470 ****
  	 * returns, but we don't have a precise idea of the number of heap tuples
  	 * involved.
  	 */
! 	PG_RETURN_INT64(totalpages * 10);
  }
  
  /*
   * Re-initialize state for a BRIN index scan
   */
! Datum
! brinrescan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(1);
- 
- 	/* other arguments ignored */
- 
  	/*
  	 * Other index AMs preprocess the scan keys at this point, or sometime
  	 * early during the scan; this lets them optimize by removing redundant
--- 545,560 ----
  	 * returns, but we don't have a precise idea of the number of heap tuples
  	 * involved.
  	 */
! 	return totalpages * 10;
  }
  
  /*
   * Re-initialize state for a BRIN index scan
   */
! void
! brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 		   ScanKey orderbys, int norderbys)
  {
  	/*
  	 * Other index AMs preprocess the scan keys at this point, or sometime
  	 * early during the scan; this lets them optimize by removing redundant
*************** brinrescan(PG_FUNCTION_ARGS)
*** 476,513 ****
  	if (scankey && scan->numberOfKeys > 0)
  		memmove(scan->keyData, scankey,
  				scan->numberOfKeys * sizeof(ScanKeyData));
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   * Close down a BRIN index scan
   */
! Datum
! brinendscan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	BrinOpaque *opaque = (BrinOpaque *) scan->opaque;
  
  	brinRevmapTerminate(opaque->bo_rmAccess);
  	brin_free_desc(opaque->bo_bdesc);
  	pfree(opaque);
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! brinmarkpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "BRIN does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
! Datum
! brinrestrpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "BRIN does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
  /*
--- 566,596 ----
  	if (scankey && scan->numberOfKeys > 0)
  		memmove(scan->keyData, scankey,
  				scan->numberOfKeys * sizeof(ScanKeyData));
  }
  
  /*
   * Close down a BRIN index scan
   */
! void
! brinendscan(IndexScanDesc scan)
  {
  	BrinOpaque *opaque = (BrinOpaque *) scan->opaque;
  
  	brinRevmapTerminate(opaque->bo_rmAccess);
  	brin_free_desc(opaque->bo_bdesc);
  	pfree(opaque);
  }
  
! void
! brinmarkpos(IndexScanDesc scan)
  {
  	elog(ERROR, "BRIN does not support mark/restore");
  }
  
! void
! brinrestrpos(IndexScanDesc scan)
  {
  	elog(ERROR, "BRIN does not support mark/restore");
  }
  
  /*
*************** brinbuildCallback(Relation index,
*** 579,590 ****
  /*
   * brinbuild() -- build a new BRIN index.
   */
! Datum
! brinbuild(PG_FUNCTION_ARGS)
  {
- 	Relation	heap = (Relation) PG_GETARG_POINTER(0);
- 	Relation	index = (Relation) PG_GETARG_POINTER(1);
- 	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
  	IndexBuildResult *result;
  	double		reltuples;
  	double		idxtuples;
--- 662,670 ----
  /*
   * brinbuild() -- build a new BRIN index.
   */
! IndexBuildResult *
! brinbuild(Relation heap, Relation index, IndexInfo *indexInfo)
  {
  	IndexBuildResult *result;
  	double		reltuples;
  	double		idxtuples;
*************** brinbuild(PG_FUNCTION_ARGS)
*** 663,675 ****
  	result->heap_tuples = reltuples;
  	result->index_tuples = idxtuples;
  
! 	PG_RETURN_POINTER(result);
  }
  
! Datum
! brinbuildempty(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
  	Buffer		metabuf;
  
  	/* An empty BRIN index has a metapage only. */
--- 743,754 ----
  	result->heap_tuples = reltuples;
  	result->index_tuples = idxtuples;
  
! 	return result;
  }
  
! void
! brinbuildempty(Relation index)
  {
  	Buffer		metabuf;
  
  	/* An empty BRIN index has a metapage only. */
*************** brinbuildempty(PG_FUNCTION_ARGS)
*** 686,693 ****
  	END_CRIT_SECTION();
  
  	UnlockReleaseBuffer(metabuf);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
--- 765,770 ----
*************** brinbuildempty(PG_FUNCTION_ARGS)
*** 699,733 ****
   * tuple is deleted), meaning the need to re-run summarization on the affected
   * range.  Would need to add an extra flag in brintuples for that.
   */
! Datum
! brinbulkdelete(PG_FUNCTION_ARGS)
  {
- 	/* other arguments are not currently used */
- 	IndexBulkDeleteResult *stats =
- 	(IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 
  	/* allocate stats if first time through, else re-use existing struct */
  	if (stats == NULL)
  		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
  
! 	PG_RETURN_POINTER(stats);
  }
  
  /*
   * This routine is in charge of "vacuuming" a BRIN index: we just summarize
   * ranges that are currently unsummarized.
   */
! Datum
! brinvacuumcleanup(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats =
- 	(IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
  	Relation	heapRel;
  
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		PG_RETURN_POINTER(stats);
  
  	if (!stats)
  		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
--- 776,804 ----
   * tuple is deleted), meaning the need to re-run summarization on the affected
   * range.  Would need to add an extra flag in brintuples for that.
   */
! IndexBulkDeleteResult *
! brinbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
! 			   IndexBulkDeleteCallback callback, void *callback_state)
  {
  	/* allocate stats if first time through, else re-use existing struct */
  	if (stats == NULL)
  		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
  
! 	return stats;
  }
  
  /*
   * This routine is in charge of "vacuuming" a BRIN index: we just summarize
   * ranges that are currently unsummarized.
   */
! IndexBulkDeleteResult *
! brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
  {
  	Relation	heapRel;
  
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		return stats;
  
  	if (!stats)
  		stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
*************** brinvacuumcleanup(PG_FUNCTION_ARGS)
*** 744,760 ****
  
  	heap_close(heapRel, AccessShareLock);
  
! 	PG_RETURN_POINTER(stats);
  }
  
  /*
   * reloptions processor for BRIN indexes
   */
! Datum
! brinoptions(PG_FUNCTION_ARGS)
  {
- 	Datum		reloptions = PG_GETARG_DATUM(0);
- 	bool		validate = PG_GETARG_BOOL(1);
  	relopt_value *options;
  	BrinOptions *rdopts;
  	int			numoptions;
--- 815,829 ----
  
  	heap_close(heapRel, AccessShareLock);
  
! 	return stats;
  }
  
  /*
   * reloptions processor for BRIN indexes
   */
! bytea *
! brinoptions(Datum reloptions, bool validate)
  {
  	relopt_value *options;
  	BrinOptions *rdopts;
  	int			numoptions;
*************** brinoptions(PG_FUNCTION_ARGS)
*** 767,773 ****
  
  	/* if none set, we're done */
  	if (numoptions == 0)
! 		PG_RETURN_NULL();
  
  	rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
  
--- 836,842 ----
  
  	/* if none set, we're done */
  	if (numoptions == 0)
! 		return NULL;
  
  	rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
  
*************** brinoptions(PG_FUNCTION_ARGS)
*** 776,782 ****
  
  	pfree(options);
  
! 	PG_RETURN_BYTEA_P(rdopts);
  }
  
  /*
--- 845,851 ----
  
  	pfree(options);
  
! 	return (bytea *)rdopts;
  }
  
  /*
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
new file mode 100644
index 931dceb..b52846f
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
*************** untransformRelOptions(Datum options)
*** 891,897 ****
   * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
   */
  bytea *
! extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
  {
  	bytea	   *options;
  	bool		isnull;
--- 891,897 ----
   * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
   */
  bytea *
! extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, amoptions_function amoptions)
  {
  	bytea	   *options;
  	bool		isnull;
*************** heap_reloptions(char relkind, Datum relo
*** 1379,1412 ****
   *	validate	error flag
   */
  bytea *
! index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
  {
! 	FmgrInfo	flinfo;
! 	FunctionCallInfoData fcinfo;
! 	Datum		result;
! 
! 	Assert(RegProcedureIsValid(amoptions));
  
  	/* Assume function is strict */
  	if (!PointerIsValid(DatumGetPointer(reloptions)))
  		return NULL;
  
! 	/* Can't use OidFunctionCallN because we might get a NULL result */
! 	fmgr_info(amoptions, &flinfo);
! 
! 	InitFunctionCallInfoData(fcinfo, &flinfo, 2, InvalidOid, NULL, NULL);
! 
! 	fcinfo.arg[0] = reloptions;
! 	fcinfo.arg[1] = BoolGetDatum(validate);
! 	fcinfo.argnull[0] = false;
! 	fcinfo.argnull[1] = false;
! 
! 	result = FunctionCallInvoke(&fcinfo);
! 
! 	if (fcinfo.isnull || DatumGetPointer(result) == NULL)
! 		return NULL;
! 
! 	return DatumGetByteaP(result);
  }
  
  /*
--- 1379,1393 ----
   *	validate	error flag
   */
  bytea *
! index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
  {
! 	Assert(amoptions);
  
  	/* Assume function is strict */
  	if (!PointerIsValid(DatumGetPointer(reloptions)))
  		return NULL;
  
! 	return amoptions(reloptions, validate);
  }
  
  /*
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
new file mode 100644
index 54b2db8..29b22d6
*** a/src/backend/access/gin/ginget.c
--- b/src/backend/access/gin/ginget.c
*************** scanPendingInsert(IndexScanDesc scan, TI
*** 1772,1782 ****
  
  #define GinIsVoidRes(s)		( ((GinScanOpaque) scan->opaque)->isVoidRes )
  
! Datum
! gingetbitmap(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
  	GinScanOpaque so = (GinScanOpaque) scan->opaque;
  	int64		ntids;
  	ItemPointerData iptr;
--- 1772,1780 ----
  
  #define GinIsVoidRes(s)		( ((GinScanOpaque) scan->opaque)->isVoidRes )
  
! int64
! gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  {
  	GinScanOpaque so = (GinScanOpaque) scan->opaque;
  	int64		ntids;
  	ItemPointerData iptr;
*************** gingetbitmap(PG_FUNCTION_ARGS)
*** 1827,1831 ****
  		ntids++;
  	}
  
! 	PG_RETURN_INT64(ntids);
  }
--- 1825,1829 ----
  		ntids++;
  	}
  
! 	return ntids;
  }
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
new file mode 100644
index 49e9185..92701b9
*** a/src/backend/access/gin/gininsert.c
--- b/src/backend/access/gin/gininsert.c
*************** ginBuildCallback(Relation index, HeapTup
*** 306,317 ****
  	MemoryContextSwitchTo(oldCtx);
  }
  
! Datum
! ginbuild(PG_FUNCTION_ARGS)
  {
- 	Relation	heap = (Relation) PG_GETARG_POINTER(0);
- 	Relation	index = (Relation) PG_GETARG_POINTER(1);
- 	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
  	IndexBuildResult *result;
  	double		reltuples;
  	GinBuildState buildstate;
--- 306,314 ----
  	MemoryContextSwitchTo(oldCtx);
  }
  
! IndexBuildResult *
! ginbuild(Relation heap, Relation index, IndexInfo *indexInfo)
  {
  	IndexBuildResult *result;
  	double		reltuples;
  	GinBuildState buildstate;
*************** ginbuild(PG_FUNCTION_ARGS)
*** 429,444 ****
  	result->heap_tuples = reltuples;
  	result->index_tuples = buildstate.indtuples;
  
! 	PG_RETURN_POINTER(result);
  }
  
  /*
   *	ginbuildempty() -- build an empty gin index in the initialization fork
   */
! Datum
! ginbuildempty(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
  	Buffer		RootBuffer,
  				MetaBuffer;
  
--- 426,440 ----
  	result->heap_tuples = reltuples;
  	result->index_tuples = buildstate.indtuples;
  
! 	return result;
  }
  
  /*
   *	ginbuildempty() -- build an empty gin index in the initialization fork
   */
! void
! ginbuildempty(Relation index)
  {
  	Buffer		RootBuffer,
  				MetaBuffer;
  
*************** ginbuildempty(PG_FUNCTION_ARGS)
*** 463,470 ****
  	/* Unlock and release the buffers. */
  	UnlockReleaseBuffer(MetaBuffer);
  	UnlockReleaseBuffer(RootBuffer);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
--- 459,464 ----
*************** ginHeapTupleInsert(GinState *ginstate, O
*** 489,506 ****
  					   item, 1, NULL);
  }
  
! Datum
! gininsert(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
- 	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
- 	bool	   *isnull = (bool *) PG_GETARG_POINTER(2);
- 	ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
- 
- #ifdef NOT_USED
- 	Relation	heapRel = (Relation) PG_GETARG_POINTER(4);
- 	IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
- #endif
  	GinState	ginstate;
  	MemoryContext oldCtx;
  	MemoryContext insertCtx;
--- 483,493 ----
  					   item, 1, NULL);
  }
  
! bool
! gininsert(Relation index, Datum *values, bool *isnull,
! 		  ItemPointer ht_ctid, Relation heapRel,
! 		  IndexUniqueCheck checkUnique)
  {
  	GinState	ginstate;
  	MemoryContext oldCtx;
  	MemoryContext insertCtx;
*************** gininsert(PG_FUNCTION_ARGS)
*** 541,545 ****
  	MemoryContextSwitchTo(oldCtx);
  	MemoryContextDelete(insertCtx);
  
! 	PG_RETURN_BOOL(false);
  }
--- 528,532 ----
  	MemoryContextSwitchTo(oldCtx);
  	MemoryContextDelete(insertCtx);
  
! 	return false;
  }
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
new file mode 100644
index ac3a92b..d97a9db
*** a/src/backend/access/gin/ginscan.c
--- b/src/backend/access/gin/ginscan.c
***************
*** 21,32 ****
  #include "utils/rel.h"
  
  
! Datum
! ginbeginscan(PG_FUNCTION_ARGS)
  {
- 	Relation	rel = (Relation) PG_GETARG_POINTER(0);
- 	int			nkeys = PG_GETARG_INT32(1);
- 	int			norderbys = PG_GETARG_INT32(2);
  	IndexScanDesc scan;
  	GinScanOpaque so;
  
--- 21,29 ----
  #include "utils/rel.h"
  
  
! IndexScanDesc
! ginbeginscan(Relation rel, int nkeys, int norderbys)
  {
  	IndexScanDesc scan;
  	GinScanOpaque so;
  
*************** ginbeginscan(PG_FUNCTION_ARGS)
*** 53,59 ****
  
  	scan->opaque = so;
  
! 	PG_RETURN_POINTER(scan);
  }
  
  /*
--- 50,56 ----
  
  	scan->opaque = so;
  
! 	return scan;
  }
  
  /*
*************** ginNewScanKey(IndexScanDesc scan)
*** 417,429 ****
  	pgstat_count_index_scan(scan->indexRelation);
  }
  
! Datum
! ginrescan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(1);
- 
- 	/* remaining arguments are ignored */
  	GinScanOpaque so = (GinScanOpaque) scan->opaque;
  
  	ginFreeScanKeys(so);
--- 414,423 ----
  	pgstat_count_index_scan(scan->indexRelation);
  }
  
! void
! ginrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 		  ScanKey orderbys, int norderbys)
  {
  	GinScanOpaque so = (GinScanOpaque) scan->opaque;
  
  	ginFreeScanKeys(so);
*************** ginrescan(PG_FUNCTION_ARGS)
*** 433,447 ****
  		memmove(scan->keyData, scankey,
  				scan->numberOfKeys * sizeof(ScanKeyData));
  	}
- 
- 	PG_RETURN_VOID();
  }
  
  
! Datum
! ginendscan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	GinScanOpaque so = (GinScanOpaque) scan->opaque;
  
  	ginFreeScanKeys(so);
--- 427,438 ----
  		memmove(scan->keyData, scankey,
  				scan->numberOfKeys * sizeof(ScanKeyData));
  	}
  }
  
  
! void
! ginendscan(IndexScanDesc scan)
  {
  	GinScanOpaque so = (GinScanOpaque) scan->opaque;
  
  	ginFreeScanKeys(so);
*************** ginendscan(PG_FUNCTION_ARGS)
*** 450,469 ****
  	MemoryContextDelete(so->keyCtx);
  
  	pfree(so);
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! ginmarkpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "GIN does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
! Datum
! ginrestrpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "GIN does not support mark/restore");
- 	PG_RETURN_VOID();
  }
--- 441,456 ----
  	MemoryContextDelete(so->keyCtx);
  
  	pfree(so);
  }
  
! void
! ginmarkpos(IndexScanDesc scan)
  {
  	elog(ERROR, "GIN does not support mark/restore");
  }
  
! void
! ginrestrpos(IndexScanDesc scan)
  {
  	elog(ERROR, "GIN does not support mark/restore");
  }
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
new file mode 100644
index cb4e32f..51c1559
*** a/src/backend/access/gin/ginutil.c
--- b/src/backend/access/gin/ginutil.c
***************
*** 22,28 ****
--- 22,140 ----
  #include "miscadmin.h"
  #include "storage/indexfsm.h"
  #include "storage/lmgr.h"
+ #include "utils/selfuncs.h"
+ 
+ 
+ /*
+  * GIN handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ ginhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 0;
+ 	amroutine->amsupport = 6;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = true;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = gininsert;
+ 	amroutine->ambeginscan = ginbeginscan;
+ 	amroutine->amgettuple = NULL;
+ 	amroutine->amgetbitmap = gingetbitmap;
+ 	amroutine->amrescan = ginrescan;
+ 	amroutine->amendscan = ginendscan;
+ 	amroutine->ammarkpos = ginmarkpos;
+ 	amroutine->amrestrpos = ginrestrpos;
+ 	amroutine->ambuild = ginbuild;
+ 	amroutine->ambuildempty = ginbuildempty;
+ 	amroutine->ambulkdelete = ginbulkdelete;
+ 	amroutine->amvacuumcleanup = ginvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = gincostestimate;
+ 	amroutine->amoptions = ginoptions;
+ 	amroutine->amvalidate = ginvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void
+ ginvalidate(OpClassInfo *opclass)
+ {
+ 	ListCell   *l;
+ 	bool		procsPresent[GINNProcs];
+ 	int			i;
+ 
+ 	memset(procsPresent, 0, sizeof(procsPresent));
+ 
+ 	foreach(l, opclass->procedures)
+ 	{
+ 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (proc->lefttype != opclass->intype ||
+ 			proc->righttype != opclass->intype)
+ 			continue;
+ 
+ 		if (proc->number < 1 || proc->number > GINNProcs)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("invalid support number for GIN: %u",
+ 							proc->number)));
  
+ 		if (procsPresent[proc->number - 1])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("GIN support number %u was defined more than once",
+ 							proc->number)));
+ 
+ 		procsPresent[proc->number - 1] = true;
+ 	}
+ 
+ 	for (i = 0; i < GINNProcs; i++)
+ 	{
+ 		if (i+1 == GIN_COMPARE_PARTIAL_PROC)
+ 			continue; /* optional method */
+ 		if (i+1 == GIN_CONSISTENT_PROC || i+1 == GIN_TRICONSISTENT_PROC)
+ 			continue; /* don't need to have both, see check below loop */
+ 
+ 		if (!procsPresent[i])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("GIN support number %u is required", i + 1)));
+ 	}
+ 
+ 	if (!procsPresent[GIN_CONSISTENT_PROC - 1]
+ 								&& !procsPresent[GIN_TRICONSISTENT_PROC - 1])
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 				 errmsg("at least one of consistent support (number %u) "
+ 						"and triconsistent support (number %u) is "
+ 						"is required",
+ 						GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC)));
+ 	}
+ 
+ 	foreach(l, opclass->operators)
+ 	{
+ 		OpFamilyMember *opr = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (OidIsValid(opr->sortfamily))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("GIN doesn't support ORDER BY operators")));
+ 	}
+ }
  
  /*
   * initGinState: fill in an empty GinState struct to describe the index
*************** ginExtractEntries(GinState *ginstate, Of
*** 516,526 ****
  	return entries;
  }
  
! Datum
! ginoptions(PG_FUNCTION_ARGS)
  {
- 	Datum		reloptions = PG_GETARG_DATUM(0);
- 	bool		validate = PG_GETARG_BOOL(1);
  	relopt_value *options;
  	GinOptions *rdopts;
  	int			numoptions;
--- 628,636 ----
  	return entries;
  }
  
! bytea *
! ginoptions(Datum reloptions, bool validate)
  {
  	relopt_value *options;
  	GinOptions *rdopts;
  	int			numoptions;
*************** ginoptions(PG_FUNCTION_ARGS)
*** 535,541 ****
  
  	/* if none set, we're done */
  	if (numoptions == 0)
! 		PG_RETURN_NULL();
  
  	rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
  
--- 645,651 ----
  
  	/* if none set, we're done */
  	if (numoptions == 0)
! 		return NULL;
  
  	rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
  
*************** ginoptions(PG_FUNCTION_ARGS)
*** 544,550 ****
  
  	pfree(options);
  
! 	PG_RETURN_BYTEA_P(rdopts);
  }
  
  /*
--- 654,660 ----
  
  	pfree(options);
  
! 	return (bytea *)rdopts;
  }
  
  /*
diff --git a/src/backend/access/gin/ginvacuum.c b/src/backend/access/gin/ginvacuum.c
new file mode 100644
index 323cfb0..5b38607
*** a/src/backend/access/gin/ginvacuum.c
--- b/src/backend/access/gin/ginvacuum.c
*************** ginVacuumEntryPage(GinVacuumState *gvs, 
*** 513,525 ****
  	return (tmppage == origpage) ? NULL : tmppage;
  }
  
! Datum
! ginbulkdelete(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 	IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- 	void	   *callback_state = (void *) PG_GETARG_POINTER(3);
  	Relation	index = info->index;
  	BlockNumber blkno = GIN_ROOT_BLKNO;
  	GinVacuumState gvs;
--- 513,522 ----
  	return (tmppage == origpage) ? NULL : tmppage;
  }
  
! IndexBulkDeleteResult *
! ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
! 			  IndexBulkDeleteCallback callback, void *callback_state)
  {
  	Relation	index = info->index;
  	BlockNumber blkno = GIN_ROOT_BLKNO;
  	GinVacuumState gvs;
*************** ginbulkdelete(PG_FUNCTION_ARGS)
*** 634,647 ****
  
  	MemoryContextDelete(gvs.tmpCxt);
  
! 	PG_RETURN_POINTER(gvs.result);
  }
  
! Datum
! ginvacuumcleanup(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
  	Relation	index = info->index;
  	bool		needLock;
  	BlockNumber npages,
--- 631,642 ----
  
  	MemoryContextDelete(gvs.tmpCxt);
  
! 	return gvs.result;
  }
  
! IndexBulkDeleteResult *
! ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
  {
  	Relation	index = info->index;
  	bool		needLock;
  	BlockNumber npages,
*************** ginvacuumcleanup(PG_FUNCTION_ARGS)
*** 661,667 ****
  			initGinState(&ginstate, index);
  			ginInsertCleanup(&ginstate, true, true, stats);
  		}
! 		PG_RETURN_POINTER(stats);
  	}
  
  	/*
--- 656,662 ----
  			initGinState(&ginstate, index);
  			ginInsertCleanup(&ginstate, true, true, stats);
  		}
! 		return stats;
  	}
  
  	/*
*************** ginvacuumcleanup(PG_FUNCTION_ARGS)
*** 746,750 ****
  	if (needLock)
  		UnlockRelationForExtension(index, ExclusiveLock);
  
! 	PG_RETURN_POINTER(stats);
  }
--- 741,745 ----
  	if (needLock)
  		UnlockRelationForExtension(index, ExclusiveLock);
  
! 	return stats;
  }
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
new file mode 100644
index 53bccf6..12224b6
*** a/src/backend/access/gist/gist.c
--- b/src/backend/access/gist/gist.c
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "access/genam.h"
  #include "access/gist_private.h"
+ #include "access/gistscan.h"
  #include "access/xloginsert.h"
  #include "catalog/index.h"
  #include "catalog/pg_collation.h"
***************
*** 24,29 ****
--- 25,31 ----
  #include "storage/indexfsm.h"
  #include "utils/memutils.h"
  #include "utils/rel.h"
+ #include "utils/selfuncs.h"
  
  /* non-export function prototypes */
  static void gistfixsplit(GISTInsertState *state, GISTSTATE *giststate);
*************** static void gistfinishsplit(GISTInsertSt
*** 38,43 ****
--- 40,143 ----
  				GISTSTATE *giststate, List *splitinfo, bool releasebuf);
  static void gistvacuumpage(Relation rel, Page page, Buffer buffer);
  
+ /*
+  * GiST handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ gisthandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 0;
+ 	amroutine->amsupport = 9;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = true;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = true;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = true;
+ 	amroutine->amstorage = true;
+ 	amroutine->amclusterable = true;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = gistinsert;
+ 	amroutine->ambeginscan = gistbeginscan;
+ 	amroutine->amgettuple = gistgettuple;
+ 	amroutine->amgetbitmap = gistgetbitmap;
+ 	amroutine->amrescan = gistrescan;
+ 	amroutine->amendscan = gistendscan;
+ 	amroutine->ammarkpos = gistmarkpos;
+ 	amroutine->amrestrpos = gistrestrpos;
+ 	amroutine->ambuild = gistbuild;
+ 	amroutine->ambuildempty = gistbuildempty;
+ 	amroutine->ambulkdelete = gistbulkdelete;
+ 	amroutine->amvacuumcleanup = gistvacuumcleanup;
+ 	amroutine->amcanreturn = gistcanreturn;
+ 	amroutine->amcostestimate = gistcostestimate;
+ 	amroutine->amoptions = gistoptions;
+ 	amroutine->amvalidate = gistvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void
+ gistvalidate(OpClassInfo *opclass)
+ {
+ 	ListCell   *l;
+ 	bool		procsPresent[GISTNProcs];
+ 	int			i;
+ 
+ 	memset(procsPresent, 0, sizeof(procsPresent));
+ 
+ 	foreach(l, opclass->procedures)
+ 	{
+ 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (proc->lefttype != opclass->intype ||
+ 			proc->righttype != opclass->intype)
+ 			continue;
+ 
+ 		if (proc->number < 1 || proc->number > GISTNProcs)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("invalid support number for GiST: %u",
+ 							proc->number)));
+ 
+ 		if (procsPresent[proc->number - 1])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("GiST support number %u was defined more than once",
+ 							proc->number)));
+ 
+ 		procsPresent[proc->number - 1] = true;
+ 	}
+ 
+ 	for (i = 0; i < GISTNProcs; i++)
+ 	{
+ 		if (i+1 == GIST_DISTANCE_PROC || i+1 == GIST_FETCH_PROC)
+ 			continue; /* optional methods */
+ 		if (!procsPresent[i])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("GiST support number %u is required", i + 1)));
+ 	}
+ 
+ 	foreach(l, opclass->operators)
+ 	{
+ 		OpFamilyMember *opr = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (OidIsValid(opr->sortfamily)
+ 									&& !procsPresent[GIST_DISTANCE_PROC - 1])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("GiST distance support function %u is required "
+ 							"for ORDER BY operators", GIST_DISTANCE_PROC)));
+ 	}
+ }
  
  #define ROTATEDIST(d) do { \
  	SplitedPageLayout *tmp=(SplitedPageLayout*)palloc(sizeof(SplitedPageLayout)); \
*************** createTempGistContext(void)
*** 70,79 ****
  /*
   *	gistbuildempty() -- build an empty gist index in the initialization fork
   */
! Datum
! gistbuildempty(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
  	Buffer		buffer;
  
  	/* Initialize the root page */
--- 170,178 ----
  /*
   *	gistbuildempty() -- build an empty gist index in the initialization fork
   */
! void
! gistbuildempty(Relation index)
  {
  	Buffer		buffer;
  
  	/* Initialize the root page */
*************** gistbuildempty(PG_FUNCTION_ARGS)
*** 89,96 ****
  
  	/* Unlock and release the buffer */
  	UnlockReleaseBuffer(buffer);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
--- 188,193 ----
*************** gistbuildempty(PG_FUNCTION_ARGS)
*** 99,116 ****
   *	  This is the public interface routine for tuple insertion in GiSTs.
   *	  It doesn't do any work; just locks the relation and passes the buck.
   */
! Datum
! gistinsert(PG_FUNCTION_ARGS)
  {
- 	Relation	r = (Relation) PG_GETARG_POINTER(0);
- 	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
- 	bool	   *isnull = (bool *) PG_GETARG_POINTER(2);
- 	ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
- 
- #ifdef NOT_USED
- 	Relation	heapRel = (Relation) PG_GETARG_POINTER(4);
- 	IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
- #endif
  	IndexTuple	itup;
  	GISTSTATE  *giststate;
  	MemoryContext oldCxt;
--- 196,206 ----
   *	  This is the public interface routine for tuple insertion in GiSTs.
   *	  It doesn't do any work; just locks the relation and passes the buck.
   */
! bool
! gistinsert(Relation r, Datum *values, bool *isnull,
! 		   ItemPointer ht_ctid, Relation heapRel,
! 		   IndexUniqueCheck checkUnique)
  {
  	IndexTuple	itup;
  	GISTSTATE  *giststate;
  	MemoryContext oldCxt;
*************** gistinsert(PG_FUNCTION_ARGS)
*** 136,142 ****
  	MemoryContextSwitchTo(oldCxt);
  	freeGISTstate(giststate);
  
! 	PG_RETURN_BOOL(false);
  }
  
  
--- 226,232 ----
  	MemoryContextSwitchTo(oldCxt);
  	freeGISTstate(giststate);
  
! 	return false;
  }
  
  
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
new file mode 100644
index ff888e2..08a029c
*** a/src/backend/access/gist/gistbuild.c
--- b/src/backend/access/gist/gistbuild.c
*************** static BlockNumber gistGetParent(GISTBui
*** 109,120 ****
   * but switches to more efficient buffering build algorithm after a certain
   * number of tuples (unless buffering mode is disabled).
   */
! Datum
! gistbuild(PG_FUNCTION_ARGS)
  {
- 	Relation	heap = (Relation) PG_GETARG_POINTER(0);
- 	Relation	index = (Relation) PG_GETARG_POINTER(1);
- 	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
  	IndexBuildResult *result;
  	double		reltuples;
  	GISTBuildState buildstate;
--- 109,117 ----
   * but switches to more efficient buffering build algorithm after a certain
   * number of tuples (unless buffering mode is disabled).
   */
! IndexBuildResult *
! gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
  {
  	IndexBuildResult *result;
  	double		reltuples;
  	GISTBuildState buildstate;
*************** gistbuild(PG_FUNCTION_ARGS)
*** 232,238 ****
  	result->heap_tuples = reltuples;
  	result->index_tuples = (double) buildstate.indtuples;
  
! 	PG_RETURN_POINTER(result);
  }
  
  /*
--- 229,235 ----
  	result->heap_tuples = reltuples;
  	result->index_tuples = (double) buildstate.indtuples;
  
! 	return result;
  }
  
  /*
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
new file mode 100644
index ce8e582..1adf92f
*** a/src/backend/access/gist/gistget.c
--- b/src/backend/access/gist/gistget.c
*************** getNextNearest(IndexScanDesc scan)
*** 618,628 ****
  /*
   * gistgettuple() -- Get the next tuple in the scan
   */
! Datum
! gistgettuple(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  
  	if (dir != ForwardScanDirection)
--- 618,626 ----
  /*
   * gistgettuple() -- Get the next tuple in the scan
   */
! bool
! gistgettuple(IndexScanDesc scan, ScanDirection dir)
  {
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  
  	if (dir != ForwardScanDirection)
*************** gistgettuple(PG_FUNCTION_ARGS)
*** 651,657 ****
  	if (scan->numberOfOrderBys > 0)
  	{
  		/* Must fetch tuples in strict distance order */
! 		PG_RETURN_BOOL(getNextNearest(scan));
  	}
  	else
  	{
--- 649,655 ----
  	if (scan->numberOfOrderBys > 0)
  	{
  		/* Must fetch tuples in strict distance order */
! 		return getNextNearest(scan);
  	}
  	else
  	{
*************** gistgettuple(PG_FUNCTION_ARGS)
*** 688,694 ****
  
  				so->curPageData++;
  
! 				PG_RETURN_BOOL(true);
  			}
  
  			/*
--- 686,692 ----
  
  				so->curPageData++;
  
! 				return true;
  			}
  
  			/*
*************** gistgettuple(PG_FUNCTION_ARGS)
*** 726,732 ****
  				item = getNextGISTSearchItem(so);
  
  				if (!item)
! 					PG_RETURN_BOOL(false);
  
  				CHECK_FOR_INTERRUPTS();
  
--- 724,730 ----
  				item = getNextGISTSearchItem(so);
  
  				if (!item)
! 					return false;
  
  				CHECK_FOR_INTERRUPTS();
  
*************** gistgettuple(PG_FUNCTION_ARGS)
*** 750,766 ****
  /*
   * gistgetbitmap() -- Get a bitmap of all heap tuple locations
   */
! Datum
! gistgetbitmap(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  	int64		ntids = 0;
  	GISTSearchItem fakeItem;
  
  	if (!so->qual_ok)
! 		PG_RETURN_INT64(0);
  
  	pgstat_count_index_scan(scan->indexRelation);
  
--- 748,762 ----
  /*
   * gistgetbitmap() -- Get a bitmap of all heap tuple locations
   */
! int64
! gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  {
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  	int64		ntids = 0;
  	GISTSearchItem fakeItem;
  
  	if (!so->qual_ok)
! 		return 0;
  
  	pgstat_count_index_scan(scan->indexRelation);
  
*************** gistgetbitmap(PG_FUNCTION_ARGS)
*** 791,797 ****
  		pfree(item);
  	}
  
! 	PG_RETURN_INT64(ntids);
  }
  
  /*
--- 787,793 ----
  		pfree(item);
  	}
  
! 	return ntids;
  }
  
  /*
*************** gistgetbitmap(PG_FUNCTION_ARGS)
*** 799,812 ****
   *
   * Opclasses that implement a fetch function support index-only scans.
   */
! Datum
! gistcanreturn(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
- 	int			attno = PG_GETARG_INT32(1);
- 
  	if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
! 		PG_RETURN_BOOL(true);
  	else
! 		PG_RETURN_BOOL(false);
  }
--- 795,805 ----
   *
   * Opclasses that implement a fetch function support index-only scans.
   */
! bool
! gistcanreturn(Relation index, int attno)
  {
  	if (OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)))
! 		return true;
  	else
! 		return false;
  }
diff --git a/src/backend/access/gist/gistscan.c b/src/backend/access/gist/gistscan.c
new file mode 100644
index a17c5bc..22f0c68
*** a/src/backend/access/gist/gistscan.c
--- b/src/backend/access/gist/gistscan.c
*************** pairingheap_GISTSearchItem_cmp(const pai
*** 54,65 ****
   * Index AM API functions for scanning GiST indexes
   */
  
! Datum
! gistbeginscan(PG_FUNCTION_ARGS)
  {
- 	Relation	r = (Relation) PG_GETARG_POINTER(0);
- 	int			nkeys = PG_GETARG_INT32(1);
- 	int			norderbys = PG_GETARG_INT32(2);
  	IndexScanDesc scan;
  	GISTSTATE  *giststate;
  	GISTScanOpaque so;
--- 54,62 ----
   * Index AM API functions for scanning GiST indexes
   */
  
! IndexScanDesc
! gistbeginscan(Relation r, int nkeys, int norderbys)
  {
  	IndexScanDesc scan;
  	GISTSTATE  *giststate;
  	GISTScanOpaque so;
*************** gistbeginscan(PG_FUNCTION_ARGS)
*** 107,121 ****
  
  	MemoryContextSwitchTo(oldCxt);
  
! 	PG_RETURN_POINTER(scan);
  }
  
! Datum
! gistrescan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanKey		key = (ScanKey) PG_GETARG_POINTER(1);
- 	ScanKey		orderbys = (ScanKey) PG_GETARG_POINTER(3);
  
  	/* nkeys and norderbys arguments are ignored */
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
--- 104,116 ----
  
  	MemoryContextSwitchTo(oldCxt);
  
! 	return scan;
  }
  
! void
! gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
! 		   ScanKey orderbys, int norderbys)
  {
  
  	/* nkeys and norderbys arguments are ignored */
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
*************** gistrescan(PG_FUNCTION_ARGS)
*** 314,341 ****
  		if (!first_time)
  			pfree(fn_extras);
  	}
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! gistmarkpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "GiST does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
! Datum
! gistrestrpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "GiST does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
! Datum
! gistendscan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  
  	/*
--- 309,331 ----
  		if (!first_time)
  			pfree(fn_extras);
  	}
  }
  
! void
! gistmarkpos(IndexScanDesc scan)
  {
  	elog(ERROR, "GiST does not support mark/restore");
  }
  
! void
! gistrestrpos(IndexScanDesc scan)
  {
  	elog(ERROR, "GiST does not support mark/restore");
  }
  
! void
! gistendscan(IndexScanDesc scan)
  {
  	GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
  
  	/*
*************** gistendscan(PG_FUNCTION_ARGS)
*** 343,348 ****
  	 * as well as the queueCxt if there is a separate context for it.
  	 */
  	freeGISTstate(so->giststate);
- 
- 	PG_RETURN_VOID();
  }
--- 333,336 ----
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
new file mode 100644
index 7d596a3..45f6246
*** a/src/backend/access/gist/gistutil.c
--- b/src/backend/access/gist/gistutil.c
*************** gistNewBuffer(Relation r)
*** 808,818 ****
  	return buffer;
  }
  
! Datum
! gistoptions(PG_FUNCTION_ARGS)
  {
- 	Datum		reloptions = PG_GETARG_DATUM(0);
- 	bool		validate = PG_GETARG_BOOL(1);
  	relopt_value *options;
  	GiSTOptions *rdopts;
  	int			numoptions;
--- 808,816 ----
  	return buffer;
  }
  
! bytea *
! gistoptions(Datum reloptions, bool validate)
  {
  	relopt_value *options;
  	GiSTOptions *rdopts;
  	int			numoptions;
*************** gistoptions(PG_FUNCTION_ARGS)
*** 826,832 ****
  
  	/* if none set, we're done */
  	if (numoptions == 0)
! 		PG_RETURN_NULL();
  
  	rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
  
--- 824,830 ----
  
  	/* if none set, we're done */
  	if (numoptions == 0)
! 		return NULL;
  
  	rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
  
*************** gistoptions(PG_FUNCTION_ARGS)
*** 835,841 ****
  
  	pfree(options);
  
! 	PG_RETURN_BYTEA_P(rdopts);
  
  }
  
--- 833,839 ----
  
  	pfree(options);
  
! 	return (bytea *)rdopts;
  
  }
  
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c
new file mode 100644
index a0b0eeb..e0ce620
*** a/src/backend/access/gist/gistvacuum.c
--- b/src/backend/access/gist/gistvacuum.c
***************
*** 25,35 ****
  /*
   * VACUUM cleanup: update FSM
   */
! Datum
! gistvacuumcleanup(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
  	Relation	rel = info->index;
  	BlockNumber npages,
  				blkno;
--- 25,33 ----
  /*
   * VACUUM cleanup: update FSM
   */
! IndexBulkDeleteResult *
! gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
  {
  	Relation	rel = info->index;
  	BlockNumber npages,
  				blkno;
*************** gistvacuumcleanup(PG_FUNCTION_ARGS)
*** 38,44 ****
  
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		PG_RETURN_POINTER(stats);
  
  	/* Set up all-zero stats if gistbulkdelete wasn't called */
  	if (stats == NULL)
--- 36,42 ----
  
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		return stats;
  
  	/* Set up all-zero stats if gistbulkdelete wasn't called */
  	if (stats == NULL)
*************** gistvacuumcleanup(PG_FUNCTION_ARGS)
*** 98,104 ****
  	if (needLock)
  		UnlockRelationForExtension(rel, ExclusiveLock);
  
! 	PG_RETURN_POINTER(stats);
  }
  
  typedef struct GistBDItem
--- 96,102 ----
  	if (needLock)
  		UnlockRelationForExtension(rel, ExclusiveLock);
  
! 	return stats;
  }
  
  typedef struct GistBDItem
*************** pushStackIfSplited(Page page, GistBDItem
*** 137,149 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! gistbulkdelete(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 	IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- 	void	   *callback_state = (void *) PG_GETARG_POINTER(3);
  	Relation	rel = info->index;
  	GistBDItem *stack,
  			   *ptr;
--- 135,144 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
! 			   IndexBulkDeleteCallback callback, void *callback_state)
  {
  	Relation	rel = info->index;
  	GistBDItem *stack,
  			   *ptr;
*************** gistbulkdelete(PG_FUNCTION_ARGS)
*** 276,280 ****
  		vacuum_delay_point();
  	}
  
! 	PG_RETURN_POINTER(stats);
  }
--- 271,275 ----
  		vacuum_delay_point();
  	}
  
! 	return stats;
  }
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
new file mode 100644
index 24b06a5..a51fa7c
*** a/src/backend/access/hash/hash.c
--- b/src/backend/access/hash/hash.c
***************
*** 18,23 ****
--- 18,24 ----
  
  #include "postgres.h"
  
+ #include "access/amapi.h"
  #include "access/hash.h"
  #include "access/relscan.h"
  #include "catalog/index.h"
***************
*** 26,31 ****
--- 27,33 ----
  #include "optimizer/plancat.h"
  #include "storage/bufmgr.h"
  #include "utils/rel.h"
+ #include "utils/selfuncs.h"
  
  
  /* Working state for hashbuild and its callback */
*************** static void hashbuildCallback(Relation i
*** 42,57 ****
  				  bool tupleIsAlive,
  				  void *state);
  
  
  /*
   *	hashbuild() -- build a new hash index.
   */
! Datum
! hashbuild(PG_FUNCTION_ARGS)
  {
- 	Relation	heap = (Relation) PG_GETARG_POINTER(0);
- 	Relation	index = (Relation) PG_GETARG_POINTER(1);
- 	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
  	IndexBuildResult *result;
  	BlockNumber relpages;
  	double		reltuples;
--- 44,150 ----
  				  bool tupleIsAlive,
  				  void *state);
  
+ /*
+  * Hash handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ hashhandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 1;
+ 	amroutine->amsupport = 1;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = true;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = false;
+ 	amroutine->amoptionalkey = false;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = false;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = INT4OID;
+ 
+ 	amroutine->aminsert = hashinsert;
+ 	amroutine->ambeginscan = hashbeginscan;
+ 	amroutine->amgettuple = hashgettuple;
+ 	amroutine->amgetbitmap = hashgetbitmap;
+ 	amroutine->amrescan = hashrescan;
+ 	amroutine->amendscan = hashendscan;
+ 	amroutine->ammarkpos = hashmarkpos;
+ 	amroutine->amrestrpos = hashrestrpos;
+ 	amroutine->ambuild = hashbuild;
+ 	amroutine->ambuildempty = hashbuildempty;
+ 	amroutine->ambulkdelete = hashbulkdelete;
+ 	amroutine->amvacuumcleanup = hashvacuumcleanup;
+ 	amroutine->amcanreturn = NULL;
+ 	amroutine->amcostestimate = hashcostestimate;
+ 	amroutine->amoptions = hashoptions;
+ 	amroutine->amvalidate = hashvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void
+ hashvalidate(OpClassInfo *opclass)
+ {
+ 	ListCell   *l;
+ 
+ 	foreach(l, opclass->procedures)
+ 	{
+ 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (proc->number != HASHPROC)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("invalid support number for hash: %u",
+ 							proc->number)));
+ 	}
+ 
+ 	foreach(l, opclass->operators)
+ 	{
+ 		OpFamilyMember *opr = (OpFamilyMember *) lfirst(l);
+ 		bool			leftFound = false, rightFound = false;
+ 		ListCell	   *ll;
+ 
+ 		if (OidIsValid(opr->sortfamily))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("hash doesn't support ORDER BY operators")));
+ 
+ 		if (opr->number < 1 || opr->number > HTMaxStrategyNumber)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("invalid strategy number for hash: %u",
+ 							opr->number)));
+ 
+ 		foreach(ll, opclass->procedures)
+ 		{
+ 			OpFamilyMember *proc = (OpFamilyMember *) lfirst(ll);
+ 
+ 			if (proc->lefttype == opr->lefttype)
+ 				leftFound = true;
+ 			if (proc->lefttype == opr->righttype)
+ 				rightFound = true;
+ 		}
+ 
+ 		if (!leftFound || !rightFound)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("no hash procedure found for operator: %u",
+ 							opr->object)));
+ 	}
+ }
  
  /*
   *	hashbuild() -- build a new hash index.
   */
! IndexBuildResult *
! hashbuild(Relation heap, Relation index, IndexInfo *indexInfo)
  {
  	IndexBuildResult *result;
  	BlockNumber relpages;
  	double		reltuples;
*************** hashbuild(PG_FUNCTION_ARGS)
*** 112,131 ****
  	result->heap_tuples = reltuples;
  	result->index_tuples = buildstate.indtuples;
  
! 	PG_RETURN_POINTER(result);
  }
  
  /*
   *	hashbuildempty() -- build an empty hash index in the initialization fork
   */
! Datum
! hashbuildempty(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
- 
  	_hash_metapinit(index, 0, INIT_FORKNUM);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
--- 205,220 ----
  	result->heap_tuples = reltuples;
  	result->index_tuples = buildstate.indtuples;
  
! 	return result;
  }
  
  /*
   *	hashbuildempty() -- build an empty hash index in the initialization fork
   */
! void
! hashbuildempty(Relation index)
  {
  	_hash_metapinit(index, 0, INIT_FORKNUM);
  }
  
  /*
*************** hashbuildCallback(Relation index,
*** 167,184 ****
   *	Hash on the heap tuple's key, form an index tuple with hash code.
   *	Find the appropriate location for the new tuple, and put it there.
   */
! Datum
! hashinsert(PG_FUNCTION_ARGS)
  {
- 	Relation	rel = (Relation) PG_GETARG_POINTER(0);
- 	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
- 	bool	   *isnull = (bool *) PG_GETARG_POINTER(2);
- 	ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
- 
- #ifdef NOT_USED
- 	Relation	heapRel = (Relation) PG_GETARG_POINTER(4);
- 	IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
- #endif
  	IndexTuple	itup;
  
  	/*
--- 256,266 ----
   *	Hash on the heap tuple's key, form an index tuple with hash code.
   *	Find the appropriate location for the new tuple, and put it there.
   */
! bool
! hashinsert(Relation rel, Datum *values, bool *isnull,
! 		   ItemPointer ht_ctid, Relation heapRel,
! 		   IndexUniqueCheck checkUnique)
  {
  	IndexTuple	itup;
  
  	/*
*************** hashinsert(PG_FUNCTION_ARGS)
*** 191,197 ****
  	 * chosen in 1986, not of the way nulls are handled here.
  	 */
  	if (isnull[0])
! 		PG_RETURN_BOOL(false);
  
  	/* generate an index tuple */
  	itup = _hash_form_tuple(rel, values, isnull);
--- 273,279 ----
  	 * chosen in 1986, not of the way nulls are handled here.
  	 */
  	if (isnull[0])
! 		return false;
  
  	/* generate an index tuple */
  	itup = _hash_form_tuple(rel, values, isnull);
*************** hashinsert(PG_FUNCTION_ARGS)
*** 201,218 ****
  
  	pfree(itup);
  
! 	PG_RETURN_BOOL(false);
  }
  
  
  /*
   *	hashgettuple() -- Get the next tuple in the scan.
   */
! Datum
! hashgettuple(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	Relation	rel = scan->indexRelation;
  	Buffer		buf;
--- 283,298 ----
  
  	pfree(itup);
  
! 	return false;
  }
  
  
  /*
   *	hashgettuple() -- Get the next tuple in the scan.
   */
! bool
! hashgettuple(IndexScanDesc scan, ScanDirection dir)
  {
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	Relation	rel = scan->indexRelation;
  	Buffer		buf;
*************** hashgettuple(PG_FUNCTION_ARGS)
*** 314,331 ****
  	/* Return current heap TID on success */
  	scan->xs_ctup.t_self = so->hashso_heappos;
  
! 	PG_RETURN_BOOL(res);
  }
  
  
  /*
   *	hashgetbitmap() -- get all tuples at once
   */
! Datum
! hashgetbitmap(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	bool		res;
  	int64		ntids = 0;
--- 394,409 ----
  	/* Return current heap TID on success */
  	scan->xs_ctup.t_self = so->hashso_heappos;
  
! 	return res;
  }
  
  
  /*
   *	hashgetbitmap() -- get all tuples at once
   */
! int64
! hashgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  {
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	bool		res;
  	int64		ntids = 0;
*************** hashgetbitmap(PG_FUNCTION_ARGS)
*** 362,380 ****
  		res = _hash_next(scan, ForwardScanDirection);
  	}
  
! 	PG_RETURN_INT64(ntids);
  }
  
  
  /*
   *	hashbeginscan() -- start a scan on a hash index
   */
! Datum
! hashbeginscan(PG_FUNCTION_ARGS)
  {
- 	Relation	rel = (Relation) PG_GETARG_POINTER(0);
- 	int			nkeys = PG_GETARG_INT32(1);
- 	int			norderbys = PG_GETARG_INT32(2);
  	IndexScanDesc scan;
  	HashScanOpaque so;
  
--- 440,455 ----
  		res = _hash_next(scan, ForwardScanDirection);
  	}
  
! 	return ntids;
  }
  
  
  /*
   *	hashbeginscan() -- start a scan on a hash index
   */
! IndexScanDesc
! hashbeginscan(Relation rel, int nkeys, int norderbys)
  {
  	IndexScanDesc scan;
  	HashScanOpaque so;
  
*************** hashbeginscan(PG_FUNCTION_ARGS)
*** 396,414 ****
  	/* register scan in case we change pages it's using */
  	_hash_regscan(scan);
  
! 	PG_RETURN_POINTER(scan);
  }
  
  /*
   *	hashrescan() -- rescan an index relation
   */
! Datum
! hashrescan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(1);
- 
- 	/* remaining arguments are ignored */
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	Relation	rel = scan->indexRelation;
  
--- 471,486 ----
  	/* register scan in case we change pages it's using */
  	_hash_regscan(scan);
  
! 	return scan;
  }
  
  /*
   *	hashrescan() -- rescan an index relation
   */
! void
! hashrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 		   ScanKey orderbys, int norderbys)
  {
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	Relation	rel = scan->indexRelation;
  
*************** hashrescan(PG_FUNCTION_ARGS)
*** 434,450 ****
  				scan->numberOfKeys * sizeof(ScanKeyData));
  		so->hashso_bucket_valid = false;
  	}
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   *	hashendscan() -- close down a scan
   */
! Datum
! hashendscan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	Relation	rel = scan->indexRelation;
  
--- 506,519 ----
  				scan->numberOfKeys * sizeof(ScanKeyData));
  		so->hashso_bucket_valid = false;
  	}
  }
  
  /*
   *	hashendscan() -- close down a scan
   */
! void
! hashendscan(IndexScanDesc scan)
  {
  	HashScanOpaque so = (HashScanOpaque) scan->opaque;
  	Relation	rel = scan->indexRelation;
  
*************** hashendscan(PG_FUNCTION_ARGS)
*** 463,490 ****
  
  	pfree(so);
  	scan->opaque = NULL;
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   *	hashmarkpos() -- save current scan position
   */
! Datum
! hashmarkpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "hash does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
  /*
   *	hashrestrpos() -- restore scan to last saved position
   */
! Datum
! hashrestrpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "hash does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
  /*
--- 532,555 ----
  
  	pfree(so);
  	scan->opaque = NULL;
  }
  
  /*
   *	hashmarkpos() -- save current scan position
   */
! void
! hashmarkpos(IndexScanDesc scan)
  {
  	elog(ERROR, "hash does not support mark/restore");
  }
  
  /*
   *	hashrestrpos() -- restore scan to last saved position
   */
! void
! hashrestrpos(IndexScanDesc scan)
  {
  	elog(ERROR, "hash does not support mark/restore");
  }
  
  /*
*************** hashrestrpos(PG_FUNCTION_ARGS)
*** 494,506 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! hashbulkdelete(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 	IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- 	void	   *callback_state = (void *) PG_GETARG_POINTER(3);
  	Relation	rel = info->index;
  	double		tuples_removed;
  	double		num_index_tuples;
--- 559,568 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! hashbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
! 			   IndexBulkDeleteCallback callback, void *callback_state)
  {
  	Relation	rel = info->index;
  	double		tuples_removed;
  	double		num_index_tuples;
*************** loop_top:
*** 670,676 ****
  	stats->tuples_removed += tuples_removed;
  	/* hashvacuumcleanup will fill in num_pages */
  
! 	PG_RETURN_POINTER(stats);
  }
  
  /*
--- 732,738 ----
  	stats->tuples_removed += tuples_removed;
  	/* hashvacuumcleanup will fill in num_pages */
  
! 	return stats;
  }
  
  /*
*************** loop_top:
*** 678,701 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! hashvacuumcleanup(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
  	Relation	rel = info->index;
  	BlockNumber num_pages;
  
  	/* If hashbulkdelete wasn't called, return NULL signifying no change */
  	/* Note: this covers the analyze_only case too */
  	if (stats == NULL)
! 		PG_RETURN_POINTER(NULL);
  
  	/* update statistics */
  	num_pages = RelationGetNumberOfBlocks(rel);
  	stats->num_pages = num_pages;
  
! 	PG_RETURN_POINTER(stats);
  }
  
  
--- 740,761 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! hashvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
  {
  	Relation	rel = info->index;
  	BlockNumber num_pages;
  
  	/* If hashbulkdelete wasn't called, return NULL signifying no change */
  	/* Note: this covers the analyze_only case too */
  	if (stats == NULL)
! 		return NULL;
  
  	/* update statistics */
  	num_pages = RelationGetNumberOfBlocks(rel);
  	stats->num_pages = num_pages;
  
! 	return stats;
  }
  
  
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
new file mode 100644
index 3d66216..85236e2
*** a/src/backend/access/hash/hashutil.c
--- b/src/backend/access/hash/hashutil.c
*************** _hash_checkpage(Relation rel, Buffer buf
*** 217,234 ****
  	}
  }
  
! Datum
! hashoptions(PG_FUNCTION_ARGS)
  {
- 	Datum		reloptions = PG_GETARG_DATUM(0);
- 	bool		validate = PG_GETARG_BOOL(1);
  	bytea	   *result;
  
  	result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
  
  	if (result)
! 		PG_RETURN_BYTEA_P(result);
! 	PG_RETURN_NULL();
  }
  
  /*
--- 217,232 ----
  	}
  }
  
! bytea *
! hashoptions(Datum reloptions, bool validate)
  {
  	bytea	   *result;
  
  	result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
  
  	if (result)
! 		return result;
! 	return NULL;
  }
  
  /*
diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c
new file mode 100644
index 2b27e73..ebf9313
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 65,70 ****
--- 65,71 ----
  
  #include "postgres.h"
  
+ #include "access/amapi.h"
  #include "access/relscan.h"
  #include "access/transam.h"
  #include "access/xlog.h"
***************
*** 92,140 ****
   * ----------------------------------------------------------------
   */
  #define RELATION_CHECKS \
! ( \
! 	AssertMacro(RelationIsValid(indexRelation)), \
! 	AssertMacro(PointerIsValid(indexRelation->rd_am)), \
! 	AssertMacro(!ReindexIsProcessingIndex(RelationGetRelid(indexRelation))) \
! )
  
  #define SCAN_CHECKS \
- ( \
- 	AssertMacro(IndexScanIsValid(scan)), \
- 	AssertMacro(RelationIsValid(scan->indexRelation)), \
- 	AssertMacro(PointerIsValid(scan->indexRelation->rd_am)) \
- )
- 
- #define GET_REL_PROCEDURE(pname) \
  do { \
! 	procedure = &indexRelation->rd_aminfo->pname; \
! 	if (!OidIsValid(procedure->fn_oid)) \
! 	{ \
! 		RegProcedure	procOid = indexRelation->rd_am->pname; \
! 		if (!RegProcedureIsValid(procOid)) \
! 			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
! 		fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
! 	} \
! } while(0)
  
! #define GET_UNCACHED_REL_PROCEDURE(pname) \
! do { \
! 	if (!RegProcedureIsValid(indexRelation->rd_am->pname)) \
! 		elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
! 	fmgr_info(indexRelation->rd_am->pname, &procedure); \
! } while(0)
  
! #define GET_SCAN_PROCEDURE(pname) \
! do { \
! 	procedure = &scan->indexRelation->rd_aminfo->pname; \
! 	if (!OidIsValid(procedure->fn_oid)) \
! 	{ \
! 		RegProcedure	procOid = scan->indexRelation->rd_am->pname; \
! 		if (!RegProcedureIsValid(procOid)) \
! 			elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
! 		fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
! 	} \
! } while(0)
  
  static IndexScanDesc index_beginscan_internal(Relation indexRelation,
  						 int nkeys, int norderbys, Snapshot snapshot);
--- 93,124 ----
   * ----------------------------------------------------------------
   */
  #define RELATION_CHECKS \
! do { \
! 	Assert(RelationIsValid(indexRelation)); \
! 	Assert(indexRelation->amroutine); \
! 	Assert(!ReindexIsProcessingIndex(RelationGetRelid(indexRelation))); \
! } while (0)
  
  #define SCAN_CHECKS \
  do { \
! 	Assert(IndexScanIsValid(scan)); \
! 	Assert(RelationIsValid(scan->indexRelation)); \
! 	Assert(scan->indexRelation->amroutine); \
! } while (0)
  
! #define CHECK_PROCEDURE(pname) \
! if (!indexRelation->amroutine->pname) \
! { \
! 	elog(ERROR, "%s is undefined for %s", \
! 		CppAsString(pname), RelationGetRelationName(indexRelation)); \
! } \
  
! #define CHECK_SCAN_PROCEDURE(pname) \
! if (!scan->indexRelation->amroutine->pname) \
! { \
! 	elog(ERROR, "%s is undefined for %s", \
! 		CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
! } \
  
  static IndexScanDesc index_beginscan_internal(Relation indexRelation,
  						 int nkeys, int norderbys, Snapshot snapshot);
*************** index_insert(Relation indexRelation,
*** 210,235 ****
  			 Relation heapRelation,
  			 IndexUniqueCheck checkUnique)
  {
- 	FmgrInfo   *procedure;
- 
  	RELATION_CHECKS;
! 	GET_REL_PROCEDURE(aminsert);
  
! 	if (!(indexRelation->rd_am->ampredlocks))
  		CheckForSerializableConflictIn(indexRelation,
  									   (HeapTuple) NULL,
  									   InvalidBuffer);
  
! 	/*
! 	 * have the am's insert proc do all the work.
! 	 */
! 	return DatumGetBool(FunctionCall6(procedure,
! 									  PointerGetDatum(indexRelation),
! 									  PointerGetDatum(values),
! 									  PointerGetDatum(isnull),
! 									  PointerGetDatum(heap_t_ctid),
! 									  PointerGetDatum(heapRelation),
! 									  Int32GetDatum((int32) checkUnique)));
  }
  
  /*
--- 194,210 ----
  			 Relation heapRelation,
  			 IndexUniqueCheck checkUnique)
  {
  	RELATION_CHECKS;
! 	CHECK_PROCEDURE(aminsert);
  
! 	if (!(indexRelation->amroutine->ampredlocks))
  		CheckForSerializableConflictIn(indexRelation,
  									   (HeapTuple) NULL,
  									   InvalidBuffer);
  
! 	return indexRelation->amroutine->aminsert(indexRelation, values, isnull,
! 											  heap_t_ctid, heapRelation,
! 											  checkUnique);
  }
  
  /*
*************** index_beginscan_internal(Relation indexR
*** 289,300 ****
  						 int nkeys, int norderbys, Snapshot snapshot)
  {
  	IndexScanDesc scan;
- 	FmgrInfo   *procedure;
  
  	RELATION_CHECKS;
! 	GET_REL_PROCEDURE(ambeginscan);
  
! 	if (!(indexRelation->rd_am->ampredlocks))
  		PredicateLockRelation(indexRelation, snapshot);
  
  	/*
--- 264,274 ----
  						 int nkeys, int norderbys, Snapshot snapshot)
  {
  	IndexScanDesc scan;
  
  	RELATION_CHECKS;
! 	CHECK_PROCEDURE(ambeginscan);
  
! 	if (!(indexRelation->amroutine->ampredlocks))
  		PredicateLockRelation(indexRelation, snapshot);
  
  	/*
*************** index_beginscan_internal(Relation indexR
*** 305,315 ****
  	/*
  	 * Tell the AM to open a scan.
  	 */
! 	scan = (IndexScanDesc)
! 		DatumGetPointer(FunctionCall3(procedure,
! 									  PointerGetDatum(indexRelation),
! 									  Int32GetDatum(nkeys),
! 									  Int32GetDatum(norderbys)));
  
  	return scan;
  }
--- 279,286 ----
  	/*
  	 * Tell the AM to open a scan.
  	 */
! 	scan = indexRelation->amroutine->ambeginscan(indexRelation, nkeys,
! 												 norderbys);
  
  	return scan;
  }
*************** index_rescan(IndexScanDesc scan,
*** 331,340 ****
  			 ScanKey keys, int nkeys,
  			 ScanKey orderbys, int norderbys)
  {
- 	FmgrInfo   *procedure;
- 
  	SCAN_CHECKS;
! 	GET_SCAN_PROCEDURE(amrescan);
  
  	Assert(nkeys == scan->numberOfKeys);
  	Assert(norderbys == scan->numberOfOrderBys);
--- 302,309 ----
  			 ScanKey keys, int nkeys,
  			 ScanKey orderbys, int norderbys)
  {
  	SCAN_CHECKS;
! 	CHECK_SCAN_PROCEDURE(amrescan);
  
  	Assert(nkeys == scan->numberOfKeys);
  	Assert(norderbys == scan->numberOfOrderBys);
*************** index_rescan(IndexScanDesc scan,
*** 350,361 ****
  
  	scan->kill_prior_tuple = false;		/* for safety */
  
! 	FunctionCall5(procedure,
! 				  PointerGetDatum(scan),
! 				  PointerGetDatum(keys),
! 				  Int32GetDatum(nkeys),
! 				  PointerGetDatum(orderbys),
! 				  Int32GetDatum(norderbys));
  }
  
  /* ----------------
--- 319,326 ----
  
  	scan->kill_prior_tuple = false;		/* for safety */
  
! 	scan->indexRelation->amroutine->amrescan(scan, keys, nkeys,
! 											 orderbys, norderbys);
  }
  
  /* ----------------
*************** index_rescan(IndexScanDesc scan,
*** 365,374 ****
  void
  index_endscan(IndexScanDesc scan)
  {
- 	FmgrInfo   *procedure;
- 
  	SCAN_CHECKS;
! 	GET_SCAN_PROCEDURE(amendscan);
  
  	/* Release any held pin on a heap page */
  	if (BufferIsValid(scan->xs_cbuf))
--- 330,337 ----
  void
  index_endscan(IndexScanDesc scan)
  {
  	SCAN_CHECKS;
! 	CHECK_SCAN_PROCEDURE(amendscan);
  
  	/* Release any held pin on a heap page */
  	if (BufferIsValid(scan->xs_cbuf))
*************** index_endscan(IndexScanDesc scan)
*** 378,384 ****
  	}
  
  	/* End the AM's scan */
! 	FunctionCall1(procedure, PointerGetDatum(scan));
  
  	/* Release index refcount acquired by index_beginscan */
  	RelationDecrementReferenceCount(scan->indexRelation);
--- 341,347 ----
  	}
  
  	/* End the AM's scan */
! 	scan->indexRelation->amroutine->amendscan(scan);
  
  	/* Release index refcount acquired by index_beginscan */
  	RelationDecrementReferenceCount(scan->indexRelation);
*************** index_endscan(IndexScanDesc scan)
*** 394,405 ****
  void
  index_markpos(IndexScanDesc scan)
  {
- 	FmgrInfo   *procedure;
- 
  	SCAN_CHECKS;
! 	GET_SCAN_PROCEDURE(ammarkpos);
  
! 	FunctionCall1(procedure, PointerGetDatum(scan));
  }
  
  /* ----------------
--- 357,366 ----
  void
  index_markpos(IndexScanDesc scan)
  {
  	SCAN_CHECKS;
! 	CHECK_SCAN_PROCEDURE(ammarkpos);
  
! 	scan->indexRelation->amroutine->ammarkpos(scan);
  }
  
  /* ----------------
*************** index_markpos(IndexScanDesc scan)
*** 421,438 ****
  void
  index_restrpos(IndexScanDesc scan)
  {
- 	FmgrInfo   *procedure;
- 
  	Assert(IsMVCCSnapshot(scan->xs_snapshot));
  
  	SCAN_CHECKS;
! 	GET_SCAN_PROCEDURE(amrestrpos);
  
  	scan->xs_continue_hot = false;
  
  	scan->kill_prior_tuple = false;		/* for safety */
  
! 	FunctionCall1(procedure, PointerGetDatum(scan));
  }
  
  /* ----------------
--- 382,397 ----
  void
  index_restrpos(IndexScanDesc scan)
  {
  	Assert(IsMVCCSnapshot(scan->xs_snapshot));
  
  	SCAN_CHECKS;
! 	CHECK_SCAN_PROCEDURE(amrestrpos);
  
  	scan->xs_continue_hot = false;
  
  	scan->kill_prior_tuple = false;		/* for safety */
  
! 	scan->indexRelation->amroutine->amrestrpos(scan);
  }
  
  /* ----------------
*************** index_restrpos(IndexScanDesc scan)
*** 445,455 ****
  ItemPointer
  index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
  {
- 	FmgrInfo   *procedure;
  	bool		found;
  
  	SCAN_CHECKS;
! 	GET_SCAN_PROCEDURE(amgettuple);
  
  	Assert(TransactionIdIsValid(RecentGlobalXmin));
  
--- 404,413 ----
  ItemPointer
  index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
  {
  	bool		found;
  
  	SCAN_CHECKS;
! 	CHECK_SCAN_PROCEDURE(amgettuple);
  
  	Assert(TransactionIdIsValid(RecentGlobalXmin));
  
*************** index_getnext_tid(IndexScanDesc scan, Sc
*** 459,467 ****
  	 * scan->xs_recheck and possibly scan->xs_itup, though we pay no attention
  	 * to those fields here.
  	 */
! 	found = DatumGetBool(FunctionCall2(procedure,
! 									   PointerGetDatum(scan),
! 									   Int32GetDatum(direction)));
  
  	/* Reset kill flag immediately for safety */
  	scan->kill_prior_tuple = false;
--- 417,423 ----
  	 * scan->xs_recheck and possibly scan->xs_itup, though we pay no attention
  	 * to those fields here.
  	 */
! 	found = scan->indexRelation->amroutine->amgettuple(scan, direction);
  
  	/* Reset kill flag immediately for safety */
  	scan->kill_prior_tuple = false;
*************** index_getnext(IndexScanDesc scan, ScanDi
*** 635,646 ****
  int64
  index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
  {
- 	FmgrInfo   *procedure;
  	int64		ntids;
  	Datum		d;
  
  	SCAN_CHECKS;
! 	GET_SCAN_PROCEDURE(amgetbitmap);
  
  	/* just make sure this is false... */
  	scan->kill_prior_tuple = false;
--- 591,601 ----
  int64
  index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
  {
  	int64		ntids;
  	Datum		d;
  
  	SCAN_CHECKS;
! 	CHECK_SCAN_PROCEDURE(amgetbitmap);
  
  	/* just make sure this is false... */
  	scan->kill_prior_tuple = false;
*************** index_getbitmap(IndexScanDesc scan, TIDB
*** 648,656 ****
  	/*
  	 * have the am's getbitmap proc do all the work.
  	 */
! 	d = FunctionCall2(procedure,
! 					  PointerGetDatum(scan),
! 					  PointerGetDatum(bitmap));
  
  	ntids = DatumGetInt64(d);
  
--- 603,609 ----
  	/*
  	 * have the am's getbitmap proc do all the work.
  	 */
! 	d = scan->indexRelation->amroutine->amgetbitmap(scan, bitmap);
  
  	ntids = DatumGetInt64(d);
  
*************** index_bulk_delete(IndexVacuumInfo *info,
*** 680,699 ****
  				  void *callback_state)
  {
  	Relation	indexRelation = info->index;
- 	FmgrInfo	procedure;
- 	IndexBulkDeleteResult *result;
  
  	RELATION_CHECKS;
! 	GET_UNCACHED_REL_PROCEDURE(ambulkdelete);
! 
! 	result = (IndexBulkDeleteResult *)
! 		DatumGetPointer(FunctionCall4(&procedure,
! 									  PointerGetDatum(info),
! 									  PointerGetDatum(stats),
! 									  PointerGetDatum((Pointer) callback),
! 									  PointerGetDatum(callback_state)));
  
! 	return result;
  }
  
  /* ----------------
--- 633,644 ----
  				  void *callback_state)
  {
  	Relation	indexRelation = info->index;
  
  	RELATION_CHECKS;
! 	CHECK_PROCEDURE(ambulkdelete);
  
! 	return indexRelation->amroutine->ambulkdelete(info, stats,
! 												  callback, callback_state);
  }
  
  /* ----------------
*************** index_vacuum_cleanup(IndexVacuumInfo *in
*** 707,724 ****
  					 IndexBulkDeleteResult *stats)
  {
  	Relation	indexRelation = info->index;
- 	FmgrInfo	procedure;
- 	IndexBulkDeleteResult *result;
  
  	RELATION_CHECKS;
! 	GET_UNCACHED_REL_PROCEDURE(amvacuumcleanup);
! 
! 	result = (IndexBulkDeleteResult *)
! 		DatumGetPointer(FunctionCall2(&procedure,
! 									  PointerGetDatum(info),
! 									  PointerGetDatum(stats)));
  
! 	return result;
  }
  
  /* ----------------
--- 652,662 ----
  					 IndexBulkDeleteResult *stats)
  {
  	Relation	indexRelation = info->index;
  
  	RELATION_CHECKS;
! 	CHECK_PROCEDURE(amvacuumcleanup);
  
! 	return indexRelation->amroutine->amvacuumcleanup(info, stats);
  }
  
  /* ----------------
*************** index_vacuum_cleanup(IndexVacuumInfo *in
*** 731,749 ****
  bool
  index_can_return(Relation indexRelation, int attno)
  {
- 	FmgrInfo   *procedure;
- 
  	RELATION_CHECKS;
  
  	/* amcanreturn is optional; assume FALSE if not provided by AM */
! 	if (!RegProcedureIsValid(indexRelation->rd_am->amcanreturn))
  		return false;
  
! 	GET_REL_PROCEDURE(amcanreturn);
! 
! 	return DatumGetBool(FunctionCall2(procedure,
! 									  PointerGetDatum(indexRelation),
! 									  Int32GetDatum(attno)));
  }
  
  /* ----------------
--- 669,681 ----
  bool
  index_can_return(Relation indexRelation, int attno)
  {
  	RELATION_CHECKS;
  
  	/* amcanreturn is optional; assume FALSE if not provided by AM */
! 	if (!indexRelation->amroutine->amcanreturn)
  		return false;
  
! 	return indexRelation->amroutine->amcanreturn(indexRelation, attno);
  }
  
  /* ----------------
*************** index_getprocid(Relation irel,
*** 781,787 ****
  	int			nproc;
  	int			procindex;
  
! 	nproc = irel->rd_am->amsupport;
  
  	Assert(procnum > 0 && procnum <= (uint16) nproc);
  
--- 713,719 ----
  	int			nproc;
  	int			procindex;
  
! 	nproc = irel->amroutine->amsupport;
  
  	Assert(procnum > 0 && procnum <= (uint16) nproc);
  
*************** index_getprocinfo(Relation irel,
*** 815,821 ****
  	int			nproc;
  	int			procindex;
  
! 	nproc = irel->rd_am->amsupport;
  
  	Assert(procnum > 0 && procnum <= (uint16) nproc);
  
--- 747,753 ----
  	int			nproc;
  	int			procindex;
  
! 	nproc = irel->amroutine->amsupport;
  
  	Assert(procnum > 0 && procnum <= (uint16) nproc);
  
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
new file mode 100644
index cf4a6dc..ee623ae
*** a/src/backend/access/nbtree/nbtree.c
--- b/src/backend/access/nbtree/nbtree.c
*************** static void btvacuumpage(BTVacState *vst
*** 76,89 ****
  
  
  /*
!  *	btbuild() -- build a new btree index.
   */
  Datum
! btbuild(PG_FUNCTION_ARGS)
  {
- 	Relation	heap = (Relation) PG_GETARG_POINTER(0);
- 	Relation	index = (Relation) PG_GETARG_POINTER(1);
- 	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
  	IndexBuildResult *result;
  	double		reltuples;
  	BTBuildState buildstate;
--- 76,182 ----
  
  
  /*
!  * Btree handler function: return IndexAmRoutine with access method parameters
!  * and callbacks.
   */
  Datum
! bthandler(PG_FUNCTION_ARGS)
! {
! 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
! 
! 	amroutine->amstrategies = 5;
! 	amroutine->amsupport = 2;
! 	amroutine->amcanorder = true;
! 	amroutine->amcanorderbyop = false;
! 	amroutine->amcanbackward = true;
! 	amroutine->amcanunique = true;
! 	amroutine->amcanmulticol = true;
! 	amroutine->amoptionalkey = true;
! 	amroutine->amsearcharray = true;
! 	amroutine->amsearchnulls = true;
! 	amroutine->amstorage = false;
! 	amroutine->amclusterable = true;
! 	amroutine->ampredlocks = true;
! 	amroutine->amkeytype = 0;
! 
! 	amroutine->aminsert = btinsert;
! 	amroutine->ambeginscan = btbeginscan;
! 	amroutine->amgettuple = btgettuple;
! 	amroutine->amgetbitmap = btgetbitmap;
! 	amroutine->amrescan = btrescan;
! 	amroutine->amendscan = btendscan;
! 	amroutine->ammarkpos = btmarkpos;
! 	amroutine->amrestrpos = btrestrpos;
! 	amroutine->ambuild = btbuild;
! 	amroutine->ambuildempty = btbuildempty;
! 	amroutine->ambulkdelete = btbulkdelete;
! 	amroutine->amvacuumcleanup = btvacuumcleanup;
! 	amroutine->amcanreturn = btcanreturn;
! 	amroutine->amcostestimate = btcostestimate;
! 	amroutine->amoptions = btoptions;
! 	amroutine->amvalidate = btvalidate;
! 
! 	PG_RETURN_POINTER(amroutine);
! }
! 
! void
! btvalidate(OpClassInfo *opclass)
! {
! 	ListCell   *l;
! 
! 	foreach(l, opclass->procedures)
! 	{
! 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
! 
! 		if (proc->number != BTORDER_PROC &&
! 			proc->number != BTSORTSUPPORT_PROC)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 					 errmsg("invalid support number for btree: %u",
! 							proc->number)));
! 	}
! 
! 	foreach(l, opclass->operators)
! 	{
! 		OpFamilyMember *opr = (OpFamilyMember *) lfirst(l);
! 		bool			found = false;
! 		ListCell	   *ll;
! 
! 		if (OidIsValid(opr->sortfamily))
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 					 errmsg("btree doesn't support ORDER BY operators")));
! 
! 		if (opr->number < 1 || opr->number > BTMaxStrategyNumber)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 					 errmsg("invalid strategy number for btree: %u",
! 							opr->number)));
! 
! 		foreach(ll, opclass->procedures)
! 		{
! 			OpFamilyMember *proc = (OpFamilyMember *) lfirst(ll);
! 
! 			if (proc->number == BTORDER_PROC &&
! 				proc->lefttype == opr->lefttype &&
! 				proc->righttype == opr->righttype)
! 				found = true;
! 		}
! 
! 		if (!found)
! 			ereport(ERROR,
! 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
! 					 errmsg("no ordering procedure found for operator: %u",
! 							opr->object)));
! 	}
! }
! 
! /*
!  *	btbuild() -- build a new btree index.
!  */
! IndexBuildResult *
! btbuild(Relation heap, Relation index, IndexInfo *indexInfo)
  {
  	IndexBuildResult *result;
  	double		reltuples;
  	BTBuildState buildstate;
*************** btbuild(PG_FUNCTION_ARGS)
*** 155,161 ****
  	result->heap_tuples = reltuples;
  	result->index_tuples = buildstate.indtuples;
  
! 	PG_RETURN_POINTER(result);
  }
  
  /*
--- 248,254 ----
  	result->heap_tuples = reltuples;
  	result->index_tuples = buildstate.indtuples;
  
! 	return result;
  }
  
  /*
*************** btbuildCallback(Relation index,
*** 190,200 ****
  /*
   *	btbuildempty() -- build an empty btree index in the initialization fork
   */
! Datum
! btbuildempty(PG_FUNCTION_ARGS)
  {
! 	Relation	index = (Relation) PG_GETARG_POINTER(0);
! 	Page		metapage;
  
  	/* Construct metapage. */
  	metapage = (Page) palloc(BLCKSZ);
--- 283,292 ----
  /*
   *	btbuildempty() -- build an empty btree index in the initialization fork
   */
! void
! btbuildempty(Relation index)
  {
! 	Page	metapage;
  
  	/* Construct metapage. */
  	metapage = (Page) palloc(BLCKSZ);
*************** btbuildempty(PG_FUNCTION_ARGS)
*** 214,221 ****
  	 * checkpoint may have moved the redo pointer past our xlog record.
  	 */
  	smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
--- 306,311 ----
*************** btbuildempty(PG_FUNCTION_ARGS)
*** 224,238 ****
   *		Descend the tree recursively, find the appropriate location for our
   *		new tuple, and put it there.
   */
! Datum
! btinsert(PG_FUNCTION_ARGS)
  {
- 	Relation	rel = (Relation) PG_GETARG_POINTER(0);
- 	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
- 	bool	   *isnull = (bool *) PG_GETARG_POINTER(2);
- 	ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
- 	Relation	heapRel = (Relation) PG_GETARG_POINTER(4);
- 	IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
  	bool		result;
  	IndexTuple	itup;
  
--- 314,324 ----
   *		Descend the tree recursively, find the appropriate location for our
   *		new tuple, and put it there.
   */
! bool
! btinsert(Relation rel, Datum *values, bool *isnull,
! 		 ItemPointer ht_ctid, Relation heapRel,
! 		 IndexUniqueCheck checkUnique)
  {
  	bool		result;
  	IndexTuple	itup;
  
*************** btinsert(PG_FUNCTION_ARGS)
*** 244,260 ****
  
  	pfree(itup);
  
! 	PG_RETURN_BOOL(result);
  }
  
  /*
   *	btgettuple() -- Get the next tuple in the scan.
   */
! Datum
! btgettuple(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  	bool		res;
  
--- 330,344 ----
  
  	pfree(itup);
  
! 	return result;
  }
  
  /*
   *	btgettuple() -- Get the next tuple in the scan.
   */
! bool
! btgettuple(IndexScanDesc scan, ScanDirection dir)
  {
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  	bool		res;
  
*************** btgettuple(PG_FUNCTION_ARGS)
*** 270,276 ****
  	{
  		/* punt if we have any unsatisfiable array keys */
  		if (so->numArrayKeys < 0)
! 			PG_RETURN_BOOL(false);
  
  		_bt_start_array_keys(scan, dir);
  	}
--- 354,360 ----
  	{
  		/* punt if we have any unsatisfiable array keys */
  		if (so->numArrayKeys < 0)
! 			return false;
  
  		_bt_start_array_keys(scan, dir);
  	}
*************** btgettuple(PG_FUNCTION_ARGS)
*** 320,336 ****
  		/* ... otherwise see if we have more array keys to deal with */
  	} while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
  
! 	PG_RETURN_BOOL(res);
  }
  
  /*
   * btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
   */
! Datum
! btgetbitmap(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  	int64		ntids = 0;
  	ItemPointer heapTid;
--- 404,418 ----
  		/* ... otherwise see if we have more array keys to deal with */
  	} while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));
  
! 	return res;
  }
  
  /*
   * btgetbitmap() -- gets all matching tuples, and adds them to a bitmap
   */
! int64
! btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  {
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  	int64		ntids = 0;
  	ItemPointer heapTid;
*************** btgetbitmap(PG_FUNCTION_ARGS)
*** 342,348 ****
  	{
  		/* punt if we have any unsatisfiable array keys */
  		if (so->numArrayKeys < 0)
! 			PG_RETURN_INT64(ntids);
  
  		_bt_start_array_keys(scan, ForwardScanDirection);
  	}
--- 424,430 ----
  	{
  		/* punt if we have any unsatisfiable array keys */
  		if (so->numArrayKeys < 0)
! 			return ntids;
  
  		_bt_start_array_keys(scan, ForwardScanDirection);
  	}
*************** btgetbitmap(PG_FUNCTION_ARGS)
*** 380,397 ****
  		/* Now see if we have more array keys to deal with */
  	} while (so->numArrayKeys && _bt_advance_array_keys(scan, ForwardScanDirection));
  
! 	PG_RETURN_INT64(ntids);
  }
  
  /*
   *	btbeginscan() -- start a scan on a btree index
   */
! Datum
! btbeginscan(PG_FUNCTION_ARGS)
  {
- 	Relation	rel = (Relation) PG_GETARG_POINTER(0);
- 	int			nkeys = PG_GETARG_INT32(1);
- 	int			norderbys = PG_GETARG_INT32(2);
  	IndexScanDesc scan;
  	BTScanOpaque so;
  
--- 462,476 ----
  		/* Now see if we have more array keys to deal with */
  	} while (so->numArrayKeys && _bt_advance_array_keys(scan, ForwardScanDirection));
  
! 	return ntids;
  }
  
  /*
   *	btbeginscan() -- start a scan on a btree index
   */
! IndexScanDesc
! btbeginscan(Relation rel, int nkeys, int norderbys)
  {
  	IndexScanDesc scan;
  	BTScanOpaque so;
  
*************** btbeginscan(PG_FUNCTION_ARGS)
*** 429,447 ****
  
  	scan->opaque = so;
  
! 	PG_RETURN_POINTER(scan);
  }
  
  /*
   *	btrescan() -- rescan an index relation
   */
! Datum
! btrescan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(1);
- 
- 	/* remaining arguments are ignored */
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* we aren't holding any read locks, but gotta drop the pins */
--- 508,523 ----
  
  	scan->opaque = so;
  
! 	return scan;
  }
  
  /*
   *	btrescan() -- rescan an index relation
   */
! void
! btrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 		 ScanKey orderbys, int norderbys)
  {
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* we aren't holding any read locks, but gotta drop the pins */
*************** btrescan(PG_FUNCTION_ARGS)
*** 492,508 ****
  
  	/* If any keys are SK_SEARCHARRAY type, set up array-key info */
  	_bt_preprocess_array_keys(scan);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   *	btendscan() -- close down a scan
   */
! Datum
! btendscan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* we aren't holding any read locks, but gotta drop the pins */
--- 568,581 ----
  
  	/* If any keys are SK_SEARCHARRAY type, set up array-key info */
  	_bt_preprocess_array_keys(scan);
  }
  
  /*
   *	btendscan() -- close down a scan
   */
! void
! btendscan(IndexScanDesc scan)
  {
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* we aren't holding any read locks, but gotta drop the pins */
*************** btendscan(PG_FUNCTION_ARGS)
*** 531,547 ****
  		pfree(so->currTuples);
  	/* so->markTuples should not be pfree'd, see btrescan */
  	pfree(so);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   *	btmarkpos() -- save current scan position
   */
! Datum
! btmarkpos(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* There may be an old mark with a pin (but no lock). */
--- 604,617 ----
  		pfree(so->currTuples);
  	/* so->markTuples should not be pfree'd, see btrescan */
  	pfree(so);
  }
  
  /*
   *	btmarkpos() -- save current scan position
   */
! void
! btmarkpos(IndexScanDesc scan)
  {
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* There may be an old mark with a pin (but no lock). */
*************** btmarkpos(PG_FUNCTION_ARGS)
*** 564,580 ****
  	/* Also record the current positions of any array keys */
  	if (so->numArrayKeys)
  		_bt_mark_array_keys(scan);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   *	btrestrpos() -- restore scan to last saved position
   */
! Datum
! btrestrpos(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* Restore the marked positions of any array keys */
--- 634,647 ----
  	/* Also record the current positions of any array keys */
  	if (so->numArrayKeys)
  		_bt_mark_array_keys(scan);
  }
  
  /*
   *	btrestrpos() -- restore scan to last saved position
   */
! void
! btrestrpos(IndexScanDesc scan)
  {
  	BTScanOpaque so = (BTScanOpaque) scan->opaque;
  
  	/* Restore the marked positions of any array keys */
*************** btrestrpos(PG_FUNCTION_ARGS)
*** 642,649 ****
  		else
  			BTScanPosInvalidate(so->currPos);
  	}
- 
- 	PG_RETURN_VOID();
  }
  
  /*
--- 709,714 ----
*************** btrestrpos(PG_FUNCTION_ARGS)
*** 653,665 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! btbulkdelete(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *volatile stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 	IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- 	void	   *callback_state = (void *) PG_GETARG_POINTER(3);
  	Relation	rel = info->index;
  	BTCycleId	cycleid;
  
--- 718,727 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! btbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
! 			 IndexBulkDeleteCallback callback, void *callback_state)
  {
  	Relation	rel = info->index;
  	BTCycleId	cycleid;
  
*************** btbulkdelete(PG_FUNCTION_ARGS)
*** 678,684 ****
  	PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
  	_bt_end_vacuum(rel);
  
! 	PG_RETURN_POINTER(stats);
  }
  
  /*
--- 740,746 ----
  	PG_END_ENSURE_ERROR_CLEANUP(_bt_end_vacuum_callback, PointerGetDatum(rel));
  	_bt_end_vacuum(rel);
  
! 	return stats;
  }
  
  /*
*************** btbulkdelete(PG_FUNCTION_ARGS)
*** 686,700 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! btvacuumcleanup(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		PG_RETURN_POINTER(stats);
  
  	/*
  	 * If btbulkdelete was called, we need not do anything, just return the
--- 748,759 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! btvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
  {
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		return stats;
  
  	/*
  	 * If btbulkdelete was called, we need not do anything, just return the
*************** btvacuumcleanup(PG_FUNCTION_ARGS)
*** 726,732 ****
  			stats->num_index_tuples = info->num_heap_tuples;
  	}
  
! 	PG_RETURN_POINTER(stats);
  }
  
  /*
--- 785,791 ----
  			stats->num_index_tuples = info->num_heap_tuples;
  	}
  
! 	return stats;
  }
  
  /*
*************** restart:
*** 1127,1134 ****
   *
   * btrees always do, so this is trivial.
   */
! Datum
! btcanreturn(PG_FUNCTION_ARGS)
  {
  	PG_RETURN_BOOL(true);
  }
--- 1186,1193 ----
   *
   * btrees always do, so this is trivial.
   */
! bool
! btcanreturn(Relation index, int attno)
  {
  	PG_RETURN_BOOL(true);
  }
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
new file mode 100644
index 91331ba..2fec62d
*** a/src/backend/access/nbtree/nbtutils.c
--- b/src/backend/access/nbtree/nbtutils.c
*************** BTreeShmemInit(void)
*** 2058,2072 ****
  		Assert(found);
  }
  
! Datum
! btoptions(PG_FUNCTION_ARGS)
  {
- 	Datum		reloptions = PG_GETARG_DATUM(0);
- 	bool		validate = PG_GETARG_BOOL(1);
  	bytea	   *result;
  
  	result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
  	if (result)
! 		PG_RETURN_BYTEA_P(result);
! 	PG_RETURN_NULL();
  }
--- 2058,2070 ----
  		Assert(found);
  }
  
! bytea *
! btoptions(Datum reloptions, bool validate)
  {
  	bytea	   *result;
  
  	result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
  	if (result)
! 		return result;
! 	return NULL;
  }
diff --git a/src/backend/access/spgist/spginsert.c b/src/backend/access/spgist/spginsert.c
new file mode 100644
index bceee8d..c95ae3d
*** a/src/backend/access/spgist/spginsert.c
--- b/src/backend/access/spgist/spginsert.c
*************** spgistBuildCallback(Relation index, Heap
*** 65,76 ****
  /*
   * Build an SP-GiST index.
   */
! Datum
! spgbuild(PG_FUNCTION_ARGS)
  {
- 	Relation	heap = (Relation) PG_GETARG_POINTER(0);
- 	Relation	index = (Relation) PG_GETARG_POINTER(1);
- 	IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
  	IndexBuildResult *result;
  	double		reltuples;
  	SpGistBuildState buildstate;
--- 65,73 ----
  /*
   * Build an SP-GiST index.
   */
! IndexBuildResult *
! spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
  {
  	IndexBuildResult *result;
  	double		reltuples;
  	SpGistBuildState buildstate;
*************** spgbuild(PG_FUNCTION_ARGS)
*** 151,166 ****
  	result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
  	result->heap_tuples = result->index_tuples = reltuples;
  
! 	PG_RETURN_POINTER(result);
  }
  
  /*
   * Build an empty SPGiST index in the initialization fork
   */
! Datum
! spgbuildempty(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
  	Page		page;
  
  	/* Construct metapage. */
--- 148,162 ----
  	result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
  	result->heap_tuples = result->index_tuples = reltuples;
  
! 	return result;
  }
  
  /*
   * Build an empty SPGiST index in the initialization fork
   */
! void
! spgbuildempty(Relation index)
  {
  	Page		page;
  
  	/* Construct metapage. */
*************** spgbuildempty(PG_FUNCTION_ARGS)
*** 201,225 ****
  	 * checkpoint may have moved the redo pointer past our xlog record.
  	 */
  	smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   * Insert one new tuple into an SPGiST index.
   */
! Datum
! spginsert(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
- 	Datum	   *values = (Datum *) PG_GETARG_POINTER(1);
- 	bool	   *isnull = (bool *) PG_GETARG_POINTER(2);
- 	ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
- 
- #ifdef NOT_USED
- 	Relation	heapRel = (Relation) PG_GETARG_POINTER(4);
- 	IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
- #endif
  	SpGistState spgstate;
  	MemoryContext oldCtx;
  	MemoryContext insertCtx;
--- 197,212 ----
  	 * checkpoint may have moved the redo pointer past our xlog record.
  	 */
  	smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
  }
  
  /*
   * Insert one new tuple into an SPGiST index.
   */
! bool
! spginsert(Relation index, Datum *values, bool *isnull,
! 		  ItemPointer ht_ctid, Relation heapRel,
! 		  IndexUniqueCheck checkUnique)
  {
  	SpGistState spgstate;
  	MemoryContext oldCtx;
  	MemoryContext insertCtx;
*************** spginsert(PG_FUNCTION_ARGS)
*** 251,255 ****
  	MemoryContextDelete(insertCtx);
  
  	/* return false since we've not done any unique check */
! 	PG_RETURN_BOOL(false);
  }
--- 238,242 ----
  	MemoryContextDelete(insertCtx);
  
  	/* return false since we've not done any unique check */
! 	return false;
  }
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
new file mode 100644
index 8a0d909..1e73690
*** a/src/backend/access/spgist/spgscan.c
--- b/src/backend/access/spgist/spgscan.c
*************** spgPrepareScanKeys(IndexScanDesc scan)
*** 173,185 ****
  	}
  }
  
! Datum
! spgbeginscan(PG_FUNCTION_ARGS)
  {
- 	Relation	rel = (Relation) PG_GETARG_POINTER(0);
- 	int			keysz = PG_GETARG_INT32(1);
- 
- 	/* ScanKey			scankey = (ScanKey) PG_GETARG_POINTER(2); */
  	IndexScanDesc scan;
  	SpGistScanOpaque so;
  
--- 173,181 ----
  	}
  }
  
! IndexScanDesc
! spgbeginscan(Relation rel, int keysz, int orderbysz)
  {
  	IndexScanDesc scan;
  	SpGistScanOpaque so;
  
*************** spgbeginscan(PG_FUNCTION_ARGS)
*** 202,216 ****
  
  	scan->opaque = so;
  
! 	PG_RETURN_POINTER(scan);
  }
  
! Datum
! spgrescan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
- 	ScanKey		scankey = (ScanKey) PG_GETARG_POINTER(1);
  
  	/* copy scankeys into local storage */
  	if (scankey && scan->numberOfKeys > 0)
--- 198,212 ----
  
  	scan->opaque = so;
  
! 	return scan;
  }
  
! 
! void
! spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 		  ScanKey orderbys, int norderbys)
  {
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	/* copy scankeys into local storage */
  	if (scankey && scan->numberOfKeys > 0)
*************** spgrescan(PG_FUNCTION_ARGS)
*** 224,256 ****
  
  	/* set up starting stack entries */
  	resetSpGistScanOpaque(so);
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! spgendscan(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	MemoryContextDelete(so->tempCxt);
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! spgmarkpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "SPGiST does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
! Datum
! spgrestrpos(PG_FUNCTION_ARGS)
  {
  	elog(ERROR, "SPGiST does not support mark/restore");
- 	PG_RETURN_VOID();
  }
  
  /*
--- 220,245 ----
  
  	/* set up starting stack entries */
  	resetSpGistScanOpaque(so);
  }
  
! void
! spgendscan(IndexScanDesc scan)
  {
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	MemoryContextDelete(so->tempCxt);
  }
  
! void
! spgmarkpos(IndexScanDesc scan)
  {
  	elog(ERROR, "SPGiST does not support mark/restore");
  }
  
! void
! spgrestrpos(IndexScanDesc scan)
  {
  	elog(ERROR, "SPGiST does not support mark/restore");
  }
  
  /*
*************** storeBitmap(SpGistScanOpaque so, ItemPoi
*** 571,581 ****
  	so->ntids++;
  }
  
! Datum
! spggetbitmap(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	/* Copy want_itup to *so so we don't need to pass it around separately */
--- 560,568 ----
  	so->ntids++;
  }
  
! int64
! spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
  {
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	/* Copy want_itup to *so so we don't need to pass it around separately */
*************** spggetbitmap(PG_FUNCTION_ARGS)
*** 586,592 ****
  
  	spgWalk(scan->indexRelation, so, true, storeBitmap);
  
! 	PG_RETURN_INT64(so->ntids);
  }
  
  /* storeRes subroutine for gettuple case */
--- 573,579 ----
  
  	spgWalk(scan->indexRelation, so, true, storeBitmap);
  
! 	return so->ntids;
  }
  
  /* storeRes subroutine for gettuple case */
*************** storeGettuple(SpGistScanOpaque so, ItemP
*** 610,620 ****
  	so->nPtrs++;
  }
  
! Datum
! spggettuple(PG_FUNCTION_ARGS)
  {
- 	IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
- 	ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	if (dir != ForwardScanDirection)
--- 597,605 ----
  	so->nPtrs++;
  }
  
! bool
! spggettuple(IndexScanDesc scan, ScanDirection dir)
  {
  	SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
  
  	if (dir != ForwardScanDirection)
*************** spggettuple(PG_FUNCTION_ARGS)
*** 632,638 ****
  			scan->xs_recheck = so->recheck[so->iPtr];
  			scan->xs_itup = so->indexTups[so->iPtr];
  			so->iPtr++;
! 			PG_RETURN_BOOL(true);
  		}
  
  		if (so->want_itup)
--- 617,623 ----
  			scan->xs_recheck = so->recheck[so->iPtr];
  			scan->xs_itup = so->indexTups[so->iPtr];
  			so->iPtr++;
! 			return true;
  		}
  
  		if (so->want_itup)
*************** spggettuple(PG_FUNCTION_ARGS)
*** 651,669 ****
  			break;				/* must have completed scan */
  	}
  
! 	PG_RETURN_BOOL(false);
  }
  
! Datum
! spgcanreturn(PG_FUNCTION_ARGS)
  {
- 	Relation	index = (Relation) PG_GETARG_POINTER(0);
- 
- 	/* int			i = PG_GETARG_INT32(1); */
  	SpGistCache *cache;
  
  	/* We can do it if the opclass config function says so */
  	cache = spgGetCache(index);
  
! 	PG_RETURN_BOOL(cache->config.canReturnData);
  }
--- 636,651 ----
  			break;				/* must have completed scan */
  	}
  
! 	return false;
  }
  
! bool
! spgcanreturn(Relation index, int attno)
  {
  	SpGistCache *cache;
  
  	/* We can do it if the opclass config function says so */
  	cache = spgGetCache(index);
  
! 	return cache->config.canReturnData;
  }
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
new file mode 100644
index f38287c..09c679b
*** a/src/backend/access/spgist/spgutils.c
--- b/src/backend/access/spgist/spgutils.c
***************
*** 24,30 ****
--- 24,126 ----
  #include "storage/indexfsm.h"
  #include "storage/lmgr.h"
  #include "utils/lsyscache.h"
+ #include "utils/selfuncs.h"
+ 
+ 
+ /*
+  * SP-GiST handler function: return IndexAmRoutine with access method parameters
+  * and callbacks.
+  */
+ Datum
+ spghandler(PG_FUNCTION_ARGS)
+ {
+ 	IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
+ 
+ 	amroutine->amstrategies = 0;
+ 	amroutine->amsupport = 5;
+ 	amroutine->amcanorder = false;
+ 	amroutine->amcanorderbyop = false;
+ 	amroutine->amcanbackward = false;
+ 	amroutine->amcanunique = false;
+ 	amroutine->amcanmulticol = false;
+ 	amroutine->amoptionalkey = true;
+ 	amroutine->amsearcharray = false;
+ 	amroutine->amsearchnulls = true;
+ 	amroutine->amstorage = false;
+ 	amroutine->amclusterable = false;
+ 	amroutine->ampredlocks = false;
+ 	amroutine->amkeytype = 0;
+ 
+ 	amroutine->aminsert = spginsert;
+ 	amroutine->ambeginscan = spgbeginscan;
+ 	amroutine->amgettuple = spggettuple;
+ 	amroutine->amgetbitmap = spggetbitmap;
+ 	amroutine->amrescan = spgrescan;
+ 	amroutine->amendscan = spgendscan;
+ 	amroutine->ammarkpos = spgmarkpos;
+ 	amroutine->amrestrpos = spgrestrpos;
+ 	amroutine->ambuild = spgbuild;
+ 	amroutine->ambuildempty = spgbuildempty;
+ 	amroutine->ambulkdelete = spgbulkdelete;
+ 	amroutine->amvacuumcleanup = spgvacuumcleanup;
+ 	amroutine->amcanreturn = spgcanreturn;
+ 	amroutine->amcostestimate = spgcostestimate;
+ 	amroutine->amoptions = spgoptions;
+ 	amroutine->amvalidate = spgvalidate;
+ 
+ 	PG_RETURN_POINTER(amroutine);
+ }
+ 
+ void
+ spgvalidate(OpClassInfo *opclass)
+ {
+ 	ListCell   *l;
+ 	bool		procsPresent[SPGISTNProc];
+ 	int			i;
  
+ 	memset(procsPresent, 0, sizeof(procsPresent));
+ 
+ 	foreach(l, opclass->procedures)
+ 	{
+ 		OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (proc->lefttype != opclass->intype ||
+ 			proc->righttype != opclass->intype)
+ 			continue;
+ 
+ 		if (proc->number < 1 || proc->number > SPGISTNProc)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("invalid support number for SP-GiST: %u",
+ 							proc->number)));
+ 
+ 		if (procsPresent[proc->number - 1])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("SP-GiST support number %u was defined more than once",
+ 							proc->number)));
+ 
+ 		procsPresent[proc->number - 1] = true;
+ 	}
+ 
+ 	for (i = 0; i < SPGISTNProc; i++)
+ 	{
+ 		if (!procsPresent[i])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("SP-GiST support number %u is required", i + 1)));
+ 	}
+ 
+ 	foreach(l, opclass->operators)
+ 	{
+ 		OpFamilyMember *opr = (OpFamilyMember *) lfirst(l);
+ 
+ 		if (OidIsValid(opr->sortfamily))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ 					 errmsg("SP-GiST doesn't support ORDER BY operators")));
+ 	}
+ }
  
  /* Fill in a SpGistTypeDesc struct with info about the specified data type */
  static void
*************** SpGistInitMetapage(Page page)
*** 489,506 ****
  /*
   * reloptions processing for SPGiST
   */
! Datum
! spgoptions(PG_FUNCTION_ARGS)
  {
- 	Datum		reloptions = PG_GETARG_DATUM(0);
- 	bool		validate = PG_GETARG_BOOL(1);
  	bytea	   *result;
  
  	result = default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
  
  	if (result)
! 		PG_RETURN_BYTEA_P(result);
! 	PG_RETURN_NULL();
  }
  
  /*
--- 585,600 ----
  /*
   * reloptions processing for SPGiST
   */
! bytea *
! spgoptions(Datum reloptions, bool validate)
  {
  	bytea	   *result;
  
  	result = default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
  
  	if (result)
! 		return (bytea *)result;
! 	return NULL;
  }
  
  /*
diff --git a/src/backend/access/spgist/spgvacuum.c b/src/backend/access/spgist/spgvacuum.c
new file mode 100644
index 06c0b0a..c3856a1
*** a/src/backend/access/spgist/spgvacuum.c
--- b/src/backend/access/spgist/spgvacuum.c
*************** spgvacuumscan(spgBulkDeleteState *bds)
*** 881,893 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! spgbulkdelete(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
- 	IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
- 	void	   *callback_state = (void *) PG_GETARG_POINTER(3);
  	spgBulkDeleteState bds;
  
  	/* allocate stats if first time through, else re-use existing struct */
--- 881,890 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
! 			  IndexBulkDeleteCallback callback, void *callback_state)
  {
  	spgBulkDeleteState bds;
  
  	/* allocate stats if first time through, else re-use existing struct */
*************** spgbulkdelete(PG_FUNCTION_ARGS)
*** 900,906 ****
  
  	spgvacuumscan(&bds);
  
! 	PG_RETURN_POINTER(stats);
  }
  
  /* Dummy callback to delete no tuples during spgvacuumcleanup */
--- 897,903 ----
  
  	spgvacuumscan(&bds);
  
! 	return stats;
  }
  
  /* Dummy callback to delete no tuples during spgvacuumcleanup */
*************** dummy_callback(ItemPointer itemptr, void
*** 915,931 ****
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! Datum
! spgvacuumcleanup(PG_FUNCTION_ARGS)
  {
- 	IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
- 	IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
  	Relation	index = info->index;
  	spgBulkDeleteState bds;
  
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		PG_RETURN_POINTER(stats);
  
  	/*
  	 * We don't need to scan the index if there was a preceding bulkdelete
--- 912,926 ----
   *
   * Result: a palloc'd struct containing statistical info for VACUUM displays.
   */
! IndexBulkDeleteResult *
! spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
  {
  	Relation	index = info->index;
  	spgBulkDeleteState bds;
  
  	/* No-op in ANALYZE ONLY mode */
  	if (info->analyze_only)
! 		return stats;
  
  	/*
  	 * We don't need to scan the index if there was a preceding bulkdelete
*************** spgvacuumcleanup(PG_FUNCTION_ARGS)
*** 959,963 ****
  			stats->num_index_tuples = info->num_heap_tuples;
  	}
  
! 	PG_RETURN_POINTER(stats);
  }
--- 954,958 ----
  			stats->num_index_tuples = info->num_heap_tuples;
  	}
  
! 	return stats;
  }
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
new file mode 100644
index e59b163..4e21304
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 23,28 ****
--- 23,29 ----
  
  #include <unistd.h>
  
+ #include "access/amapi.h"
  #include "access/multixact.h"
  #include "access/relscan.h"
  #include "access/sysattr.h"
*************** ConstructTupleDescriptor(Relation heapRe
*** 277,296 ****
  	int			numatts = indexInfo->ii_NumIndexAttrs;
  	ListCell   *colnames_item = list_head(indexColNames);
  	ListCell   *indexpr_item = list_head(indexInfo->ii_Expressions);
! 	HeapTuple	amtuple;
! 	Form_pg_am	amform;
  	TupleDesc	heapTupDesc;
  	TupleDesc	indexTupDesc;
  	int			natts;			/* #atts in heap rel --- for error checks */
  	int			i;
  
  	/* We need access to the index AM's pg_am tuple */
! 	amtuple = SearchSysCache1(AMOID,
! 							  ObjectIdGetDatum(accessMethodObjectId));
! 	if (!HeapTupleIsValid(amtuple))
! 		elog(ERROR, "cache lookup failed for access method %u",
! 			 accessMethodObjectId);
! 	amform = (Form_pg_am) GETSTRUCT(amtuple);
  
  	/* ... and to the table's tuple descriptor */
  	heapTupDesc = RelationGetDescr(heapRelation);
--- 278,291 ----
  	int			numatts = indexInfo->ii_NumIndexAttrs;
  	ListCell   *colnames_item = list_head(indexColNames);
  	ListCell   *indexpr_item = list_head(indexInfo->ii_Expressions);
! 	IndexAmRoutine  *amroutine;
  	TupleDesc	heapTupDesc;
  	TupleDesc	indexTupDesc;
  	int			natts;			/* #atts in heap rel --- for error checks */
  	int			i;
  
  	/* We need access to the index AM's pg_am tuple */
! 	amroutine = GetIndexAmRoutineByAmId(accessMethodObjectId);
  
  	/* ... and to the table's tuple descriptor */
  	heapTupDesc = RelationGetDescr(heapRelation);
*************** ConstructTupleDescriptor(Relation heapRe
*** 437,443 ****
  		if (OidIsValid(opclassTup->opckeytype))
  			keyType = opclassTup->opckeytype;
  		else
! 			keyType = amform->amkeytype;
  		ReleaseSysCache(tuple);
  
  		if (OidIsValid(keyType) && keyType != to->atttypid)
--- 432,438 ----
  		if (OidIsValid(opclassTup->opckeytype))
  			keyType = opclassTup->opckeytype;
  		else
! 			keyType = amroutine->amkeytype;
  		ReleaseSysCache(tuple);
  
  		if (OidIsValid(keyType) && keyType != to->atttypid)
*************** ConstructTupleDescriptor(Relation heapRe
*** 459,466 ****
  		}
  	}
  
- 	ReleaseSysCache(amtuple);
- 
  	return indexTupDesc;
  }
  
--- 454,459 ----
*************** index_build(Relation heapRelation,
*** 1988,1994 ****
  			bool isprimary,
  			bool isreindex)
  {
- 	RegProcedure procedure;
  	IndexBuildResult *stats;
  	Oid			save_userid;
  	int			save_sec_context;
--- 1981,1986 ----
*************** index_build(Relation heapRelation,
*** 1998,2007 ****
  	 * sanity checks
  	 */
  	Assert(RelationIsValid(indexRelation));
! 	Assert(PointerIsValid(indexRelation->rd_am));
! 
! 	procedure = indexRelation->rd_am->ambuild;
! 	Assert(RegProcedureIsValid(procedure));
  
  	ereport(DEBUG1,
  			(errmsg("building index \"%s\" on table \"%s\"",
--- 1990,1997 ----
  	 * sanity checks
  	 */
  	Assert(RelationIsValid(indexRelation));
! 	Assert(PointerIsValid(indexRelation->amroutine));
! 	Assert(PointerIsValid(indexRelation->amroutine->ambuild));
  
  	ereport(DEBUG1,
  			(errmsg("building index \"%s\" on table \"%s\"",
*************** index_build(Relation heapRelation,
*** 2021,2031 ****
  	/*
  	 * Call the access method's build procedure
  	 */
! 	stats = (IndexBuildResult *)
! 		DatumGetPointer(OidFunctionCall3(procedure,
! 										 PointerGetDatum(heapRelation),
! 										 PointerGetDatum(indexRelation),
! 										 PointerGetDatum(indexInfo)));
  	Assert(PointerIsValid(stats));
  
  	/*
--- 2011,2018 ----
  	/*
  	 * Call the access method's build procedure
  	 */
! 	stats = indexRelation->amroutine->ambuild(heapRelation, indexRelation,
! 											  indexInfo);
  	Assert(PointerIsValid(stats));
  
  	/*
*************** index_build(Relation heapRelation,
*** 2038,2048 ****
  	if (indexRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
  		!smgrexists(indexRelation->rd_smgr, INIT_FORKNUM))
  	{
- 		RegProcedure ambuildempty = indexRelation->rd_am->ambuildempty;
- 
  		RelationOpenSmgr(indexRelation);
  		smgrcreate(indexRelation->rd_smgr, INIT_FORKNUM, false);
! 		OidFunctionCall1(ambuildempty, PointerGetDatum(indexRelation));
  	}
  
  	/*
--- 2025,2033 ----
  	if (indexRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
  		!smgrexists(indexRelation->rd_smgr, INIT_FORKNUM))
  	{
  		RelationOpenSmgr(indexRelation);
  		smgrcreate(indexRelation->rd_smgr, INIT_FORKNUM, false);
! 		indexRelation->amroutine->ambuildempty(indexRelation);
  	}
  
  	/*
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
new file mode 100644
index 7ab4874..7a7fd95
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 17,22 ****
--- 17,23 ----
   */
  #include "postgres.h"
  
+ #include "access/amapi.h"
  #include "access/multixact.h"
  #include "access/relscan.h"
  #include "access/rewriteheap.h"
*************** check_index_is_clusterable(Relation OldH
*** 433,439 ****
  						RelationGetRelationName(OldHeap))));
  
  	/* Index AM must allow clustering */
! 	if (!OldIndex->rd_am->amclusterable)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
--- 434,440 ----
  						RelationGetRelationName(OldHeap))));
  
  	/* Index AM must allow clustering */
! 	if (!OldIndex->amroutine->amclusterable)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("cannot cluster on index \"%s\" because access method does not support clustering",
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
new file mode 100644
index b450bcf..b794d59
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
***************
*** 15,20 ****
--- 15,21 ----
  
  #include "postgres.h"
  
+ #include "access/amapi.h"
  #include "access/htup_details.h"
  #include "access/reloptions.h"
  #include "access/xact.h"
*************** CheckIndexCompatible(Oid oldId,
*** 125,130 ****
--- 126,132 ----
  	HeapTuple	tuple;
  	Form_pg_index indexForm;
  	Form_pg_am	accessMethodForm;
+ 	IndexAmRoutine *amRoutine;
  	bool		amcanorder;
  	int16	   *coloptions;
  	IndexInfo  *indexInfo;
*************** CheckIndexCompatible(Oid oldId,
*** 160,166 ****
  						accessMethodName)));
  	accessMethodId = HeapTupleGetOid(tuple);
  	accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
! 	amcanorder = accessMethodForm->amcanorder;
  	ReleaseSysCache(tuple);
  
  	/*
--- 162,169 ----
  						accessMethodName)));
  	accessMethodId = HeapTupleGetOid(tuple);
  	accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
! 	amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
! 	amcanorder = amRoutine->amcanorder;
  	ReleaseSysCache(tuple);
  
  	/*
*************** DefineIndex(Oid relationId,
*** 315,322 ****
  	Relation	indexRelation;
  	HeapTuple	tuple;
  	Form_pg_am	accessMethodForm;
  	bool		amcanorder;
! 	RegProcedure amoptions;
  	Datum		reloptions;
  	int16	   *coloptions;
  	IndexInfo  *indexInfo;
--- 318,326 ----
  	Relation	indexRelation;
  	HeapTuple	tuple;
  	Form_pg_am	accessMethodForm;
+ 	IndexAmRoutine *amRoutine;
  	bool		amcanorder;
! 	amoptions_function amoptions;
  	Datum		reloptions;
  	int16	   *coloptions;
  	IndexInfo  *indexInfo;
*************** DefineIndex(Oid relationId,
*** 489,518 ****
  	}
  	accessMethodId = HeapTupleGetOid(tuple);
  	accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
  
  	if (strcmp(accessMethodName, "hash") == 0 &&
  		RelationNeedsWAL(rel))
  		ereport(WARNING,
  				(errmsg("hash indexes are not WAL-logged and their use is discouraged")));
  
! 	if (stmt->unique && !accessMethodForm->amcanunique)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			   errmsg("access method \"%s\" does not support unique indexes",
  					  accessMethodName)));
! 	if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  		  errmsg("access method \"%s\" does not support multicolumn indexes",
  				 accessMethodName)));
! 	if (stmt->excludeOpNames && !OidIsValid(accessMethodForm->amgettuple))
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  		errmsg("access method \"%s\" does not support exclusion constraints",
  			   accessMethodName)));
  
! 	amcanorder = accessMethodForm->amcanorder;
! 	amoptions = accessMethodForm->amoptions;
  
  	ReleaseSysCache(tuple);
  
--- 493,523 ----
  	}
  	accessMethodId = HeapTupleGetOid(tuple);
  	accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
+ 	amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
  
  	if (strcmp(accessMethodName, "hash") == 0 &&
  		RelationNeedsWAL(rel))
  		ereport(WARNING,
  				(errmsg("hash indexes are not WAL-logged and their use is discouraged")));
  
! 	if (stmt->unique && !amRoutine->amcanunique)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  			   errmsg("access method \"%s\" does not support unique indexes",
  					  accessMethodName)));
! 	if (numberOfAttributes > 1 && !amRoutine->amcanmulticol)
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  		  errmsg("access method \"%s\" does not support multicolumn indexes",
  				 accessMethodName)));
! 	if (stmt->excludeOpNames && !OidIsValid(amRoutine->amgettuple))
  		ereport(ERROR,
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  		errmsg("access method \"%s\" does not support exclusion constraints",
  			   accessMethodName)));
  
! 	amcanorder = amRoutine->amcanorder;
! 	amoptions = amRoutine->amoptions;
  
  	ReleaseSysCache(tuple);
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 3375f10..4319dd7
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
***************
*** 42,47 ****
--- 42,48 ----
  #include "parser/parse_oper.h"
  #include "parser/parse_type.h"
  #include "utils/builtins.h"
+ #include "utils/catcache.h"
  #include "utils/fmgroids.h"
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 334,346 ****
  	ListCell   *l;
  	Relation	rel;
  	HeapTuple	tup;
- 	Form_pg_am	pg_am;
  	Datum		values[Natts_pg_opclass];
  	bool		nulls[Natts_pg_opclass];
  	AclResult	aclresult;
  	NameData	opcName;
  	ObjectAddress myself,
  				referenced;
  
  	/* Convert list of names to a name and namespace */
  	namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
--- 335,348 ----
  	ListCell   *l;
  	Relation	rel;
  	HeapTuple	tup;
  	Datum		values[Natts_pg_opclass];
  	bool		nulls[Natts_pg_opclass];
  	AclResult	aclresult;
  	NameData	opcName;
  	ObjectAddress myself,
  				referenced;
+ 	IndexAmRoutine *amroutine;
+ 	OpClassInfo	opclassinfo;
  
  	/* Convert list of names to a name and namespace */
  	namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 361,373 ****
  						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;
! 	amstorage = pg_am->amstorage;
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 363,377 ----
  						stmt->amname)));
  
  	amoid = HeapTupleGetOid(tup);
! 
! 	amroutine = GetIndexAmRoutineByAmId(amoid);
! 
! 	maxOpNumber = amroutine->amstrategies;
  	/* if amstrategies is zero, just enforce that op numbers fit in int16 */
  	if (maxOpNumber <= 0)
  		maxOpNumber = SHRT_MAX;
! 	maxProcNumber = amroutine->amsupport;
! 	amstorage = amroutine->amstorage;
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 582,587 ****
--- 586,597 ----
  							stmt->amname)));
  	}
  
+ 	opclassinfo.intype = typeoid;
+ 	opclassinfo.keytype = storageoid;
+ 	opclassinfo.procedures = procedures;
+ 	opclassinfo.operators = operators;
+ 	amroutine->amvalidate(&opclassinfo);
+ 
  	rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
  
  	/*
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 718,723 ****
--- 728,816 ----
  	return myself;
  }
  
+ /*
+  * Collect opclass information for validation.
+  */
+ static void
+ getOpClassInfo(OpClassInfo *opclassinfo, Oid opclassoid)
+ {
+ 	HeapTuple		classtup;
+ 	Form_pg_opclass	classform;
+ 	Oid				opfamilyoid;
+ 	CatCList	   *catlist;
+ 	int				i;
+ 
+ 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+ 
+ 	if (!HeapTupleIsValid(classtup))
+ 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+ 
+ 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+ 	opclassinfo->intype = classform->opcintype;
+ 	opclassinfo->keytype = classform->opckeytype;
+ 	opclassinfo->amoid = classform->opcmethod;
+ 	opfamilyoid = classform->opcfamily;
+ 
+ 	ReleaseSysCache(classtup);
+ 
+ 	opclassinfo->operators = NIL;
+ 	opclassinfo->procedures = NIL;
+ 
+ 	/* Find operators */
+ 	catlist = SearchSysCacheList1(AMOPSTRATEGY, opfamilyoid);
+ 	for (i = 0; i < catlist->n_members; i++)
+ 	{
+ 		HeapTuple		oprtup = &catlist->members[i]->tuple;
+ 		Form_pg_amop	oprform = (Form_pg_amop) GETSTRUCT(oprtup);
+ 		OpFamilyMember *opmember = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+ 
+ 		opmember->object = oprform->amopopr;
+ 		opmember->number = oprform->amopstrategy;
+ 		opmember->lefttype = oprform->amoplefttype;
+ 		opmember->righttype = oprform->amoprighttype;
+ 		opmember->sortfamily = oprform->amopsortfamily;
+ 
+ 		opclassinfo->operators = lappend(opclassinfo->operators, opmember);
+ 	}
+ 	ReleaseCatCacheList(catlist);
+ 
+ 	/* Find support functions */
+ 	catlist = SearchSysCacheList1(AMPROCNUM, opfamilyoid);
+ 	for (i = 0; i < catlist->n_members; i++)
+ 	{
+ 		HeapTuple		proctup = &catlist->members[i]->tuple;
+ 		Form_pg_amproc	procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ 		OpFamilyMember *procmember = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
+ 
+ 		procmember->object = procform->amproc;
+ 		procmember->number = procform->amprocnum;
+ 		procmember->lefttype = procform->amproclefttype;
+ 		procmember->righttype = procform->amprocrighttype;
+ 
+ 		opclassinfo->procedures = lappend(opclassinfo->procedures, procmember);
+ 	}
+ 	ReleaseCatCacheList(catlist);
+ }
+ 
+ /*
+  * Ask access method to validate given opclass.
+  */
+ Datum
+ amvalidate(PG_FUNCTION_ARGS)
+ {
+ 	OpClassInfo			opclassinfo;
+ 	Oid					opclassoid = PG_GETARG_OID(0);
+ 	IndexAmRoutine	   *amroutine;
+ 
+ 	getOpClassInfo(&opclassinfo, opclassoid);
+ 
+ 	amroutine = GetIndexAmRoutineByAmId(opclassinfo.amoid);
+ 
+ 	amroutine->amvalidate(&opclassinfo);
+ 
+ 	PG_RETURN_BOOL(true);
+ }
+ 
  
  /*
   * DefineOpFamily
*************** AlterOpFamily(AlterOpFamilyStmt *stmt)
*** 776,782 ****
  	int			maxOpNumber,	/* amstrategies value */
  				maxProcNumber;	/* amsupport value */
  	HeapTuple	tup;
! 	Form_pg_am	pg_am;
  
  	/* Get necessary info about access method */
  	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
--- 869,875 ----
  	int			maxOpNumber,	/* amstrategies value */
  				maxProcNumber;	/* amsupport value */
  	HeapTuple	tup;
! 	IndexAmRoutine *amroutine;
  
  	/* Get necessary info about access method */
  	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
*************** AlterOpFamily(AlterOpFamilyStmt *stmt)
*** 787,802 ****
  						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 */
  	opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
--- 880,895 ----
  						stmt->amname)));
  
  	amoid = HeapTupleGetOid(tup);
! 	amroutine = GetIndexAmRoutineByAmId(amoid);
! 	ReleaseSysCache(tup);
! 	maxOpNumber = amroutine->amstrategies;
  	/* if amstrategies is zero, just enforce that op numbers fit in int16 */
  	if (maxOpNumber <= 0)
  		maxOpNumber = SHRT_MAX;
! 	maxProcNumber = amroutine->amsupport;
  
  	/* XXX Should we make any privilege check against the AM? */
  
  
  	/* Look up the opfamily */
  	opfamilyoid = get_opfamily_oid(amoid, stmt->opfamilyname, false);
*************** assignOperTypes(OpFamilyMember *member, 
*** 1099,1119 ****
  		 * the family has been created but not yet populated with the required
  		 * operators.)
  		 */
! 		HeapTuple	amtup;
! 		Form_pg_am	pg_am;
  
! 		amtup = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
! 		if (amtup == NULL)
! 			elog(ERROR, "cache lookup failed for access method %u", amoid);
! 		pg_am = (Form_pg_am) GETSTRUCT(amtup);
  
! 		if (!pg_am->amcanorderbyop)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  			errmsg("access method \"%s\" does not support ordering operators",
! 				   NameStr(pg_am->amname))));
! 
! 		ReleaseSysCache(amtup);
  	}
  	else
  	{
--- 1192,1206 ----
  		 * the family has been created but not yet populated with the required
  		 * operators.)
  		 */
! 		IndexAmRoutine *amroutine;
  
! 		amroutine = GetIndexAmRoutineByAmId(amoid);
  
! 		if (!amroutine->amcanorderbyop)
  			ereport(ERROR,
  					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  			errmsg("access method \"%s\" does not support ordering operators",
! 				   get_am_name(amoid))));
  	}
  	else
  	{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index 7668c9d..36b6403
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATExecSetRelOptions(Relation rel, List *
*** 9374,9380 ****
  			(void) view_reloptions(newOptions, true);
  			break;
  		case RELKIND_INDEX:
! 			(void) index_reloptions(rel->rd_am->amoptions, newOptions, true);
  			break;
  		default:
  			ereport(ERROR,
--- 9374,9380 ----
  			(void) view_reloptions(newOptions, true);
  			break;
  		case RELKIND_INDEX:
! 			(void) index_reloptions(rel->amroutine->amoptions, newOptions, true);
  			break;
  		default:
  			ereport(ERROR,
*************** ATExecReplicaIdentity(Relation rel, Repl
*** 10965,10971 ****
  						RelationGetRelationName(indexRel),
  						RelationGetRelationName(rel))));
  	/* The AM must support uniqueness, and the index must in fact be unique. */
! 	if (!indexRel->rd_am->amcanunique || !indexRel->rd_index->indisunique)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  			 errmsg("cannot use non-unique index \"%s\" as replica identity",
--- 10965,10971 ----
  						RelationGetRelationName(indexRel),
  						RelationGetRelationName(rel))));
  	/* The AM must support uniqueness, and the index must in fact be unique. */
! 	if (!indexRel->amroutine->amcanunique || !indexRel->rd_index->indisunique)
  		ereport(ERROR,
  				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  			 errmsg("cannot use non-unique index \"%s\" as replica identity",
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
new file mode 100644
index 163650c..78695e0
*** a/src/backend/executor/execAmi.c
--- b/src/backend/executor/execAmi.c
***************
*** 12,17 ****
--- 12,18 ----
   */
  #include "postgres.h"
  
+ #include "access/amapi.h"
  #include "access/htup_details.h"
  #include "executor/execdebug.h"
  #include "executor/nodeAgg.h"
*************** IndexSupportsBackwardScan(Oid indexid)
*** 536,541 ****
--- 537,543 ----
  	HeapTuple	ht_am;
  	Form_pg_class idxrelrec;
  	Form_pg_am	amrec;
+ 	IndexAmRoutine *amroutine;
  
  	/* Fetch the pg_class tuple of the index relation */
  	ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
*************** IndexSupportsBackwardScan(Oid indexid)
*** 549,557 ****
  		elog(ERROR, "cache lookup failed for access method %u",
  			 idxrelrec->relam);
  	amrec = (Form_pg_am) GETSTRUCT(ht_am);
  
! 	result = amrec->amcanbackward;
  
  	ReleaseSysCache(ht_idxrel);
  	ReleaseSysCache(ht_am);
  
--- 551,561 ----
  		elog(ERROR, "cache lookup failed for access method %u",
  			 idxrelrec->relam);
  	amrec = (Form_pg_am) GETSTRUCT(ht_am);
+ 	amroutine = GetIndexAmRoutine(amrec->amhandler);
  
! 	result = amroutine->amcanbackward;
  
+ 	pfree(amroutine);
  	ReleaseSysCache(ht_idxrel);
  	ReleaseSysCache(ht_am);
  
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
new file mode 100644
index c0f14db..2de0579
*** a/src/backend/executor/nodeIndexscan.c
--- b/src/backend/executor/nodeIndexscan.c
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 1436,1442 ****
  
  			Assert(rightop != NULL);
  
! 			if (index->rd_am->amsearcharray)
  			{
  				/* Index AM will handle this like a simple operator */
  				flags |= SK_SEARCHARRAY;
--- 1436,1442 ----
  
  			Assert(rightop != NULL);
  
! 			if (index->amroutine->amsearcharray)
  			{
  				/* Index AM will handle this like a simple operator */
  				flags |= SK_SEARCHARRAY;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
new file mode 100644
index 1b61fd9..3fa37e2
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_index(IndexPath *path, PlannerInfo 
*** 406,419 ****
  	 * the fraction of main-table tuples we will have to retrieve) and its
  	 * correlation to the main-table tuple order.
  	 */
! 	OidFunctionCall7(index->amcostestimate,
! 					 PointerGetDatum(root),
! 					 PointerGetDatum(path),
! 					 Float8GetDatum(loop_count),
! 					 PointerGetDatum(&indexStartupCost),
! 					 PointerGetDatum(&indexTotalCost),
! 					 PointerGetDatum(&indexSelectivity),
! 					 PointerGetDatum(&indexCorrelation));
  
  	/*
  	 * Save amcostestimate's results for possible use in bitmap scan planning.
--- 406,413 ----
  	 * the fraction of main-table tuples we will have to retrieve) and its
  	 * correlation to the main-table tuple order.
  	 */
! 	index->amroutine->amcostestimate(root, path, loop_count, &indexStartupCost,
! 						  &indexTotalCost, &indexSelectivity, &indexCorrelation);
  
  	/*
  	 * Save amcostestimate's results for possible use in bitmap scan planning.
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
new file mode 100644
index 9442e5f..d3d5d43
*** a/src/backend/optimizer/util/plancat.c
--- b/src/backend/optimizer/util/plancat.c
*************** get_relation_info(PlannerInfo *root, Oid
*** 223,235 ****
  			}
  
  			info->relam = indexRelation->rd_rel->relam;
! 			info->amcostestimate = indexRelation->rd_am->amcostestimate;
! 			info->amcanorderbyop = indexRelation->rd_am->amcanorderbyop;
! 			info->amoptionalkey = indexRelation->rd_am->amoptionalkey;
! 			info->amsearcharray = indexRelation->rd_am->amsearcharray;
! 			info->amsearchnulls = indexRelation->rd_am->amsearchnulls;
! 			info->amhasgettuple = OidIsValid(indexRelation->rd_am->amgettuple);
! 			info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
  
  			/*
  			 * Fetch the ordering information for the index, if any.
--- 223,235 ----
  			}
  
  			info->relam = indexRelation->rd_rel->relam;
! 			info->amroutine = indexRelation->amroutine;
! 			info->amcanorderbyop = indexRelation->amroutine->amcanorderbyop;
! 			info->amoptionalkey = indexRelation->amroutine->amoptionalkey;
! 			info->amsearcharray = indexRelation->amroutine->amsearcharray;
! 			info->amsearchnulls = indexRelation->amroutine->amsearchnulls;
! 			info->amhasgettuple = OidIsValid(indexRelation->amroutine->amgettuple);
! 			info->amhasgetbitmap = OidIsValid(indexRelation->amroutine->amgetbitmap);
  
  			/*
  			 * Fetch the ordering information for the index, if any.
*************** get_relation_info(PlannerInfo *root, Oid
*** 240,246 ****
  				 * If it's a btree index, we can use its opfamily OIDs
  				 * directly as the sort ordering opfamily OIDs.
  				 */
! 				Assert(indexRelation->rd_am->amcanorder);
  
  				info->sortopfamily = info->opfamily;
  				info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
--- 240,246 ----
  				 * If it's a btree index, we can use its opfamily OIDs
  				 * directly as the sort ordering opfamily OIDs.
  				 */
! 				Assert(indexRelation->amroutine->amcanorder);
  
  				info->sortopfamily = info->opfamily;
  				info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
*************** get_relation_info(PlannerInfo *root, Oid
*** 254,260 ****
  					info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
  				}
  			}
! 			else if (indexRelation->rd_am->amcanorder)
  			{
  				/*
  				 * Otherwise, identify the corresponding btree opfamilies by
--- 254,260 ----
  					info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
  				}
  			}
! 			else if (indexRelation->amroutine->amcanorder)
  			{
  				/*
  				 * Otherwise, identify the corresponding btree opfamilies by
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index 16d40c7..0031511
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
***************
*** 26,31 ****
--- 26,32 ----
  
  #include "postgres.h"
  
+ #include "access/amapi.h"
  #include "access/htup_details.h"
  #include "access/reloptions.h"
  #include "catalog/dependency.h"
*************** generateClonedIndexStmt(CreateStmtContex
*** 1258,1264 ****
  		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
  
  		/* Adjust options if necessary */
! 		if (amrec->amcanorder)
  		{
  			/*
  			 * If it supports sort ordering, copy DESC and NULLS opts. Don't
--- 1259,1265 ----
  		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
  
  		/* Adjust options if necessary */
! 		if (source_idx->amroutine->amcanorder)
  		{
  			/*
  			 * If it supports sort ordering, copy DESC and NULLS opts. Don't
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
new file mode 100644
index 94e748e..63d6f0d
*** a/src/backend/postmaster/autovacuum.c
--- b/src/backend/postmaster/autovacuum.c
*************** extract_autovac_opts(HeapTuple tup, Tupl
*** 2420,2426 ****
  		   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
  		   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
  
! 	relopts = extractRelOptions(tup, pg_class_desc, InvalidOid);
  	if (relopts == NULL)
  		return NULL;
  
--- 2420,2426 ----
  		   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
  		   ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);
  
! 	relopts = extractRelOptions(tup, pg_class_desc, NULL);
  	if (relopts == NULL)
  		return NULL;
  
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
new file mode 100644
index 5b809aa..bdd451d
*** a/src/backend/utils/adt/pseudotypes.c
--- b/src/backend/utils/adt/pseudotypes.c
*************** tsm_handler_out(PG_FUNCTION_ARGS)
*** 401,406 ****
--- 401,433 ----
  
  
  /*
+  * index_am_handler_in		- input routine for pseudo-type INDEX_AM_HANDLER.
+  */
+ Datum
+ index_am_handler_in(PG_FUNCTION_ARGS)
+ {
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 			 errmsg("cannot accept a value of type index_am_handler")));
+ 
+ 	PG_RETURN_VOID();			/* keep compiler quiet */
+ }
+ 
+ /*
+  * index_am_handler_out		- output routine for pseudo-type INDEX_AM_HANDLER.
+  */
+ Datum
+ index_am_handler_out(PG_FUNCTION_ARGS)
+ {
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 			 errmsg("cannot display a value of type index_am_handler")));
+ 
+ 	PG_RETURN_VOID();			/* keep compiler quiet */
+ }
+ 
+ 
+ /*
   * internal_in		- input routine for pseudo-type INTERNAL.
   */
  Datum
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
new file mode 100644
index 51391f6..06f122b
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 19,24 ****
--- 19,25 ----
  #include <unistd.h>
  #include <fcntl.h>
  
+ #include "access/amapi.h"
  #include "access/htup_details.h"
  #include "access/sysattr.h"
  #include "catalog/dependency.h"
*************** pg_get_indexdef_worker(Oid indexrelid, i
*** 1167,1172 ****
--- 1168,1174 ----
  		if (!attrsOnly && (!colno || colno == keyno + 1))
  		{
  			Oid			indcoll;
+ 			IndexAmRoutine  *amroutine;
  
  			/* Add collation, if not default for column */
  			indcoll = indcollation->values[keyno];
*************** pg_get_indexdef_worker(Oid indexrelid, i
*** 1177,1184 ****
  			/* Add the operator class name, if not default */
  			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
  
  			/* Add options if relevant */
! 			if (amrec->amcanorder)
  			{
  				/* if it supports sort ordering, report DESC and NULLS opts */
  				if (opt & INDOPTION_DESC)
--- 1179,1188 ----
  			/* Add the operator class name, if not default */
  			get_opclass_name(indclass->values[keyno], keycoltype, &buf);
  
+ 			amroutine = GetIndexAmRoutine(amrec->amhandler);
+ 
  			/* Add options if relevant */
! 			if (amroutine->amcanorder)
  			{
  				/* if it supports sort ordering, report DESC and NULLS opts */
  				if (opt & INDOPTION_DESC)
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 37fad86..9a83e10
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** add_predicate_to_quals(IndexOptInfo *ind
*** 6443,6458 ****
  }
  
  
! Datum
! btcostestimate(PG_FUNCTION_ARGS)
  {
- 	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- 	IndexPath  *path = (IndexPath *) PG_GETARG_POINTER(1);
- 	double		loop_count = PG_GETARG_FLOAT8(2);
- 	Cost	   *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- 	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- 	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- 	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(6);
  	IndexOptInfo *index = path->indexinfo;
  	List	   *qinfos;
  	GenericCosts costs;
--- 6443,6453 ----
  }
  
  
! void
! btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
! 			   Cost *indexStartupCost, Cost *indexTotalCost,
! 			   Selectivity *indexSelectivity, double *indexCorrelation)
  {
  	IndexOptInfo *index = path->indexinfo;
  	List	   *qinfos;
  	GenericCosts costs;
*************** btcostestimate(PG_FUNCTION_ARGS)
*** 6741,6760 ****
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! hashcostestimate(PG_FUNCTION_ARGS)
  {
- 	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- 	IndexPath  *path = (IndexPath *) PG_GETARG_POINTER(1);
- 	double		loop_count = PG_GETARG_FLOAT8(2);
- 	Cost	   *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- 	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- 	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- 	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(6);
  	List	   *qinfos;
  	GenericCosts costs;
  
--- 6736,6748 ----
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
  }
  
! void
! hashcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
! 				 Cost *indexStartupCost, Cost *indexTotalCost,
! 				 Selectivity *indexSelectivity, double *indexCorrelation)
  {
  	List	   *qinfos;
  	GenericCosts costs;
  
*************** hashcostestimate(PG_FUNCTION_ARGS)
*** 6794,6813 ****
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! gistcostestimate(PG_FUNCTION_ARGS)
  {
- 	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- 	IndexPath  *path = (IndexPath *) PG_GETARG_POINTER(1);
- 	double		loop_count = PG_GETARG_FLOAT8(2);
- 	Cost	   *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- 	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- 	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- 	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(6);
  	IndexOptInfo *index = path->indexinfo;
  	List	   *qinfos;
  	GenericCosts costs;
--- 6782,6794 ----
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
  }
  
! void
! gistcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
! 				 Cost *indexStartupCost, Cost *indexTotalCost,
! 				 Selectivity *indexSelectivity, double *indexCorrelation)
  {
  	IndexOptInfo *index = path->indexinfo;
  	List	   *qinfos;
  	GenericCosts costs;
*************** gistcostestimate(PG_FUNCTION_ARGS)
*** 6860,6879 ****
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
- 
- 	PG_RETURN_VOID();
  }
  
! Datum
! spgcostestimate(PG_FUNCTION_ARGS)
  {
- 	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- 	IndexPath  *path = (IndexPath *) PG_GETARG_POINTER(1);
- 	double		loop_count = PG_GETARG_FLOAT8(2);
- 	Cost	   *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- 	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- 	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- 	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(6);
  	IndexOptInfo *index = path->indexinfo;
  	List	   *qinfos;
  	GenericCosts costs;
--- 6841,6853 ----
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
  }
  
! void
! spgcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
! 				Cost *indexStartupCost, Cost *indexTotalCost,
! 				Selectivity *indexSelectivity, double *indexCorrelation)
  {
  	IndexOptInfo *index = path->indexinfo;
  	List	   *qinfos;
  	GenericCosts costs;
*************** spgcostestimate(PG_FUNCTION_ARGS)
*** 6926,6933 ****
  	*indexTotalCost = costs.indexTotalCost;
  	*indexSelectivity = costs.indexSelectivity;
  	*indexCorrelation = costs.indexCorrelation;
- 
- 	PG_RETURN_VOID();
  }
  
  
--- 6900,6905 ----
*************** gincost_scalararrayopexpr(PlannerInfo *r
*** 7222,7237 ****
  /*
   * GIN has search behavior completely different from other index types
   */
! Datum
! gincostestimate(PG_FUNCTION_ARGS)
  {
- 	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- 	IndexPath  *path = (IndexPath *) PG_GETARG_POINTER(1);
- 	double		loop_count = PG_GETARG_FLOAT8(2);
- 	Cost	   *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- 	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- 	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- 	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(6);
  	IndexOptInfo *index = path->indexinfo;
  	List	   *indexQuals = path->indexquals;
  	List	   *indexOrderBys = path->indexorderbys;
--- 7194,7204 ----
  /*
   * GIN has search behavior completely different from other index types
   */
! void
! gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
! 				Cost *indexStartupCost, Cost *indexTotalCost,
! 				Selectivity *indexSelectivity, double *indexCorrelation)
  {
  	IndexOptInfo *index = path->indexinfo;
  	List	   *indexQuals = path->indexquals;
  	List	   *indexOrderBys = path->indexorderbys;
*************** gincostestimate(PG_FUNCTION_ARGS)
*** 7382,7388 ****
  		*indexStartupCost = 0;
  		*indexTotalCost = 0;
  		*indexSelectivity = 0;
! 		PG_RETURN_VOID();
  	}
  
  	if (counts.haveFullScan || indexQuals == NIL)
--- 7349,7355 ----
  		*indexStartupCost = 0;
  		*indexTotalCost = 0;
  		*indexSelectivity = 0;
! 		return;
  	}
  
  	if (counts.haveFullScan || indexQuals == NIL)
*************** gincostestimate(PG_FUNCTION_ARGS)
*** 7504,7526 ****
  	*indexStartupCost += qual_arg_cost;
  	*indexTotalCost += qual_arg_cost;
  	*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost + qual_op_cost);
- 
- 	PG_RETURN_VOID();
  }
  
  /*
   * BRIN has search behavior completely different from other index types
   */
! Datum
! brincostestimate(PG_FUNCTION_ARGS)
  {
- 	PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
- 	IndexPath  *path = (IndexPath *) PG_GETARG_POINTER(1);
- 	double		loop_count = PG_GETARG_FLOAT8(2);
- 	Cost	   *indexStartupCost = (Cost *) PG_GETARG_POINTER(3);
- 	Cost	   *indexTotalCost = (Cost *) PG_GETARG_POINTER(4);
- 	Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(5);
- 	double	   *indexCorrelation = (double *) PG_GETARG_POINTER(6);
  	IndexOptInfo *index = path->indexinfo;
  	List	   *indexQuals = path->indexquals;
  	List	   *indexOrderBys = path->indexorderbys;
--- 7471,7486 ----
  	*indexStartupCost += qual_arg_cost;
  	*indexTotalCost += qual_arg_cost;
  	*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost + qual_op_cost);
  }
  
  /*
   * BRIN has search behavior completely different from other index types
   */
! void
! brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
! 				 Cost *indexStartupCost, Cost *indexTotalCost,
! 				 Selectivity *indexSelectivity, double *indexCorrelation)
  {
  	IndexOptInfo *index = path->indexinfo;
  	List	   *indexQuals = path->indexquals;
  	List	   *indexOrderBys = path->indexorderbys;
*************** brincostestimate(PG_FUNCTION_ARGS)
*** 7573,7578 ****
  	*indexTotalCost += (numTuples * *indexSelectivity) * (cpu_index_tuple_cost + qual_op_cost);
  
  	/* XXX what about pages_per_range? */
- 
- 	PG_RETURN_VOID();
  }
--- 7533,7536 ----
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
new file mode 100644
index 9c3d096..feed71e
*** a/src/backend/utils/cache/relcache.c
--- b/src/backend/utils/cache/relcache.c
*************** RelationParseRelOptions(Relation relatio
*** 445,451 ****
  	options = extractRelOptions(tuple,
  								GetPgClassDescriptor(),
  								relation->rd_rel->relkind == RELKIND_INDEX ?
! 								relation->rd_am->amoptions : InvalidOid);
  
  	/*
  	 * Copy parsed data into CacheMemoryContext.  To guard against the
--- 445,451 ----
  	options = extractRelOptions(tuple,
  								GetPgClassDescriptor(),
  								relation->rd_rel->relkind == RELKIND_INDEX ?
! 								relation->amroutine->amoptions : NULL);
  
  	/*
  	 * Copy parsed data into CacheMemoryContext.  To guard against the
*************** RelationInitPhysicalAddr(Relation relati
*** 1160,1172 ****
  }
  
  /*
   * Initialize index-access-method support data for an index relation
   */
  void
  RelationInitIndexAccessInfo(Relation relation)
  {
  	HeapTuple	tuple;
- 	Form_pg_am	aform;
  	Datum		indcollDatum;
  	Datum		indclassDatum;
  	Datum		indoptionDatum;
--- 1160,1237 ----
  }
  
  /*
+  * GetIndexAmRoutine - call the specified access method handler routine
+  * to get its IndexAmRoutine struct.
+  */
+ IndexAmRoutine *
+ GetIndexAmRoutine(Oid amhandler)
+ {
+ 	Datum			datum;
+ 	IndexAmRoutine *routine;
+ 
+ 	datum = OidFunctionCall0(amhandler);
+ 	routine = (IndexAmRoutine *) DatumGetPointer(datum);
+ 
+ 	if (routine == NULL || !IsA(routine, IndexAmRoutine))
+ 		elog(ERROR, "index access method handler function %u did not return an IndexAmRoutine struct",
+ 			 amhandler);
+ 
+ 	return routine;
+ }
+ 
+ /*
+  * GetIndexAmRoutine - look up the handler of the access method
+  * for the given OID, and retrieve its IndexAmRoutine struct.
+  */
+ IndexAmRoutine *
+ GetIndexAmRoutineByAmId(Oid amoid)
+ {
+ 	HeapTuple		tuple;
+ 	regproc			amhandler;
+ 	Form_pg_am		amform;
+ 
+ 	/* Get handler function OID for the access method */
+ 	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+ 	if (!HeapTupleIsValid(tuple))
+ 		elog(ERROR, "cache lookup failed for access method %u",
+ 			 amoid);
+ 	amform = (Form_pg_am) GETSTRUCT(tuple);
+ 	amhandler = amform->amhandler;
+ 
+ 	/* Complain if handler OID is invalid */
+ 	if (!RegProcedureIsValid(amhandler))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("index access method \"%s\" does not have a handler",
+ 						NameStr(amform->amname))));
+ 	ReleaseSysCache(tuple);
+ 
+ 	/* And finally, call the handler function. */
+ 	return GetIndexAmRoutine(amhandler);
+ }
+ 
+ /*
+  * Initialize IndexAmRoutine for relation.
+  */
+ void
+ InitIndexAmRoutine(Relation relation)
+ {
+ 	IndexAmRoutine	*result, *tmp;
+ 
+ 	result = (IndexAmRoutine *) MemoryContextAlloc(relation->rd_indexcxt,
+ 												sizeof(IndexAmRoutine));
+ 	tmp = GetIndexAmRoutine(relation->rd_am->amhandler);
+ 	memcpy(result, tmp, sizeof(IndexAmRoutine));
+ 	relation->amroutine = result;
+ }
+ 
+ /*
   * Initialize index-access-method support data for an index relation
   */
  void
  RelationInitIndexAccessInfo(Relation relation)
  {
  	HeapTuple	tuple;
  	Datum		indcollDatum;
  	Datum		indclassDatum;
  	Datum		indoptionDatum;
*************** RelationInitIndexAccessInfo(Relation rel
*** 1178,1183 ****
--- 1243,1249 ----
  	MemoryContext oldcontext;
  	int			natts;
  	uint16		amsupport;
+ 	Form_pg_am	aform;
  
  	/*
  	 * Make a copy of the pg_index entry for the index.  Since pg_index
*************** RelationInitIndexAccessInfo(Relation rel
*** 1211,1217 ****
  	if (natts != relation->rd_index->indnatts)
  		elog(ERROR, "relnatts disagrees with indnatts for index %u",
  			 RelationGetRelid(relation));
- 	amsupport = aform->amsupport;
  
  	/*
  	 * Make the private context to hold index access info.  The reason we need
--- 1277,1282 ----
*************** RelationInitIndexAccessInfo(Relation rel
*** 1228,1233 ****
--- 1293,1303 ----
  									 ALLOCSET_SMALL_MAXSIZE);
  	relation->rd_indexcxt = indexcxt;
  
+ 	/* Initialize index AM routine */
+ 	InitIndexAmRoutine(relation);
+ 
+ 	amsupport = relation->amroutine->amsupport;
+ 
  	/*
  	 * Allocate arrays to hold data
  	 */
*************** load_relcache_init_file(bool shared)
*** 4743,4749 ****
  			Oid		   *opfamily;
  			Oid		   *opcintype;
  			RegProcedure *support;
- 			int			nsupport;
  			int16	   *indoption;
  			Oid		   *indcollation;
  
--- 4813,4818 ----
*************** load_relcache_init_file(bool shared)
*** 4771,4776 ****
--- 4840,4846 ----
  			if (fread(am, 1, len, fp) != len)
  				goto read_failed;
  			rel->rd_am = am;
+ 			rel->amroutine = NULL;
  
  			/*
  			 * prepare index info context --- parameters should match
*************** load_relcache_init_file(bool shared)
*** 4832,4843 ****
  
  			rel->rd_indoption = indoption;
  
  			/* set up zeroed fmgr-info vectors */
  			rel->rd_aminfo = (RelationAmInfo *)
  				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
- 			nsupport = relform->relnatts * am->amsupport;
  			rel->rd_supportinfo = (FmgrInfo *)
! 				MemoryContextAllocZero(indexcxt, nsupport * sizeof(FmgrInfo));
  		}
  		else
  		{
--- 4902,4914 ----
  
  			rel->rd_indoption = indoption;
  
+ 			InitIndexAmRoutine(rel);
+ 
  			/* set up zeroed fmgr-info vectors */
  			rel->rd_aminfo = (RelationAmInfo *)
  				MemoryContextAllocZero(indexcxt, sizeof(RelationAmInfo));
  			rel->rd_supportinfo = (FmgrInfo *)
! 				MemoryContextAllocZero(indexcxt, relform->relnatts * rel->amroutine->amsupport * sizeof(FmgrInfo));
  		}
  		else
  		{
*************** write_relcache_init_file(bool shared)
*** 5106,5112 ****
  
  			/* next, write the vector of support procedure OIDs */
  			write_item(rel->rd_support,
! 				  relform->relnatts * (am->amsupport * sizeof(RegProcedure)),
  					   fp);
  
  			/* next, write the vector of collation OIDs */
--- 5177,5183 ----
  
  			/* next, write the vector of support procedure OIDs */
  			write_item(rel->rd_support,
! 				  relform->relnatts * (rel->amroutine->amsupport * sizeof(RegProcedure)),
  					   fp);
  
  			/* next, write the vector of collation OIDs */
diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h
new file mode 100644
index ...2e0257a
*** a/src/include/access/amapi.h
--- b/src/include/access/amapi.h
***************
*** 0 ****
--- 1,169 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amapi.h
+  *	  API for access methods
+  *
+  * Copyright (c) 2015, PostgreSQL Global Development Group
+  *
+  * src/include/access/amapi.h
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef AMAPI_H
+ #define AMAPI_H
+ 
+ #include "access/genam.h"
+ #include "catalog/opfam_internal.h"
+ #include "nodes/relation.h"
+ 
+ /*
+  * Callback function signatures --- see indexam.sgml for more info.
+  */
+ 
+ /* "insert this tuple" function */
+ typedef bool
+ (*aminsert_function) (Relation indexRelation,
+ 					  Datum *values,
+ 					  bool *isnull,
+ 					  ItemPointer heap_tid,
+ 					  Relation heapRelation,
+ 					  IndexUniqueCheck checkUnique);
+ 
+ /* "prepare for index scan" function */
+ typedef IndexScanDesc
+ (*ambeginscan_function) (Relation indexRelation,
+ 						 int nkeys,
+ 						 int norderbys);
+ 
+ /* "next valid tuple" function, or NULL */
+ typedef bool
+ (*amgettuple_function) (IndexScanDesc scan,
+ 						ScanDirection direction);
+ 
+ /* "fetch all valid tuples" function, or NULL */
+ typedef int64
+ (*amgetbitmap_function) (IndexScanDesc scan,
+ 						 TIDBitmap *tbm);
+ 
+ /* "(re)start index scan" function */
+ typedef void
+ (*amrescan_function) (IndexScanDesc scan,
+ 					  ScanKey keys,
+ 					  int nkeys,
+ 					  ScanKey orderbys,
+ 					  int norderbys);
+ 
+ /* "end index scan" function */
+ typedef void
+ (*amendscan_function) (IndexScanDesc scan);
+ 
+ /* "mark current scan position" function */
+ typedef void
+ (*ammarkpos_function) (IndexScanDesc scan);
+ 
+ /* "restore marked scan position" function */
+ typedef void
+ (*amrestrpos_function) (IndexScanDesc scan);
+ 
+ /* "build new index" function */
+ typedef IndexBuildResult *
+ (*ambuild_function) (Relation heapRelation,
+ 					 Relation indexRelation,
+ 					 IndexInfo *indexInfo);
+ 
+ /* "build empty index" function */
+ typedef void
+ (*ambuildempty_function) (Relation indexRelation);
+ 
+ /* bulk-delete function */
+ typedef IndexBulkDeleteResult *
+ (*ambulkdelete_function) (IndexVacuumInfo *info,
+ 						  IndexBulkDeleteResult *stats,
+ 						  IndexBulkDeleteCallback callback,
+ 						  void *callback_state);
+ 
+ /* post-VACUUM cleanup function */
+ typedef IndexBulkDeleteResult *
+ (*amvacuumcleanup_function) (IndexVacuumInfo *info,
+ 							 IndexBulkDeleteResult *stats);
+ 
+ /* can indexscan return IndexTuples? */
+ typedef bool
+ (*amcanreturn_function) (Relation indexRelation, int attno);
+ 
+ /* estimate cost of an indexscan */
+ typedef void
+ (*amcostestimate_function) (PlannerInfo *root,
+ 							IndexPath *path,
+ 							double loop_count,
+ 							Cost *indexStartupCost,
+ 							Cost *indexTotalCost,
+ 							Selectivity *indexSelectivity,
+ 							double *indexCorrelation);
+ 
+ /* parse AM-specific parameters */
+ typedef bytea *
+ (*amoptions_function) (Datum reloptions,
+ 					   bool validate);
+ 
+ /* validate oplass */
+ typedef void
+ (*amvalidate_function) (OpClassInfo *opclass);
+ 
+ 
+ struct IndexAmRoutine
+ {
+ 	NodeTag		type;
+ 
+ 	/*
+ 	 * Total number of strategies (operators) by which we can traverse/search
+ 	 * this AM. Zero if AM does not have a fixed set of strategy assignments.
+ 	 */
+ 	int16						amstrategies;
+ 	/* total number of support functions that this AM uses */
+ 	int16						amsupport;
+ 	/* does AM support order by column value? */
+ 	bool						amcanorder;
+ 	/* does AM support order by operator result? */
+ 	bool						amcanorderbyop;
+ 	/* does AM support backward scan? */
+ 	bool						amcanbackward;
+ 	/* does AM support UNIQUE indexes? */
+ 	bool						amcanunique;
+ 	/* does AM support multi-column indexes? */
+ 	bool						amcanmulticol;
+ 	/* can query omit key for the first column? */
+ 	bool						amoptionalkey;
+ 	/* can AM handle ScalarArrayOpExpr quals? */
+ 	bool						amsearcharray;
+ 	/* can AM search for NULL/NOT NULL entries? */
+ 	bool						amsearchnulls;
+ 	/* can storage type differ from column type? */
+ 	bool						amstorage;
+ 	/* does AM support cluster command? */
+ 	bool						amclusterable;
+ 	/* does AM handle predicate locks? */
+ 	bool						ampredlocks;
+ 	/* type of data in index, or InvalidOid */
+ 	Oid							amkeytype;
+ 
+ 	/* interface functions */
+ 	aminsert_function			aminsert;
+ 	ambeginscan_function		ambeginscan;
+ 	amgettuple_function			amgettuple;
+ 	amgetbitmap_function		amgetbitmap;
+ 	amrescan_function			amrescan;
+ 	amendscan_function			amendscan;
+ 	ammarkpos_function			ammarkpos;
+ 	amrestrpos_function			amrestrpos;
+ 	ambuild_function			ambuild;
+ 	ambuildempty_function		ambuildempty;
+ 	ambulkdelete_function		ambulkdelete;
+ 	amvacuumcleanup_function	amvacuumcleanup;
+ 	amcanreturn_function		amcanreturn;
+ 	amcostestimate_function		amcostestimate;
+ 	amoptions_function			amoptions;
+ 	amvalidate_function			amvalidate;
+ };
+ 
+ #endif   /* AMAPI_H */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
new file mode 100644
index e72fb2d..5ae5722
*** a/src/include/access/brin.h
--- b/src/include/access/brin.h
***************
*** 10,15 ****
--- 10,16 ----
  #ifndef BRIN_H
  #define BRIN_H
  
+ #include "access/amapi.h"
  #include "fmgr.h"
  #include "nodes/execnodes.h"
  #include "utils/relcache.h"
***************
*** 18,36 ****
  /*
   * prototypes for functions in brin.c (external entry points for BRIN)
   */
! extern Datum brinbuild(PG_FUNCTION_ARGS);
! extern Datum brinbuildempty(PG_FUNCTION_ARGS);
! extern Datum brininsert(PG_FUNCTION_ARGS);
! extern Datum brinbeginscan(PG_FUNCTION_ARGS);
! extern Datum bringetbitmap(PG_FUNCTION_ARGS);
! extern Datum brinrescan(PG_FUNCTION_ARGS);
! extern Datum brinendscan(PG_FUNCTION_ARGS);
! extern Datum brinmarkpos(PG_FUNCTION_ARGS);
! extern Datum brinrestrpos(PG_FUNCTION_ARGS);
! extern Datum brinbulkdelete(PG_FUNCTION_ARGS);
! extern Datum brinvacuumcleanup(PG_FUNCTION_ARGS);
! extern Datum brincostestimate(PG_FUNCTION_ARGS);
! extern Datum brinoptions(PG_FUNCTION_ARGS);
  
  /*
   * Storage type for BRIN's reloptions
--- 19,51 ----
  /*
   * prototypes for functions in brin.c (external entry points for BRIN)
   */
! extern Datum brinhandler(PG_FUNCTION_ARGS);
! extern void brinvalidate(OpClassInfo *opclass);
! extern IndexBuildResult *brinbuild(Relation heap, Relation index,
! 								   IndexInfo *indexInfo);
! extern void brinbuildempty(Relation index);
! extern bool brininsert(Relation idxRel, Datum *values, bool *nulls,
! 					   ItemPointer heaptid, Relation heapRel,
! 					   IndexUniqueCheck checkUnique);
! extern IndexScanDesc brinbeginscan(Relation r, int nkeys, int norderbys);
! extern int64 bringetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
! extern void brinrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 					   ScanKey orderbys, int norderbys);
! extern void brinendscan(IndexScanDesc scan);
! extern void brinmarkpos(IndexScanDesc scan);
! extern void brinrestrpos(IndexScanDesc scan);
! extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
! 											 IndexBulkDeleteResult *stats,
! 											 IndexBulkDeleteCallback callback,
! 											 void *callback_state);
! extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
! 												IndexBulkDeleteResult *stats);
! extern void brincostestimate(PlannerInfo *root, IndexPath *path,
! 							 double loop_count, Cost *indexStartupCost,
! 							 Cost *indexTotalCost,
! 							 Selectivity *indexSelectivity,
! 							 double *indexCorrelation);
! extern bytea *brinoptions(Datum reloptions, bool validate);
  
  /*
   * Storage type for BRIN's reloptions
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
new file mode 100644
index 6be199e..cd72544
*** a/src/include/access/brin_internal.h
--- b/src/include/access/brin_internal.h
*************** typedef struct BrinDesc
*** 71,76 ****
--- 71,77 ----
  #define BRIN_PROCNUM_ADDVALUE		2
  #define BRIN_PROCNUM_CONSISTENT		3
  #define BRIN_PROCNUM_UNION			4
+ #define BRIN_MANDATORY_NPROCS		4
  /* procedure numbers up to 10 are reserved for BRIN future expansion */
  
  #undef BRIN_DEBUG
diff --git a/src/include/access/genam.h b/src/include/access/genam.h
new file mode 100644
index d9d05a0..2f4ad91
*** a/src/include/access/genam.h
--- b/src/include/access/genam.h
*************** typedef enum IndexUniqueCheck
*** 111,117 ****
  	UNIQUE_CHECK_EXISTING		/* Check if existing tuple is unique */
  } IndexUniqueCheck;
  
- 
  /*
   * generalized index_ interface routines (in indexam.c)
   */
--- 111,116 ----
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
new file mode 100644
index 5021887..a77d43f
*** a/src/include/access/gin_private.h
--- b/src/include/access/gin_private.h
***************
*** 10,16 ****
  #ifndef GIN_PRIVATE_H
  #define GIN_PRIVATE_H
  
! #include "access/genam.h"
  #include "access/gin.h"
  #include "access/itup.h"
  #include "fmgr.h"
--- 10,16 ----
  #ifndef GIN_PRIVATE_H
  #define GIN_PRIVATE_H
  
! #include "access/amapi.h"
  #include "access/gin.h"
  #include "access/itup.h"
  #include "fmgr.h"
*************** typedef struct ginxlogDeleteListPages
*** 593,599 ****
  
  
  /* ginutil.c */
! extern Datum ginoptions(PG_FUNCTION_ARGS);
  extern void initGinState(GinState *state, Relation index);
  extern Buffer GinNewBuffer(Relation index);
  extern void GinInitBuffer(Buffer b, uint32 f);
--- 593,601 ----
  
  
  /* ginutil.c */
! extern Datum ginhandler(PG_FUNCTION_ARGS);
! extern void ginvalidate(OpClassInfo *opclass);
! extern bytea *ginoptions(Datum reloptions, bool validate);
  extern void initGinState(GinState *state, Relation index);
  extern Buffer GinNewBuffer(Relation index);
  extern void GinInitBuffer(Buffer b, uint32 f);
*************** extern Datum gintuple_get_key(GinState *
*** 614,622 ****
  				 GinNullCategory *category);
  
  /* gininsert.c */
! extern Datum ginbuild(PG_FUNCTION_ARGS);
! extern Datum ginbuildempty(PG_FUNCTION_ARGS);
! extern Datum gininsert(PG_FUNCTION_ARGS);
  extern void ginEntryInsert(GinState *ginstate,
  			   OffsetNumber attnum, Datum key, GinNullCategory category,
  			   ItemPointerData *items, uint32 nitem,
--- 616,627 ----
  				 GinNullCategory *category);
  
  /* gininsert.c */
! extern IndexBuildResult *ginbuild(Relation heap, Relation index,
! 								  IndexInfo *indexInfo);
! extern void ginbuildempty(Relation index);
! extern bool gininsert(Relation index, Datum *values, bool *isnull,
! 					  ItemPointer ht_ctid, Relation heapRel,
! 					  IndexUniqueCheck checkUnique);
  extern void ginEntryInsert(GinState *ginstate,
  			   OffsetNumber attnum, Datum key, GinNullCategory category,
  			   ItemPointerData *items, uint32 nitem,
*************** typedef struct GinScanOpaqueData
*** 867,889 ****
  
  typedef GinScanOpaqueData *GinScanOpaque;
  
! extern Datum ginbeginscan(PG_FUNCTION_ARGS);
! extern Datum ginendscan(PG_FUNCTION_ARGS);
! extern Datum ginrescan(PG_FUNCTION_ARGS);
! extern Datum ginmarkpos(PG_FUNCTION_ARGS);
! extern Datum ginrestrpos(PG_FUNCTION_ARGS);
  extern void ginNewScanKey(IndexScanDesc scan);
  extern void ginFreeScanKeys(GinScanOpaque so);
  
  /* ginget.c */
! extern Datum gingetbitmap(PG_FUNCTION_ARGS);
  
  /* ginlogic.c */
  extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key);
  
  /* ginvacuum.c */
! extern Datum ginbulkdelete(PG_FUNCTION_ARGS);
! extern Datum ginvacuumcleanup(PG_FUNCTION_ARGS);
  extern ItemPointer ginVacuumItemPointers(GinVacuumState *gvs,
  					  ItemPointerData *items, int nitem, int *nremaining);
  
--- 872,899 ----
  
  typedef GinScanOpaqueData *GinScanOpaque;
  
! extern IndexScanDesc ginbeginscan(Relation rel, int nkeys, int norderbys);
! extern void ginendscan(IndexScanDesc scan);
! extern void ginrescan(IndexScanDesc scan, ScanKey key, int nkeys,
! 					  ScanKey orderbys, int norderbys);
! extern void ginmarkpos(IndexScanDesc scan);
! extern void ginrestrpos(IndexScanDesc scan);
  extern void ginNewScanKey(IndexScanDesc scan);
  extern void ginFreeScanKeys(GinScanOpaque so);
  
  /* ginget.c */
! extern int64 gingetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  
  /* ginlogic.c */
  extern void ginInitConsistentFunction(GinState *ginstate, GinScanKey key);
  
  /* ginvacuum.c */
! extern IndexBulkDeleteResult *ginbulkdelete(IndexVacuumInfo *info,
! 											IndexBulkDeleteResult *stats,
! 											IndexBulkDeleteCallback callback,
! 											void *callback_state);
! extern IndexBulkDeleteResult *ginvacuumcleanup(IndexVacuumInfo *info,
! 											   IndexBulkDeleteResult *stats);
  extern ItemPointer ginVacuumItemPointers(GinVacuumState *gvs,
  					  ItemPointerData *items, int nitem, int *nremaining);
  
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
new file mode 100644
index 1a77982..35fb34a
*** a/src/include/access/gist_private.h
--- b/src/include/access/gist_private.h
***************
*** 14,19 ****
--- 14,20 ----
  #ifndef GIST_PRIVATE_H
  #define GIST_PRIVATE_H
  
+ #include "access/amapi.h"
  #include "access/gist.h"
  #include "access/itup.h"
  #include "access/xlogreader.h"
*************** typedef struct GiSTOptions
*** 426,434 ****
  } GiSTOptions;
  
  /* gist.c */
! extern Datum gistbuildempty(PG_FUNCTION_ARGS);
! extern Datum gistinsert(PG_FUNCTION_ARGS);
! extern Datum gistcanreturn(PG_FUNCTION_ARGS);
  extern MemoryContext createTempGistContext(void);
  extern GISTSTATE *initGISTstate(Relation index);
  extern void freeGISTstate(GISTSTATE *giststate);
--- 427,439 ----
  } GiSTOptions;
  
  /* gist.c */
! extern Datum gisthandler(PG_FUNCTION_ARGS);
! extern void gistvalidate(OpClassInfo *opclass);
! extern void gistbuildempty(Relation index);
! extern bool gistinsert(Relation r, Datum *values, bool *isnull,
! 					   ItemPointer ht_ctid, Relation heapRel,
! 					   IndexUniqueCheck checkUnique);
! extern bool gistcanreturn(Relation index, int attno);
  extern MemoryContext createTempGistContext(void);
  extern GISTSTATE *initGISTstate(Relation index);
  extern void freeGISTstate(GISTSTATE *giststate);
*************** extern XLogRecPtr gistXLogSplit(RelFileN
*** 474,481 ****
  			  Buffer leftchild, bool markfollowright);
  
  /* gistget.c */
! extern Datum gistgettuple(PG_FUNCTION_ARGS);
! extern Datum gistgetbitmap(PG_FUNCTION_ARGS);
  
  /* gistutil.c */
  
--- 479,486 ----
  			  Buffer leftchild, bool markfollowright);
  
  /* gistget.c */
! extern bool gistgettuple(IndexScanDesc scan, ScanDirection dir);
! extern int64 gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
  
  /* gistutil.c */
  
*************** extern Datum gistgetbitmap(PG_FUNCTION_A
*** 485,491 ****
  #define GIST_MIN_FILLFACTOR			10
  #define GIST_DEFAULT_FILLFACTOR		90
  
! extern Datum gistoptions(PG_FUNCTION_ARGS);
  extern bool gistfitpage(IndexTuple *itvec, int len);
  extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
  extern void gistcheckpage(Relation rel, Buffer buf);
--- 490,496 ----
  #define GIST_MIN_FILLFACTOR			10
  #define GIST_DEFAULT_FILLFACTOR		90
  
! extern bytea *gistoptions(Datum reloptions, bool validate);
  extern bool gistfitpage(IndexTuple *itvec, int len);
  extern bool gistnospace(Page page, IndexTuple *itvec, int len, OffsetNumber todelete, Size freespace);
  extern void gistcheckpage(Relation rel, Buffer buf);
*************** extern void gistMakeUnionKey(GISTSTATE *
*** 534,541 ****
  extern XLogRecPtr gistGetFakeLSN(Relation rel);
  
  /* gistvacuum.c */
! extern Datum gistbulkdelete(PG_FUNCTION_ARGS);
! extern Datum gistvacuumcleanup(PG_FUNCTION_ARGS);
  
  /* gistsplit.c */
  extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
--- 539,550 ----
  extern XLogRecPtr gistGetFakeLSN(Relation rel);
  
  /* gistvacuum.c */
! extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
! 											 IndexBulkDeleteResult *stats,
! 											 IndexBulkDeleteCallback callback,
! 											 void *callback_state);
! extern IndexBulkDeleteResult *gistvacuumcleanup(IndexVacuumInfo *info,
! 												IndexBulkDeleteResult *stats);
  
  /* gistsplit.c */
  extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
*************** extern void gistSplitByKey(Relation r, P
*** 544,550 ****
  			   int attno);
  
  /* gistbuild.c */
! extern Datum gistbuild(PG_FUNCTION_ARGS);
  extern void gistValidateBufferingOption(char *value);
  
  /* gistbuildbuffers.c */
--- 553,560 ----
  			   int attno);
  
  /* gistbuild.c */
! extern IndexBuildResult *gistbuild(Relation heap, Relation index,
! 								   IndexInfo *indexInfo);
  extern void gistValidateBufferingOption(char *value);
  
  /* gistbuildbuffers.c */
diff --git a/src/include/access/gistscan.h b/src/include/access/gistscan.h
new file mode 100644
index 15cb3d1..0e6afe9
*** a/src/include/access/gistscan.h
--- b/src/include/access/gistscan.h
***************
*** 16,25 ****
  
  #include "fmgr.h"
  
! extern Datum gistbeginscan(PG_FUNCTION_ARGS);
! extern Datum gistrescan(PG_FUNCTION_ARGS);
! extern Datum gistmarkpos(PG_FUNCTION_ARGS);
! extern Datum gistrestrpos(PG_FUNCTION_ARGS);
! extern Datum gistendscan(PG_FUNCTION_ARGS);
  
  #endif   /* GISTSCAN_H */
--- 16,26 ----
  
  #include "fmgr.h"
  
! extern IndexScanDesc gistbeginscan(Relation r, int nkeys, int norderbys);
! extern void gistrescan(IndexScanDesc scan, ScanKey key, int nkeys,
! 					   ScanKey orderbys, int norderbys);
! extern void gistmarkpos(IndexScanDesc scan);
! extern void gistrestrpos(IndexScanDesc scan);
! extern void gistendscan(IndexScanDesc scan);
  
  #endif   /* GISTSCAN_H */
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
new file mode 100644
index 97cb859..9595c6b
*** a/src/include/access/hash.h
--- b/src/include/access/hash.h
***************
*** 17,23 ****
  #ifndef HASH_H
  #define HASH_H
  
! #include "access/genam.h"
  #include "access/itup.h"
  #include "access/sdir.h"
  #include "access/xlogreader.h"
--- 17,23 ----
  #ifndef HASH_H
  #define HASH_H
  
! #include "access/amapi.h"
  #include "access/itup.h"
  #include "access/sdir.h"
  #include "access/xlogreader.h"
*************** typedef HashMetaPageData *HashMetaPage;
*** 243,261 ****
  
  /* public routines */
  
! extern Datum hashbuild(PG_FUNCTION_ARGS);
! extern Datum hashbuildempty(PG_FUNCTION_ARGS);
! extern Datum hashinsert(PG_FUNCTION_ARGS);
! extern Datum hashbeginscan(PG_FUNCTION_ARGS);
! extern Datum hashgettuple(PG_FUNCTION_ARGS);
! extern Datum hashgetbitmap(PG_FUNCTION_ARGS);
! extern Datum hashrescan(PG_FUNCTION_ARGS);
! extern Datum hashendscan(PG_FUNCTION_ARGS);
! extern Datum hashmarkpos(PG_FUNCTION_ARGS);
! extern Datum hashrestrpos(PG_FUNCTION_ARGS);
! extern Datum hashbulkdelete(PG_FUNCTION_ARGS);
! extern Datum hashvacuumcleanup(PG_FUNCTION_ARGS);
! extern Datum hashoptions(PG_FUNCTION_ARGS);
  
  /*
   * Datatype-specific hash functions in hashfunc.c.
--- 243,271 ----
  
  /* public routines */
  
! extern Datum hashhandler(PG_FUNCTION_ARGS);
! extern void hashvalidate(OpClassInfo *opclass);
! extern IndexBuildResult *hashbuild(Relation heap, Relation index,
! 								   IndexInfo *indexInfo);
! extern void hashbuildempty(Relation index);
! extern bool hashinsert(Relation rel, Datum *values, bool *isnull,
! 					   ItemPointer ht_ctid, Relation heapRel,
! 					   IndexUniqueCheck checkUnique);
! extern IndexScanDesc hashbeginscan(Relation rel, int nkeys, int norderbys);
! extern bool hashgettuple(IndexScanDesc scan, ScanDirection dir);
! extern int64 hashgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
! extern void hashrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
! 					   ScanKey orderbys, int norderbys);
! extern void hashendscan(IndexScanDesc scan);
! extern void hashmarkpos(IndexScanDesc scan);
! extern void hashrestrpos(IndexScanDesc scan);
! extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info,
! 											 IndexBulkDeleteResult *stats,
! 											 IndexBulkDeleteCallback callback,
! 											 void *callback_state);
! extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,
! 												IndexBulkDeleteResult *stats);
! extern bytea *hashoptions(Datum reloptions, bool validate);
  
  /*
   * Datatype-specific hash functions in hashfunc.c.
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
new file mode 100644
index 9e48efd..4fab0a3
*** a/src/include/access/nbtree.h
--- b/src/include/access/nbtree.h
***************
*** 14,26 ****
  #ifndef NBTREE_H
  #define NBTREE_H
  
! #include "access/genam.h"
  #include "access/itup.h"
  #include "access/sdir.h"
  #include "access/xlogreader.h"
  #include "catalog/pg_index.h"
  #include "lib/stringinfo.h"
  #include "storage/bufmgr.h"
  
  /* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
  typedef uint16 BTCycleId;
--- 14,27 ----
  #ifndef NBTREE_H
  #define NBTREE_H
  
! #include "access/amapi.h"
  #include "access/itup.h"
  #include "access/sdir.h"
  #include "access/xlogreader.h"
  #include "catalog/pg_index.h"
  #include "lib/stringinfo.h"
  #include "storage/bufmgr.h"
+ #include "utils/selfuncs.h"
  
  /* There's room for a 16-bit vacuum cycle ID in BTPageOpaqueData */
  typedef uint16 BTCycleId;
*************** typedef BTScanOpaqueData *BTScanOpaque;
*** 652,671 ****
  /*
   * prototypes for functions in nbtree.c (external entry points for btree)
   */
! extern Datum btbuild(PG_FUNCTION_ARGS);
! extern Datum btbuildempty(PG_FUNCTION_ARGS);
! extern Datum btinsert(PG_FUNCTION_ARGS);
! extern Datum btbeginscan(PG_FUNCTION_ARGS);
! extern Datum btgettuple(PG_FUNCTION_ARGS);
! extern Datum btgetbitmap(PG_FUNCTION_ARGS);
! extern Datum btrescan(PG_FUNCTION_ARGS);
! extern Datum btendscan(PG_FUNCTION_ARGS);
! extern Datum btmarkpos(PG_FUNCTION_ARGS);
! extern Datum btrestrpos(PG_FUNCTION_ARGS);
! extern Datum btbulkdelete(PG_FUNCTION_ARGS);
! extern Datum btvacuumcleanup(PG_FUNCTION_ARGS);
! extern Datum btcanreturn(PG_FUNCTION_ARGS);
! extern Datum btoptions(PG_FUNCTION_ARGS);
  
  /*
   * prototypes for functions in nbtinsert.c
--- 653,683 ----
  /*
   * prototypes for functions in nbtree.c (external entry points for btree)
   */
! extern Datum bthandler(PG_FUNCTION_ARGS);
! extern void btvalidate(OpClassInfo *opclass);
! extern IndexBuildResult *btbuild(Relation heap, Relation index,
! 								 IndexInfo *indexInfo);
! extern void btbuildempty(Relation index);
! extern bool btinsert(Relation rel, Datum *values, bool *isnull,
! 					 ItemPointer ht_ctid, Relation heapRel,
! 					 IndexUniqueCheck checkUnique);
! extern IndexScanDesc btbeginscan(Relation indexRelation,
! 								 int nkeys, int norderbys);
! extern bool btgettuple(IndexScanDesc scan, ScanDirection dir);
! extern int64 btgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
! extern void btrescan(IndexScanDesc scan, ScanKey keys, int nkeys,
! 					 ScanKey orderbys, int norderbys);
! extern void btendscan(IndexScanDesc scan);
! extern void btmarkpos(IndexScanDesc scan);
! extern void btrestrpos(IndexScanDesc scan);
! extern IndexBulkDeleteResult *btbulkdelete(IndexVacuumInfo *info,
! 										   IndexBulkDeleteResult *stats,
! 										   IndexBulkDeleteCallback callback,
! 										   void *callback_state);
! extern IndexBulkDeleteResult *btvacuumcleanup(IndexVacuumInfo *info,
! 											  IndexBulkDeleteResult *stats);
! extern bool btcanreturn(Relation index, int attno);
! extern bytea *btoptions(Datum reloptions, bool validate);
  
  /*
   * prototypes for functions in nbtinsert.c
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
new file mode 100644
index 2a3cbcd..7de02d2
*** a/src/include/access/reloptions.h
--- b/src/include/access/reloptions.h
***************
*** 19,24 ****
--- 19,25 ----
  #ifndef RELOPTIONS_H
  #define RELOPTIONS_H
  
+ #include "access/amapi.h"
  #include "access/htup.h"
  #include "access/tupdesc.h"
  #include "nodes/pg_list.h"
*************** extern Datum transformRelOptions(Datum o
*** 258,264 ****
  					bool ignoreOids, bool isReset);
  extern List *untransformRelOptions(Datum options);
  extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
! 				  Oid amoptions);
  extern relopt_value *parseRelOptions(Datum options, bool validate,
  				relopt_kind kind, int *numrelopts);
  extern void *allocateReloptStruct(Size base, relopt_value *options,
--- 259,265 ----
  					bool ignoreOids, bool isReset);
  extern List *untransformRelOptions(Datum options);
  extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
! 				  amoptions_function amoptions);
  extern relopt_value *parseRelOptions(Datum options, bool validate,
  				relopt_kind kind, int *numrelopts);
  extern void *allocateReloptStruct(Size base, relopt_value *options,
*************** extern bytea *default_reloptions(Datum r
*** 272,278 ****
  				   relopt_kind kind);
  extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
  extern bytea *view_reloptions(Datum reloptions, bool validate);
! extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
  				 bool validate);
  extern bytea *attribute_reloptions(Datum reloptions, bool validate);
  extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
--- 273,279 ----
  				   relopt_kind kind);
  extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
  extern bytea *view_reloptions(Datum reloptions, bool validate);
! extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
  				 bool validate);
  extern bytea *attribute_reloptions(Datum reloptions, bool validate);
  extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
new file mode 100644
index 0cb8fd9..ba2c1c1
*** a/src/include/access/spgist.h
--- b/src/include/access/spgist.h
***************
*** 14,20 ****
  #ifndef SPGIST_H
  #define SPGIST_H
  
! #include "access/skey.h"
  #include "access/xlogreader.h"
  #include "fmgr.h"
  #include "lib/stringinfo.h"
--- 14,20 ----
  #ifndef SPGIST_H
  #define SPGIST_H
  
! #include "access/amapi.h"
  #include "access/xlogreader.h"
  #include "fmgr.h"
  #include "lib/stringinfo.h"
*************** typedef struct spgLeafConsistentOut
*** 174,200 ****
  } spgLeafConsistentOut;
  
  
  /* spginsert.c */
! extern Datum spgbuild(PG_FUNCTION_ARGS);
! extern Datum spgbuildempty(PG_FUNCTION_ARGS);
! extern Datum spginsert(PG_FUNCTION_ARGS);
  
  /* spgscan.c */
! extern Datum spgbeginscan(PG_FUNCTION_ARGS);
! extern Datum spgendscan(PG_FUNCTION_ARGS);
! extern Datum spgrescan(PG_FUNCTION_ARGS);
! extern Datum spgmarkpos(PG_FUNCTION_ARGS);
! extern Datum spgrestrpos(PG_FUNCTION_ARGS);
! extern Datum spggetbitmap(PG_FUNCTION_ARGS);
! extern Datum spggettuple(PG_FUNCTION_ARGS);
! extern Datum spgcanreturn(PG_FUNCTION_ARGS);
  
  /* spgutils.c */
! extern Datum spgoptions(PG_FUNCTION_ARGS);
  
  /* spgvacuum.c */
! extern Datum spgbulkdelete(PG_FUNCTION_ARGS);
! extern Datum spgvacuumcleanup(PG_FUNCTION_ARGS);
  
  /* spgxlog.c */
  extern void spg_redo(XLogReaderState *record);
--- 174,212 ----
  } spgLeafConsistentOut;
  
  
+ /* spgutils.c */
+ extern Datum spghandler(PG_FUNCTION_ARGS);
+ extern void spgvalidate(OpClassInfo *opclass);
+ 
  /* spginsert.c */
! extern IndexBuildResult *spgbuild(Relation heap, Relation index,
! 								  IndexInfo *indexInfo);
! extern void spgbuildempty(Relation indexRelation);
! extern bool spginsert(Relation index, Datum *values, bool *isnull,
! 					  ItemPointer ht_ctid, Relation heapRel,
! 					  IndexUniqueCheck checkUnique);
  
  /* spgscan.c */
! extern IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz);
! extern void spgendscan(IndexScanDesc scan);
! extern void spgrescan(IndexScanDesc scan, ScanKey key, int nkeys,
! 					  ScanKey orderbys, int norderbys);
! extern void spgmarkpos(IndexScanDesc scan);
! extern void spgrestrpos(IndexScanDesc scan);
! extern int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
! extern bool spggettuple(IndexScanDesc scan, ScanDirection dir);
! extern bool spgcanreturn(Relation index, int attno);
  
  /* spgutils.c */
! extern bytea *spgoptions(Datum reloptions, bool validate);
  
  /* spgvacuum.c */
! extern IndexBulkDeleteResult *spgbulkdelete(IndexVacuumInfo *info,
! 											IndexBulkDeleteResult *stats,
! 											IndexBulkDeleteCallback callback,
! 											void *callback_state);
! extern IndexBulkDeleteResult *spgvacuumcleanup(IndexVacuumInfo *info,
! 											   IndexBulkDeleteResult *stats);
  
  /* spgxlog.c */
  extern void spg_redo(XLogReaderState *record);
diff --git a/src/include/catalog/opfam_internal.h b/src/include/catalog/opfam_internal.h
new file mode 100644
index 32195a7..2cbefe9
*** a/src/include/catalog/opfam_internal.h
--- b/src/include/catalog/opfam_internal.h
*************** typedef struct
*** 25,28 ****
--- 25,41 ----
  	Oid			sortfamily;		/* ordering operator's sort opfamily, or 0 */
  } OpFamilyMember;
  
+ /*
+  * Opclass data for validation by access method.
+  */
+ typedef struct
+ {
+ 	Oid			amoid;			/* access method OID */
+ 	Oid			intype;			/* input datatype */
+ 	Oid			keytype;		/* key datatype */
+ 	List	   *procedures;		/* list of OpFamilyMember */
+ 	List	   *operators;		/* list of OpFamilyMember */
+ } OpClassInfo;
+ 
+ 
  #endif   /* OPFAM_INTERNAL_H */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index 8a28b8e..f19a873
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
***************
*** 34,72 ****
  CATALOG(pg_am,2601)
  {
  	NameData	amname;			/* access method name */
! 	int16		amstrategies;	/* total number of strategies (operators) by
! 								 * which we can traverse/search this AM. Zero
! 								 * if AM does not have a fixed set of strategy
! 								 * assignments. */
! 	int16		amsupport;		/* total number of support functions that this
! 								 * AM uses */
! 	bool		amcanorder;		/* does AM support order by column value? */
! 	bool		amcanorderbyop; /* does AM support order by operator result? */
! 	bool		amcanbackward;	/* does AM support backward scan? */
! 	bool		amcanunique;	/* does AM support UNIQUE indexes? */
! 	bool		amcanmulticol;	/* does AM support multi-column indexes? */
! 	bool		amoptionalkey;	/* can query omit key for the first column? */
! 	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
! 	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
! 	bool		amstorage;		/* can storage type differ from column type? */
! 	bool		amclusterable;	/* does AM support cluster command? */
! 	bool		ampredlocks;	/* does AM handle predicate locks? */
! 	Oid			amkeytype;		/* type of data in index, or InvalidOid */
! 	regproc		aminsert;		/* "insert this tuple" function */
! 	regproc		ambeginscan;	/* "prepare for index scan" function */
! 	regproc		amgettuple;		/* "next valid tuple" function, or 0 */
! 	regproc		amgetbitmap;	/* "fetch all valid tuples" function, or 0 */
! 	regproc		amrescan;		/* "(re)start index scan" function */
! 	regproc		amendscan;		/* "end index scan" function */
! 	regproc		ammarkpos;		/* "mark current scan position" function */
! 	regproc		amrestrpos;		/* "restore marked scan position" function */
! 	regproc		ambuild;		/* "build new index" function */
! 	regproc		ambuildempty;	/* "build empty index" function */
! 	regproc		ambulkdelete;	/* bulk-delete function */
! 	regproc		amvacuumcleanup;	/* post-VACUUM cleanup function */
! 	regproc		amcanreturn;	/* can indexscan return IndexTuples? */
! 	regproc		amcostestimate; /* estimate cost of an indexscan */
! 	regproc		amoptions;		/* parse AM-specific parameters */
  } FormData_pg_am;
  
  /* ----------------
--- 34,40 ----
  CATALOG(pg_am,2601)
  {
  	NameData	amname;			/* access method name */
! 	regproc		amhandler;		/* handler function */
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 80,138 ****
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						30
  #define Anum_pg_am_amname				1
! #define Anum_pg_am_amstrategies			2
! #define Anum_pg_am_amsupport			3
! #define Anum_pg_am_amcanorder			4
! #define Anum_pg_am_amcanorderbyop		5
! #define Anum_pg_am_amcanbackward		6
! #define Anum_pg_am_amcanunique			7
! #define Anum_pg_am_amcanmulticol		8
! #define Anum_pg_am_amoptionalkey		9
! #define Anum_pg_am_amsearcharray		10
! #define Anum_pg_am_amsearchnulls		11
! #define Anum_pg_am_amstorage			12
! #define Anum_pg_am_amclusterable		13
! #define Anum_pg_am_ampredlocks			14
! #define Anum_pg_am_amkeytype			15
! #define Anum_pg_am_aminsert				16
! #define Anum_pg_am_ambeginscan			17
! #define Anum_pg_am_amgettuple			18
! #define Anum_pg_am_amgetbitmap			19
! #define Anum_pg_am_amrescan				20
! #define Anum_pg_am_amendscan			21
! #define Anum_pg_am_ammarkpos			22
! #define Anum_pg_am_amrestrpos			23
! #define Anum_pg_am_ambuild				24
! #define Anum_pg_am_ambuildempty			25
! #define Anum_pg_am_ambulkdelete			26
! #define Anum_pg_am_amvacuumcleanup		27
! #define Anum_pg_am_amcanreturn			28
! #define Anum_pg_am_amcostestimate		29
! #define Anum_pg_am_amoptions			30
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		5 2 t f t t t t t t f t t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbuildempty btbulkdelete btvacuumcleanup btcanreturn btcostestimate btoptions ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		1 1 f f t f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbuildempty hashbulkdelete hashvacuumcleanup - hashcostestimate hashoptions ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		0 9 f t f f t t f t t t f 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbuildempty gistbulkdelete gistvacuumcleanup gistcanreturn gistcostestimate gistoptions ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		0 6 f f f f t t f f t f f 0 gininsert ginbeginscan - gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbuildempty ginbulkdelete ginvacuumcleanup - gincostestimate ginoptions ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	0 5 f f f f f t f t f f f 0 spginsert spgbeginscan spggettuple spggetbitmap spgrescan spgendscan spgmarkpos spgrestrpos spgbuild spgbuildempty spgbulkdelete spgvacuumcleanup spgcanreturn spgcostestimate spgoptions ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin	   0 15 f f f f t t f t t f f 0 brininsert brinbeginscan - bringetbitmap brinrescan brinendscan brinmarkpos brinrestrpos brinbuild brinbuildempty brinbulkdelete brinvacuumcleanup - brincostestimate brinoptions ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
--- 48,78 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						2
  #define Anum_pg_am_amname				1
! #define Anum_pg_am_amhandler			2
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index eb55b3a..587aaac
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("convert int4 to float4");
*** 547,609 ****
  DATA(insert OID = 319 (  int4			   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1  0 23 "700" _null_ _null_ _null_ _null_ _null_	ftoi4 _null_ _null_ _null_ ));
  DESCR("convert float4 to int4");
  
- DATA(insert OID = 330 (  btgettuple		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_	btgettuple _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 636 (  btgetbitmap	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_	btgetbitmap _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 331 (  btinsert		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_	btinsert _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 333 (  btbeginscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_	btbeginscan _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 334 (  btrescan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btrescan _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 335 (  btendscan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btendscan _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 336 (  btmarkpos		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btmarkpos _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 337 (  btrestrpos		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btrestrpos _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 338 (  btbuild		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btbuild _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 328 (  btbuildempty	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ btbuildempty _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 332 (  btbulkdelete	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btbulkdelete _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 972 (  btvacuumcleanup   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ btvacuumcleanup _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 276 (  btcanreturn	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ _null_ btcanreturn _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 1268 (  btcostestimate   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ btcostestimate _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- DATA(insert OID = 2785 (  btoptions		   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  _null_ btoptions _null_ _null_ _null_ ));
- DESCR("btree(internal)");
- 
- DATA(insert OID = 3789 (  bringetbitmap    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_	bringetbitmap _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3790 (  brininsert		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_	brininsert _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3791 (  brinbeginscan    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_	brinbeginscan _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3792 (  brinrescan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinrescan _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3793 (  brinendscan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinendscan _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3794 (  brinmarkpos		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinmarkpos _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3795 (  brinrestrpos		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinrestrpos _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3796 (  brinbuild		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinbuild _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3797 (  brinbuildempty	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ brinbuildempty _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3798 (  brinbulkdelete	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brinbulkdelete _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3799 (  brinvacuumcleanup   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ brinvacuumcleanup _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3800 (  brincostestimate	 PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ brincostestimate _null_ _null_ _null_ ));
- DESCR("brin(internal)");
- DATA(insert OID = 3801 (  brinoptions		   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_ _null_ brinoptions _null_ _null_ _null_ ));
- DESCR("brin(internal)");
  DATA(insert OID = 3952 (  brin_summarize_new_values PGNSP PGUID 12 1 0 0 0 f f f f f f v s 1 0 23 "2205" _null_ _null_ _null_ _null_ _null_ brin_summarize_new_values _null_ _null_ _null_ ));
  DESCR("brin: standalone scan new table pages");
  
--- 547,552 ----
*************** DESCR("convert name to char(n)");
*** 694,728 ****
  DATA(insert OID =  409 (  name			   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 19 "1042" _null_ _null_ _null_ _null_ _null_	bpchar_name _null_ _null_ _null_ ));
  DESCR("convert char(n) to name");
  
- DATA(insert OID = 440 (  hashgettuple	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_	hashgettuple _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 637 (  hashgetbitmap	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_	hashgetbitmap _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 441 (  hashinsert		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_	hashinsert _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 443 (  hashbeginscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_	hashbeginscan _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 444 (  hashrescan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashrescan _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 445 (  hashendscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashendscan _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 446 (  hashmarkpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashmarkpos _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 447 (  hashrestrpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashrestrpos _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 448 (  hashbuild		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashbuild _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 327 (  hashbuildempty    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ hashbuildempty _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 442 (  hashbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashbulkdelete _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 425 (  hashvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ hashvacuumcleanup _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 438 (  hashcostestimate  PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ hashcostestimate _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- DATA(insert OID = 2786 (  hashoptions	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  _null_ hashoptions _null_ _null_ _null_ ));
- DESCR("hash(internal)");
- 
  DATA(insert OID = 449 (  hashint2		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "21" _null_ _null_ _null_ _null_ _null_ hashint2 _null_ _null_ _null_ ));
  DESCR("hash");
  DATA(insert OID = 450 (  hashint4		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "23" _null_ _null_ _null_ _null_ _null_ hashint4 _null_ _null_ _null_ ));
--- 637,642 ----
*************** DESCR("larger of two");
*** 978,1014 ****
  DATA(insert OID = 771 (  int2smaller	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 21 "21 21" _null_ _null_ _null_ _null_ _null_ int2smaller _null_ _null_ _null_ ));
  DESCR("smaller of two");
  
- DATA(insert OID = 774 (  gistgettuple	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_	gistgettuple _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 638 (  gistgetbitmap	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_	gistgetbitmap _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 775 (  gistinsert		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_	gistinsert _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 777 (  gistbeginscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_	gistbeginscan _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 778 (  gistrescan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistrescan _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 779 (  gistendscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistendscan _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 780 (  gistmarkpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistmarkpos _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 781 (  gistrestrpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistrestrpos _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 782 (  gistbuild		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistbuild _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 326 (  gistbuildempty    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ gistbuildempty _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 776 (  gistbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistbulkdelete _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 2561 (  gistvacuumcleanup   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ gistvacuumcleanup _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 3280 (  gistcanreturn    PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ _null_ gistcanreturn _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 772 (  gistcostestimate  PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gistcostestimate _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- DATA(insert OID = 2787 (  gistoptions	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  _null_ gistoptions _null_ _null_ _null_ ));
- DESCR("gist(internal)");
- 
  DATA(insert OID = 784 (  tintervaleq	   PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "704 704" _null_ _null_ _null_ _null_ _null_ tintervaleq _null_ _null_ _null_ ));
  DATA(insert OID = 785 (  tintervalne	   PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "704 704" _null_ _null_ _null_ _null_ _null_ tintervalne _null_ _null_ _null_ ));
  DATA(insert OID = 786 (  tintervallt	   PGNSP PGUID 12 1 0 0 0 f f f t t f i s 2 0 16 "704 704" _null_ _null_ _null_ _null_ _null_ tintervallt _null_ _null_ _null_ ));
--- 892,897 ----
*************** DATA(insert OID = 3311 (  tsm_handler_in
*** 3740,3745 ****
--- 3623,3632 ----
  DESCR("I/O");
  DATA(insert OID = 3312 (  tsm_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "3310" _null_ _null_ _null_ _null_ _null_ tsm_handler_out _null_ _null_ _null_ ));
  DESCR("I/O");
+ DATA(insert OID = 326  (  index_am_handler_in	PGNSP PGUID 12 1 0 0 0 f f f f f f i s 1 0 325 "2275" _null_ _null_ _null_ _null_ _null_ index_am_handler_in _null_ _null_ _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 327  (  index_am_handler_out	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 2275 "325" _null_ _null_ _null_ _null_ _null_ index_am_handler_out _null_ _null_ _null_ ));
+ DESCR("I/O");
  
  /* tablesample method handlers */
  DATA(insert OID = 3313 (  bernoulli			PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 3310 "2281" _null_ _null_ _null_ _null_ _null_ tsm_bernoulli_handler _null_ _null_ _null_ ));
*************** DESCR("GiST support");
*** 4196,4229 ****
  DATA(insert OID = 3288 (  gist_bbox_distance	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 701 "2281 600 23 26" _null_ _null_ _null_ _null_ _null_	gist_bbox_distance _null_ _null_ _null_ ));
  DESCR("GiST support");
  
- /* GIN */
- DATA(insert OID = 2731 (  gingetbitmap	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_	gingetbitmap _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2732 (  gininsert		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_	gininsert _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2733 (  ginbeginscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_	ginbeginscan _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2734 (  ginrescan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginrescan _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2735 (  ginendscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginendscan _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2736 (  ginmarkpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginmarkpos _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2737 (  ginrestrpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginrestrpos _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2738 (  ginbuild		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginbuild _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 325 (  ginbuildempty	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ ginbuildempty _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2739 (  ginbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ ginbulkdelete _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2740 (  ginvacuumcleanup PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ ginvacuumcleanup _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2741 (  gincostestimate  PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ gincostestimate _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- DATA(insert OID = 2788 (  ginoptions	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  _null_ ginoptions _null_ _null_ _null_ ));
- DESCR("gin(internal)");
- 
  /* GIN array support */
  DATA(insert OID = 2743 (  ginarrayextract	 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 3 0 2281 "2277 2281 2281" _null_ _null_ _null_ _null_ _null_ ginarrayextract _null_ _null_ _null_ ));
  DESCR("GIN array support");
--- 4083,4088 ----
*************** DESCR("construct timestamp with time zon
*** 5119,5156 ****
  DATA(insert OID = 3464 ( make_interval	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 7 0 1186 "23 23 23 23 23 23 701" _null_ _null_ "{years,months,weeks,days,hours,mins,secs}" _null_ _null_ make_interval _null_ _null_ _null_ ));
  DESCR("construct interval");
  
- /* spgist support functions */
- DATA(insert OID = 4001 (  spggettuple	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 16 "2281 2281" _null_ _null_ _null_ _null_ _null_	spggettuple _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4002 (  spggetbitmap	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 20 "2281 2281" _null_ _null_ _null_ _null_ _null_	spggetbitmap _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4003 (  spginsert		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 6 0 16 "2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_	spginsert _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4004 (  spgbeginscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_	spgbeginscan _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4005 (  spgrescan		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 5 0 2278 "2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgrescan _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4006 (  spgendscan	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgendscan _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4007 (  spgmarkpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgmarkpos _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4008 (  spgrestrpos	   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgrestrpos _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4009 (  spgbuild		   PGNSP PGUID 12 1 0 0 0 f f f f t f v s 3 0 2281 "2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgbuild _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4010 (  spgbuildempty    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 2278 "2281" _null_ _null_ _null_ _null_ _null_ spgbuildempty _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4011 (  spgbulkdelete    PGNSP PGUID 12 1 0 0 0 f f f f t f v s 4 0 2281 "2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgbulkdelete _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4012 (  spgvacuumcleanup	 PGNSP PGUID 12 1 0 0 0 f f f f t f v s 2 0 2281 "2281 2281" _null_ _null_ _null_ _null_ _null_ spgvacuumcleanup _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4032 (  spgcanreturn	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 16 "2281 23" _null_ _null_ _null_ _null_ _null_ spgcanreturn _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4013 (  spgcostestimate  PGNSP PGUID 12 1 0 0 0 f f f f t f v s 7 0 2278 "2281 2281 2281 2281 2281 2281 2281" _null_ _null_ _null_ _null_ _null_ spgcostestimate _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- DATA(insert OID = 4014 (  spgoptions	   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 2 0 17 "1009 16" _null_ _null_ _null_ _null_  _null_ spgoptions _null_ _null_ _null_ ));
- DESCR("spgist(internal)");
- 
  /* spgist opclasses */
  DATA(insert OID = 4018 (  spg_quad_config	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2278 "2281 2281" _null_ _null_ _null_ _null_  _null_ spg_quad_config _null_ _null_ _null_ ));
  DESCR("SP-GiST support for quad tree over point");
--- 4978,4983 ----
*************** DESCR("get an individual replication ori
*** 5333,5338 ****
--- 5160,5184 ----
  DATA(insert OID = 6014 ( pg_show_replication_origin_status PGNSP PGUID 12 1 100 0 0 f f f f f t v r 0 0 2249 "" "{26,25,3220,3220}" "{o,o,o,o}" "{local_id, external_id, remote_lsn, local_lsn}" _null_ _null_ pg_show_replication_origin_status _null_ _null_ _null_ ));
  DESCR("get progress for all replication origins");
  
+ /* Access methods handlers */
+ DATA(insert OID = 330 (  bthandler		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 325 "" _null_ _null_ _null_ _null_ _null_	bthandler _null_ _null_ _null_ ));
+ DESCR("btree handler");
+ DATA(insert OID = 331 (  hashhandler	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 325 "" _null_ _null_ _null_ _null_ _null_	hashhandler _null_ _null_ _null_ ));
+ DESCR("hash handler");
+ DATA(insert OID = 332 (  gisthandler	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 325 "" _null_ _null_ _null_ _null_ _null_	gisthandler _null_ _null_ _null_ ));
+ DESCR("gist handler");
+ DATA(insert OID = 333 (  ginhandler		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 325 "" _null_ _null_ _null_ _null_ _null_	ginhandler _null_ _null_ _null_ ));
+ DESCR("gin handler");
+ DATA(insert OID = 334 (  spghandler		PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 325 "" _null_ _null_ _null_ _null_ _null_	spghandler _null_ _null_ _null_ ));
+ DESCR("spgist handler");
+ DATA(insert OID = 335 (  brinhandler	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 325 "" _null_ _null_ _null_ _null_ _null_	brinhandler _null_ _null_ _null_ ));
+ DESCR("brin handler");
+ 
+ DATA(insert OID = 336 (  amvalidate	PGNSP PGUID 12 1 0 0 0 f f f f t f v s 1 0 16 "26" _null_ _null_ _null_ _null_ _null_	amvalidate _null_ _null_ _null_ ));
+ DESCR("ask access method to validate opclass");
+ 
+ 
+ 
  /*
   * Symbolic values for provolatile column: these indicate whether the result
   * of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
new file mode 100644
index 7dc95c8..59b04ac
*** a/src/include/catalog/pg_type.h
--- b/src/include/catalog/pg_type.h
*************** DATA(insert OID = 3115 ( fdw_handler	PGN
*** 696,701 ****
--- 696,703 ----
  #define FDW_HANDLEROID	3115
  DATA(insert OID = 3310 ( tsm_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
  #define TSM_HANDLEROID	3310
+ DATA(insert OID = 325 ( index_am_handler	PGNSP PGUID  4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+ #define INDEX_AM_HANDLEROID	325
  DATA(insert OID = 3831 ( anyrange		PGNSP PGUID  -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
  #define ANYRANGEOID		3831
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index adae296..d8b9649
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern Oid	get_am_oid(const char *amname
*** 95,100 ****
--- 95,101 ----
  extern char *get_am_name(Oid amOid);
  extern Oid	get_opclass_oid(Oid amID, List *opclassname, bool missing_ok);
  extern Oid	get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok);
+ extern Datum	amvalidate(PG_FUNCTION_ARGS);
  
  /* commands/tsearchcmds.c */
  extern ObjectAddress DefineTSParser(List *names, List *parameters);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
new file mode 100644
index b6895f9..0ed8a96
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 14,20 ****
  #ifndef EXECNODES_H
  #define EXECNODES_H
  
! #include "access/genam.h"
  #include "access/heapam.h"
  #include "executor/instrument.h"
  #include "lib/pairingheap.h"
--- 14,20 ----
  #ifndef EXECNODES_H
  #define EXECNODES_H
  
! #include "access/amapi.h"
  #include "access/heapam.h"
  #include "executor/instrument.h"
  #include "lib/pairingheap.h"
***************
*** 55,61 ****
   * they're conventionally set to false otherwise.
   * ----------------
   */
! typedef struct IndexInfo
  {
  	NodeTag		type;
  	int			ii_NumIndexAttrs;
--- 55,61 ----
   * they're conventionally set to false otherwise.
   * ----------------
   */
! struct IndexInfo
  {
  	NodeTag		type;
  	int			ii_NumIndexAttrs;
*************** typedef struct IndexInfo
*** 74,80 ****
  	bool		ii_ReadyForInserts;
  	bool		ii_Concurrent;
  	bool		ii_BrokenHotChain;
! } IndexInfo;
  
  /* ----------------
   *	  ExprContext_CB
--- 74,80 ----
  	bool		ii_ReadyForInserts;
  	bool		ii_Concurrent;
  	bool		ii_BrokenHotChain;
! };
  
  /* ----------------
   *	  ExprContext_CB
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index 94bdb7c..84e4a36
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 455,461 ****
  	T_TIDBitmap,				/* in nodes/tidbitmap.h */
  	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
  	T_FdwRoutine,				/* in foreign/fdwapi.h */
! 	T_TsmRoutine				/* in access/tsmapi.h */
  } NodeTag;
  
  /*
--- 455,462 ----
  	T_TIDBitmap,				/* in nodes/tidbitmap.h */
  	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
  	T_FdwRoutine,				/* in foreign/fdwapi.h */
! 	T_TsmRoutine,				/* in access/tsmapi.h */
! 	T_IndexAmRoutine			/* in access/amapi.h */
  } NodeTag;
  
  /*
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
new file mode 100644
index 6cf2e24..3c51bf3
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 19,24 ****
--- 19,25 ----
  #include "nodes/params.h"
  #include "nodes/parsenodes.h"
  #include "storage/block.h"
+ #include "utils/relcache.h"
  
  
  /*
*************** typedef struct IndexOptInfo
*** 548,555 ****
  	bool	   *canreturn;		/* which index cols can be returned in an
  								 * index-only scan? */
  	Oid			relam;			/* OID of the access method (in pg_am) */
! 
! 	RegProcedure amcostestimate;	/* OID of the access method's cost fcn */
  
  	List	   *indexprs;		/* expressions for non-simple index columns */
  	List	   *indpred;		/* predicate if a partial index, else NIL */
--- 549,555 ----
  	bool	   *canreturn;		/* which index cols can be returned in an
  								 * index-only scan? */
  	Oid			relam;			/* OID of the access method (in pg_am) */
! 	IndexAmRoutine *amroutine;	/* routine of access method */
  
  	List	   *indexprs;		/* expressions for non-simple index columns */
  	List	   *indpred;		/* predicate if a partial index, else NIL */
*************** typedef struct IndexOptInfo
*** 560,571 ****
  	bool		unique;			/* true if a unique index */
  	bool		immediate;		/* is uniqueness enforced immediately? */
  	bool		hypothetical;	/* true if index doesn't really exist */
! 	bool		amcanorderbyop; /* does AM support order by operator result? */
  	bool		amoptionalkey;	/* can query omit key for the first column? */
  	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
  	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
  	bool		amhasgettuple;	/* does AM have amgettuple interface? */
! 	bool		amhasgetbitmap; /* does AM have amgetbitmap interface? */
  } IndexOptInfo;
  
  
--- 560,571 ----
  	bool		unique;			/* true if a unique index */
  	bool		immediate;		/* is uniqueness enforced immediately? */
  	bool		hypothetical;	/* true if index doesn't really exist */
! 	bool		amcanorderbyop;	/* does AM support order by operator result? */
  	bool		amoptionalkey;	/* can query omit key for the first column? */
  	bool		amsearcharray;	/* can AM handle ScalarArrayOpExpr quals? */
  	bool		amsearchnulls;	/* can AM search for NULL/NOT NULL entries? */
  	bool		amhasgettuple;	/* does AM have amgettuple interface? */
! 	bool		amhasgetbitmap;	/* does AM have amgetbitmap interface? */
  } IndexOptInfo;
  
  
*************** typedef struct Path
*** 819,825 ****
   * itself represent the costs of an IndexScan or IndexOnlyScan plan type.
   *----------
   */
! typedef struct IndexPath
  {
  	Path		path;
  	IndexOptInfo *indexinfo;
--- 819,825 ----
   * itself represent the costs of an IndexScan or IndexOnlyScan plan type.
   *----------
   */
! struct IndexPath
  {
  	Path		path;
  	IndexOptInfo *indexinfo;
*************** typedef struct IndexPath
*** 831,837 ****
  	ScanDirection indexscandir;
  	Cost		indextotalcost;
  	Selectivity indexselectivity;
! } IndexPath;
  
  /*
   * BitmapHeapPath represents one or more indexscans that generate TID bitmaps
--- 831,837 ----
  	ScanDirection indexscandir;
  	Cost		indextotalcost;
  	Selectivity indexselectivity;
! };
  
  /*
   * BitmapHeapPath represents one or more indexscans that generate TID bitmaps
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
new file mode 100644
index fc1679e..8e59b8c
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
*************** extern Datum fdw_handler_in(PG_FUNCTION_
*** 568,573 ****
--- 568,575 ----
  extern Datum fdw_handler_out(PG_FUNCTION_ARGS);
  extern Datum tsm_handler_in(PG_FUNCTION_ARGS);
  extern Datum tsm_handler_out(PG_FUNCTION_ARGS);
+ extern Datum index_am_handler_in(PG_FUNCTION_ARGS);
+ extern Datum index_am_handler_out(PG_FUNCTION_ARGS);
  extern Datum internal_in(PG_FUNCTION_ARGS);
  extern Datum internal_out(PG_FUNCTION_ARGS);
  extern Datum opaque_in(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
new file mode 100644
index 8a55a09..483b840
*** a/src/include/utils/rel.h
--- b/src/include/utils/rel.h
*************** typedef struct RelationData
*** 129,134 ****
--- 129,135 ----
  	/* use "struct" here to avoid needing to include htup.h: */
  	struct HeapTupleData *rd_indextuple;		/* all of pg_index tuple */
  	Form_pg_am	rd_am;			/* pg_am tuple for index's AM */
+ 	IndexAmRoutine *amroutine;
  
  	/*
  	 * index access support info (used only for an index relation)
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
new file mode 100644
index 6953281..e686c9e
*** a/src/include/utils/relcache.h
--- b/src/include/utils/relcache.h
***************
*** 19,24 ****
--- 19,27 ----
  
  
  typedef struct RelationData *Relation;
+ typedef struct IndexPath IndexPath;
+ typedef struct IndexInfo IndexInfo;
+ 
  
  /* ----------------
   *		RelationPtr is used in the executor to support index scans
*************** typedef struct RelationData *Relation;
*** 28,33 ****
--- 31,45 ----
   */
  typedef Relation *RelationPtr;
  
+ typedef struct IndexAmRoutine IndexAmRoutine;
+ 
+ /*
+  * Routines to retreive IndexAmRoutine
+  */
+ extern IndexAmRoutine *GetIndexAmRoutine(Oid amhandler);
+ extern IndexAmRoutine *GetIndexAmRoutineByAmId(Oid amoid);
+ extern void InitIndexAmRoutine(Relation relation);
+ 
  /*
   * Routines to open (lookup) and close a relcache entry
   */
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index a7433d9..3745d1c
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** extern double estimate_num_groups(Planne
*** 191,202 ****
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
! extern Datum brincostestimate(PG_FUNCTION_ARGS);
! extern Datum btcostestimate(PG_FUNCTION_ARGS);
! extern Datum hashcostestimate(PG_FUNCTION_ARGS);
! extern Datum gistcostestimate(PG_FUNCTION_ARGS);
! extern Datum spgcostestimate(PG_FUNCTION_ARGS);
! extern Datum gincostestimate(PG_FUNCTION_ARGS);
  
  /* Functions in array_selfuncs.c */
  
--- 191,226 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
! extern void brincostestimate(PlannerInfo *root, IndexPath *path,
! 							 double loop_count, Cost *indexStartupCost,
! 							 Cost *indexTotalCost,
! 							 Selectivity *indexSelectivity,
! 							 double *indexCorrelation);
! extern void btcostestimate(PlannerInfo *root, IndexPath *path,
! 						   double loop_count, Cost *indexStartupCost,
! 						   Cost *indexTotalCost,
! 						   Selectivity *indexSelectivity,
! 						   double *indexCorrelation);
! extern void hashcostestimate(PlannerInfo *root, IndexPath *path,
! 							 double loop_count, Cost *indexStartupCost,
! 							 Cost *indexTotalCost,
! 							 Selectivity *indexSelectivity,
! 							 double *indexCorrelation);
! extern void gistcostestimate(PlannerInfo *root, IndexPath *path,
! 							 double loop_count, Cost *indexStartupCost,
! 							 Cost *indexTotalCost,
! 							 Selectivity *indexSelectivity,
! 							 double *indexCorrelation);
! extern void spgcostestimate(PlannerInfo *root, IndexPath *path,
! 							double loop_count, Cost *indexStartupCost,
! 							Cost *indexTotalCost,
! 							Selectivity *indexSelectivity,
! 							double *indexCorrelation);
! extern void gincostestimate(PlannerInfo *root, IndexPath *path,
! 							double loop_count, Cost *indexStartupCost,
! 							Cost *indexTotalCost,
! 							Selectivity *indexSelectivity,
! 							double *indexCorrelation);
  
  /* Functions in array_selfuncs.c */
  
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
new file mode 100644
index 44ce6f5..9e3b599
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
*************** create type alter1.ctype as (f1 int, f2 
*** 2124,2131 ****
  create function alter1.same(alter1.ctype, alter1.ctype) returns boolean language sql
  as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2';
  create operator alter1.=(procedure = alter1.same, leftarg  = alter1.ctype, rightarg = alter1.ctype);
  create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as
!   operator 1 alter1.=(alter1.ctype, alter1.ctype);
  create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8;
  create text search parser alter1.prs(start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype);
  create text search configuration alter1.cfg(parser = alter1.prs);
--- 2124,2134 ----
  create function alter1.same(alter1.ctype, alter1.ctype) returns boolean language sql
  as 'select $1.f1 is not distinct from $2.f1 and $1.f2 is not distinct from $2.f2';
  create operator alter1.=(procedure = alter1.same, leftarg  = alter1.ctype, rightarg = alter1.ctype);
+ create function alter1.hash(alter1.ctype) returns int4 language sql
+ as 'select hashint4($1.f1) # hashtext($1.f2)';
  create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as
!   operator 1 alter1.=(alter1.ctype, alter1.ctype),
!   function 1 alter1.hash(alter1.ctype);
  create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8;
  create text search parser alter1.prs(start = prsd_start, gettoken = prsd_nexttoken, end = prsd_end, lextypes = prsd_lextype);
  create text search configuration alter1.cfg(parser = alter1.prs);
*************** alter operator class alter1.ctype_hash_o
*** 2141,2146 ****
--- 2144,2150 ----
  alter operator family alter1.ctype_hash_ops using hash set schema alter2;
  alter operator alter1.=(alter1.ctype, alter1.ctype) set schema alter2;
  alter function alter1.same(alter1.ctype, alter1.ctype) set schema alter2;
+ alter function alter1.hash(alter1.ctype) set schema alter2;
  alter type alter1.ctype set schema alter2;
  alter conversion alter1.ascii_to_utf8 set schema alter2;
  alter text search parser alter1.prs set schema alter2;
*************** select alter2.plus1(41);
*** 2177,2183 ****
  
  -- clean up
  drop schema alter2 cascade;
! NOTICE:  drop cascades to 13 other objects
  DETAIL:  drop cascades to table alter2.t1
  drop cascades to view alter2.v1
  drop cascades to function alter2.plus1(integer)
--- 2181,2187 ----
  
  -- clean up
  drop schema alter2 cascade;
! NOTICE:  drop cascades to 14 other objects
  DETAIL:  drop cascades to table alter2.t1
  drop cascades to view alter2.v1
  drop cascades to function alter2.plus1(integer)
*************** drop cascades to operator family alter2.
*** 2186,2191 ****
--- 2190,2196 ----
  drop cascades to type alter2.ctype
  drop cascades to function alter2.same(alter2.ctype,alter2.ctype)
  drop cascades to operator alter2.=(alter2.ctype,alter2.ctype)
+ drop cascades to function alter2.hash(alter2.ctype)
  drop cascades to conversion ascii_to_utf8
  drop cascades to text search parser prs
  drop cascades to text search configuration cfg
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
new file mode 100644
index d85bc83..d1a322d
*** a/src/test/regress/expected/oidjoins.out
--- b/src/test/regress/expected/oidjoins.out
*************** WHERE	aggmtranstype != 0 AND
*** 73,206 ****
  ------+---------------
  (0 rows)
  
- SELECT	ctid, amkeytype
- FROM	pg_catalog.pg_am fk
- WHERE	amkeytype != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amkeytype);
-  ctid | amkeytype 
- ------+-----------
- (0 rows)
- 
- SELECT	ctid, aminsert
- FROM	pg_catalog.pg_am fk
- WHERE	aminsert != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aminsert);
-  ctid | aminsert 
- ------+----------
- (0 rows)
- 
- SELECT	ctid, ambeginscan
- FROM	pg_catalog.pg_am fk
- WHERE	ambeginscan != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
-  ctid | ambeginscan 
- ------+-------------
- (0 rows)
- 
- SELECT	ctid, amgettuple
- FROM	pg_catalog.pg_am fk
- WHERE	amgettuple != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
-  ctid | amgettuple 
- ------+------------
- (0 rows)
- 
- SELECT	ctid, amgetbitmap
- FROM	pg_catalog.pg_am fk
- WHERE	amgetbitmap != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
-  ctid | amgetbitmap 
- ------+-------------
- (0 rows)
- 
- SELECT	ctid, amrescan
- FROM	pg_catalog.pg_am fk
- WHERE	amrescan != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrescan);
-  ctid | amrescan 
- ------+----------
- (0 rows)
- 
- SELECT	ctid, amendscan
- FROM	pg_catalog.pg_am fk
- WHERE	amendscan != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amendscan);
-  ctid | amendscan 
- ------+-----------
- (0 rows)
- 
- SELECT	ctid, ammarkpos
- FROM	pg_catalog.pg_am fk
- WHERE	ammarkpos != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ammarkpos);
-  ctid | ammarkpos 
- ------+-----------
- (0 rows)
- 
- SELECT	ctid, amrestrpos
- FROM	pg_catalog.pg_am fk
- WHERE	amrestrpos != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrestrpos);
-  ctid | amrestrpos 
- ------+------------
- (0 rows)
- 
- SELECT	ctid, ambuild
- FROM	pg_catalog.pg_am fk
- WHERE	ambuild != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
-  ctid | ambuild 
- ------+---------
- (0 rows)
- 
- SELECT	ctid, ambuildempty
- FROM	pg_catalog.pg_am fk
- WHERE	ambuildempty != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
-  ctid | ambuildempty 
- ------+--------------
- (0 rows)
- 
- SELECT	ctid, ambulkdelete
- FROM	pg_catalog.pg_am fk
- WHERE	ambulkdelete != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambulkdelete);
-  ctid | ambulkdelete 
- ------+--------------
- (0 rows)
- 
- SELECT	ctid, amvacuumcleanup
- FROM	pg_catalog.pg_am fk
- WHERE	amvacuumcleanup != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amvacuumcleanup);
-  ctid | amvacuumcleanup 
- ------+-----------------
- (0 rows)
- 
- SELECT	ctid, amcanreturn
- FROM	pg_catalog.pg_am fk
- WHERE	amcanreturn != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcanreturn);
-  ctid | amcanreturn 
- ------+-------------
- (0 rows)
- 
- SELECT	ctid, amcostestimate
- FROM	pg_catalog.pg_am fk
- WHERE	amcostestimate != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcostestimate);
-  ctid | amcostestimate 
- ------+----------------
- (0 rows)
- 
- SELECT	ctid, amoptions
- FROM	pg_catalog.pg_am fk
- WHERE	amoptions != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amoptions);
-  ctid | amoptions 
- ------+-----------
- (0 rows)
- 
  SELECT	ctid, amopfamily
  FROM	pg_catalog.pg_amop fk
  WHERE	amopfamily != 0 AND
--- 73,78 ----
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
new file mode 100644
index df29fe5..708d84c
*** a/src/test/regress/expected/opr_sanity.out
--- b/src/test/regress/expected/opr_sanity.out
*************** WHERE p1.oid != p2.oid AND
*** 1479,1484 ****
--- 1479,1490 ----
  -----+-----
  (0 rows)
  
+ -- Ask access methods to validate opclasses
+ SELECT oid FROM pg_opclass WHERE NOT amvalidate(oid);
+  oid 
+ -----
+ (0 rows)
+ 
  -- **************** pg_amop ****************
  -- Look for illegal values in pg_amop fields
  SELECT p1.amopfamily, p1.amopstrategy
*************** WHERE p1.amopsortfamily <> 0 AND NOT EXI
*** 1524,1572 ****
  ------------+--------------
  (0 rows)
  
- -- check for ordering operators not supported by parent AM
- SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
- FROM pg_amop AS p1, pg_am AS p2
- WHERE p1.amopmethod = p2.oid AND
-     p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
-  amopfamily | amopopr | oid | amname 
- ------------+---------+-----+--------
- (0 rows)
- 
- -- Cross-check amopstrategy index against parent AM
- SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
- FROM pg_amop AS p1, pg_am AS p2
- WHERE p1.amopmethod = p2.oid AND
-     p1.amopstrategy > p2.amstrategies AND p2.amstrategies <> 0;
-  amopfamily | amopopr | oid | amname 
- ------------+---------+-----+--------
- (0 rows)
- 
- -- Detect missing pg_amop entries: should have as many strategy operators
- -- as AM expects for each datatype combination supported by the opfamily.
- -- We can't check this for AMs with variable strategy sets.
- SELECT p1.amname, p2.amoplefttype, p2.amoprighttype
- FROM pg_am AS p1, pg_amop AS p2
- WHERE p2.amopmethod = p1.oid AND
-     p1.amstrategies <> 0 AND
-     p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
-                         WHERE p3.amopfamily = p2.amopfamily AND
-                               p3.amoplefttype = p2.amoplefttype AND
-                               p3.amoprighttype = p2.amoprighttype AND
-                               p3.amoppurpose = 's');
-  amname | amoplefttype | amoprighttype 
- --------+--------------+---------------
- (0 rows)
- 
- -- Currently, none of the AMs with fixed strategy sets support ordering ops.
- SELECT p1.amname, p2.amopfamily, p2.amopstrategy
- FROM pg_am AS p1, pg_amop AS p2
- WHERE p2.amopmethod = p1.oid AND
-     p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
-  amname | amopfamily | amopstrategy 
- --------+------------+--------------
- (0 rows)
- 
  -- Check that amopopr points at a reasonable-looking operator, ie a binary
  -- operator.  If it's a search operator it had better yield boolean,
  -- otherwise an input type of its sort opfamily.
--- 1530,1535 ----
*************** WHERE p1.amprocfamily = 0 OR p1.amprocle
*** 1849,1863 ****
  --------------+-----------
  (0 rows)
  
- -- Cross-check amprocnum index against parent AM
- SELECT p1.amprocfamily, p1.amprocnum, p2.oid, p2.amname
- FROM pg_amproc AS p1, pg_am AS p2, pg_opfamily AS p3
- WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
-     p1.amprocnum > p2.amsupport;
-  amprocfamily | amprocnum | oid | amname 
- --------------+-----------+-----+--------
- (0 rows)
- 
  -- Detect missing pg_amproc entries: should have as many support functions
  -- as AM expects for each datatype combination supported by the opfamily.
  SELECT * FROM (
--- 1812,1817 ----
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
new file mode 100644
index 778791d..a426ef5
*** a/src/test/regress/sql/alter_table.sql
--- b/src/test/regress/sql/alter_table.sql
*************** as 'select $1.f1 is not distinct from $2
*** 1452,1459 ****
  
  create operator alter1.=(procedure = alter1.same, leftarg  = alter1.ctype, rightarg = alter1.ctype);
  
  create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as
!   operator 1 alter1.=(alter1.ctype, alter1.ctype);
  
  create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8;
  
--- 1452,1463 ----
  
  create operator alter1.=(procedure = alter1.same, leftarg  = alter1.ctype, rightarg = alter1.ctype);
  
+ create function alter1.hash(alter1.ctype) returns int4 language sql
+ as 'select hashint4($1.f1) # hashtext($1.f2)';
+ 
  create operator class alter1.ctype_hash_ops default for type alter1.ctype using hash as
!   operator 1 alter1.=(alter1.ctype, alter1.ctype),
!   function 1 alter1.hash(alter1.ctype);
  
  create conversion alter1.ascii_to_utf8 for 'sql_ascii' to 'utf8' from ascii_to_utf8;
  
*************** alter operator class alter1.ctype_hash_o
*** 1473,1478 ****
--- 1477,1483 ----
  alter operator family alter1.ctype_hash_ops using hash set schema alter2;
  alter operator alter1.=(alter1.ctype, alter1.ctype) set schema alter2;
  alter function alter1.same(alter1.ctype, alter1.ctype) set schema alter2;
+ alter function alter1.hash(alter1.ctype) set schema alter2;
  alter type alter1.ctype set schema alter2;
  alter conversion alter1.ascii_to_utf8 set schema alter2;
  alter text search parser alter1.prs set schema alter2;
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
new file mode 100644
index 2fa628d..ca419fd
*** a/src/test/regress/sql/oidjoins.sql
--- b/src/test/regress/sql/oidjoins.sql
*************** SELECT	ctid, aggmtranstype
*** 37,106 ****
  FROM	pg_catalog.pg_aggregate fk
  WHERE	aggmtranstype != 0 AND
  	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.aggmtranstype);
- SELECT	ctid, amkeytype
- FROM	pg_catalog.pg_am fk
- WHERE	amkeytype != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.amkeytype);
- SELECT	ctid, aminsert
- FROM	pg_catalog.pg_am fk
- WHERE	aminsert != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.aminsert);
- SELECT	ctid, ambeginscan
- FROM	pg_catalog.pg_am fk
- WHERE	ambeginscan != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambeginscan);
- SELECT	ctid, amgettuple
- FROM	pg_catalog.pg_am fk
- WHERE	amgettuple != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgettuple);
- SELECT	ctid, amgetbitmap
- FROM	pg_catalog.pg_am fk
- WHERE	amgetbitmap != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amgetbitmap);
- SELECT	ctid, amrescan
- FROM	pg_catalog.pg_am fk
- WHERE	amrescan != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrescan);
- SELECT	ctid, amendscan
- FROM	pg_catalog.pg_am fk
- WHERE	amendscan != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amendscan);
- SELECT	ctid, ammarkpos
- FROM	pg_catalog.pg_am fk
- WHERE	ammarkpos != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ammarkpos);
- SELECT	ctid, amrestrpos
- FROM	pg_catalog.pg_am fk
- WHERE	amrestrpos != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amrestrpos);
- SELECT	ctid, ambuild
- FROM	pg_catalog.pg_am fk
- WHERE	ambuild != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
- SELECT	ctid, ambuildempty
- FROM	pg_catalog.pg_am fk
- WHERE	ambuildempty != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
- SELECT	ctid, ambulkdelete
- FROM	pg_catalog.pg_am fk
- WHERE	ambulkdelete != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambulkdelete);
- SELECT	ctid, amvacuumcleanup
- FROM	pg_catalog.pg_am fk
- WHERE	amvacuumcleanup != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amvacuumcleanup);
- SELECT	ctid, amcanreturn
- FROM	pg_catalog.pg_am fk
- WHERE	amcanreturn != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcanreturn);
- SELECT	ctid, amcostestimate
- FROM	pg_catalog.pg_am fk
- WHERE	amcostestimate != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amcostestimate);
- SELECT	ctid, amoptions
- FROM	pg_catalog.pg_am fk
- WHERE	amoptions != 0 AND
- 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amoptions);
  SELECT	ctid, amopfamily
  FROM	pg_catalog.pg_amop fk
  WHERE	amopfamily != 0 AND
--- 37,42 ----
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
new file mode 100644
index c9bdd77..0e0506e
*** a/src/test/regress/sql/opr_sanity.sql
--- b/src/test/regress/sql/opr_sanity.sql
*************** WHERE p1.oid != p2.oid AND
*** 960,965 ****
--- 960,969 ----
      p1.opcmethod = p2.opcmethod AND p1.opcintype = p2.opcintype AND
      p1.opcdefault AND p2.opcdefault;
  
+ -- Ask access methods to validate opclasses
+ 
+ SELECT oid FROM pg_opclass WHERE NOT amvalidate(oid);
+ 
  -- **************** pg_amop ****************
  
  -- Look for illegal values in pg_amop fields
*************** WHERE p1.amopsortfamily <> 0 AND NOT EXI
*** 995,1035 ****
      (SELECT 1 from pg_opfamily op WHERE op.oid = p1.amopsortfamily
       AND op.opfmethod = (SELECT oid FROM pg_am WHERE amname = 'btree'));
  
- -- check for ordering operators not supported by parent AM
- 
- SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
- FROM pg_amop AS p1, pg_am AS p2
- WHERE p1.amopmethod = p2.oid AND
-     p1.amoppurpose = 'o' AND NOT p2.amcanorderbyop;
- 
- -- Cross-check amopstrategy index against parent AM
- 
- SELECT p1.amopfamily, p1.amopopr, p2.oid, p2.amname
- FROM pg_amop AS p1, pg_am AS p2
- WHERE p1.amopmethod = p2.oid AND
-     p1.amopstrategy > p2.amstrategies AND p2.amstrategies <> 0;
- 
- -- Detect missing pg_amop entries: should have as many strategy operators
- -- as AM expects for each datatype combination supported by the opfamily.
- -- We can't check this for AMs with variable strategy sets.
- 
- SELECT p1.amname, p2.amoplefttype, p2.amoprighttype
- FROM pg_am AS p1, pg_amop AS p2
- WHERE p2.amopmethod = p1.oid AND
-     p1.amstrategies <> 0 AND
-     p1.amstrategies != (SELECT count(*) FROM pg_amop AS p3
-                         WHERE p3.amopfamily = p2.amopfamily AND
-                               p3.amoplefttype = p2.amoplefttype AND
-                               p3.amoprighttype = p2.amoprighttype AND
-                               p3.amoppurpose = 's');
- 
- -- Currently, none of the AMs with fixed strategy sets support ordering ops.
- 
- SELECT p1.amname, p2.amopfamily, p2.amopstrategy
- FROM pg_am AS p1, pg_amop AS p2
- WHERE p2.amopmethod = p1.oid AND
-     p1.amstrategies <> 0 AND p2.amoppurpose <> 's';
- 
  -- Check that amopopr points at a reasonable-looking operator, ie a binary
  -- operator.  If it's a search operator it had better yield boolean,
  -- otherwise an input type of its sort opfamily.
--- 999,1004 ----
*************** FROM pg_amproc as p1
*** 1176,1188 ****
  WHERE p1.amprocfamily = 0 OR p1.amproclefttype = 0 OR p1.amprocrighttype = 0
      OR p1.amprocnum < 1 OR p1.amproc = 0;
  
- -- Cross-check amprocnum index against parent AM
- 
- SELECT p1.amprocfamily, p1.amprocnum, p2.oid, p2.amname
- FROM pg_amproc AS p1, pg_am AS p2, pg_opfamily AS p3
- WHERE p1.amprocfamily = p3.oid AND p3.opfmethod = p2.oid AND
-     p1.amprocnum > p2.amsupport;
- 
  -- Detect missing pg_amproc entries: should have as many support functions
  -- as AM expects for each datatype combination supported by the opfamily.
  
--- 1145,1150 ----
