create-am.9.patch

application/octet-stream

Filename: create-am.9.patch
Type: application/octet-stream
Part: 0
Message: Re: WIP: Access method extendability

Patch

Format: unified
File+
doc/src/sgml/indexam.sgml 0 0
doc/src/sgml/ref/allfiles.sgml 0 0
doc/src/sgml/ref/create_access_method.sgml 0 0
doc/src/sgml/ref/drop_access_method.sgml 0 0
doc/src/sgml/reference.sgml 0 0
src/backend/access/index/amapi.c 0 0
src/backend/catalog/dependency.c 0 0
src/backend/catalog/objectaddress.c 0 0
src/backend/commands/amcmds.c 0 0
src/backend/commands/event_trigger.c 0 0
src/backend/commands/Makefile 0 0
src/backend/commands/opclasscmds.c 0 0
src/backend/nodes/copyfuncs.c 0 0
src/backend/nodes/equalfuncs.c 0 0
src/backend/parser/gram.y 0 0
src/backend/parser/parse_utilcmd.c 0 0
src/backend/tcop/utility.c 0 0
src/backend/utils/adt/selfuncs.c 0 0
src/bin/pg_dump/common.c 0 0
src/bin/pg_dump/pg_dump.c 0 0
src/bin/pg_dump/pg_dump.h 0 0
src/bin/pg_dump/pg_dump_sort.c 0 0
src/include/catalog/dependency.h 0 0
src/include/catalog/pg_am.h 0 0
src/include/commands/defrem.h 0 0
src/include/nodes/nodes.h 0 0
src/include/nodes/parsenodes.h 0 0
src/include/parser/kwlist.h 0 0
src/include/utils/selfuncs.h 0 0
src/test/regress/expected/create_am.out 0 0
src/test/regress/expected/sanity_check.out 0 0
src/test/regress/parallel_schedule 0 0
src/test/regress/serial_schedule 0 0
src/test/regress/sql/create_am.sql 0 0
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
new file mode 100644
index 5f7befb..e5f8f1c
*** a/doc/src/sgml/indexam.sgml
--- b/doc/src/sgml/indexam.sgml
***************
*** 58,63 ****
--- 58,69 ----
    </para>
  
    <para>
+    Index access access methods can be defined and dropped using
+    <xref linkend="sql-createaccessmethod"> and
+     <xref linkend="sql-dropaccessmethod"> SQL commands respectively.
+   </para>
+ 
+   <para>
     An index access method handler function must be declared to accept a
     single argument of type <type>internal</> and to return the
     pseudo-type <type>index_am_handler</>.  The argument is a dummy value that
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
new file mode 100644
index bf95453..77667bd
*** a/doc/src/sgml/ref/allfiles.sgml
--- b/doc/src/sgml/ref/allfiles.sgml
*************** Complete list of usable sgml source file
*** 52,57 ****
--- 52,58 ----
  <!ENTITY commit             SYSTEM "commit.sgml">
  <!ENTITY commitPrepared     SYSTEM "commit_prepared.sgml">
  <!ENTITY copyTable          SYSTEM "copy.sgml">
+ <!ENTITY createAccessMethod SYSTEM "create_access_method.sgml">
  <!ENTITY createAggregate    SYSTEM "create_aggregate.sgml">
  <!ENTITY createCast         SYSTEM "create_cast.sgml">
  <!ENTITY createCollation    SYSTEM "create_collation.sgml">
*************** Complete list of usable sgml source file
*** 94,99 ****
--- 95,101 ----
  <!ENTITY delete             SYSTEM "delete.sgml">
  <!ENTITY discard            SYSTEM "discard.sgml">
  <!ENTITY do                 SYSTEM "do.sgml">
+ <!ENTITY dropAccessMethod   SYSTEM "drop_access_method.sgml">
  <!ENTITY dropAggregate      SYSTEM "drop_aggregate.sgml">
  <!ENTITY dropCast           SYSTEM "drop_cast.sgml">
  <!ENTITY dropCollation      SYSTEM "drop_collation.sgml">
diff --git a/doc/src/sgml/ref/create_access_method.sgml b/doc/src/sgml/ref/create_access_method.sgml
new file mode 100644
index ...f255ecc
*** a/doc/src/sgml/ref/create_access_method.sgml
--- b/doc/src/sgml/ref/create_access_method.sgml
***************
*** 0 ****
--- 1,120 ----
+ <!--
+ doc/src/sgml/ref/create_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-CREATEACCESSMETHOD">
+  <indexterm zone="sql-createaccessmethod">
+   <primary>CREATE ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>CREATE ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>CREATE ACCESS METHOD</refname>
+   <refpurpose>define a new access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ CREATE ACCESS METHOD <replaceable class="parameter">name</replaceable>
+     TYPE INDEX
+     HANDLER <replaceable class="parameter">handler_function</replaceable>
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> creates a new access method.
+   </para>
+ 
+   <para>
+    The access method name must be unique within the database.
+   </para>
+ 
+   <para>
+    Only superusers can define new access methods.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of the access method to be created.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>TYPE INDEX</literal></term>
+     <listitem>
+      <para>
+       This clause specifies type of access method to define.
+       For now, there are only index access methods.  But intentionally
+       there would be other types of access methods.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>HANDLER <replaceable class="parameter">handler_function</replaceable></literal></term>
+     <listitem>
+      <para><replaceable class="parameter">handler_function</replaceable> is the
+       name of a previously registered function that will be called to
+       retrieve the struct which contains required parameters and functions
+       of access method to the core.  The handler function must take single
+       argument of type <type>internal</>, and its return type must be
+       <type>index_am_handler</type>.
+      </para>
+ 
+      <para>
+       See <xref linkend="index-api"> for index access methods API.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Create an access method <literal>bloom</> with
+    handler function <literal>blhandler</>:
+ <programlisting>
+ CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
+ </programlisting>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>CREATE ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-dropaccessmethod"></member>
+    <member><xref linkend="sql-createopclass"></member>
+    <member><xref linkend="sql-createopfamily"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/ref/drop_access_method.sgml b/doc/src/sgml/ref/drop_access_method.sgml
new file mode 100644
index ...354b923
*** a/doc/src/sgml/ref/drop_access_method.sgml
--- b/doc/src/sgml/ref/drop_access_method.sgml
***************
*** 0 ****
--- 1,112 ----
+ <!--
+ doc/src/sgml/ref/drop_access_method.sgml
+ PostgreSQL documentation
+ -->
+ 
+ <refentry id="SQL-DROPACCESSMETHOD">
+  <indexterm zone="sql-dropaccessmethod">
+   <primary>DROP ACCESS METHOD</primary>
+  </indexterm>
+ 
+  <refmeta>
+   <refentrytitle>DROP ACCESS METHOD</refentrytitle>
+   <manvolnum>7</manvolnum>
+   <refmiscinfo>SQL - Language Statements</refmiscinfo>
+  </refmeta>
+ 
+  <refnamediv>
+   <refname>DROP ACCESS METHOD</refname>
+   <refpurpose>remove an access method</refpurpose>
+  </refnamediv>
+ 
+  <refsynopsisdiv>
+ <synopsis>
+ DROP ACCESS METHOD [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ CASCADE | RESTRICT ]
+ </synopsis>
+  </refsynopsisdiv>
+ 
+  <refsect1>
+   <title>Description</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> removes an existing access method.
+    Only superusers can drop access methods.
+   </para>
+ 
+   <para>
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Parameters</title>
+ 
+   <variablelist>
+    <varlistentry>
+     <term><literal>IF EXISTS</literal></term>
+     <listitem>
+      <para>
+       Do not throw an error if the access method does not exist.
+       A notice is issued in this case.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+     <listitem>
+      <para>
+       The name of an existing access method.
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>CASCADE</literal></term>
+     <listitem>
+      <para>
+       Automatically drop objects that depend on the access method
+       (such as operator classes, operator families, indexes).
+      </para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term><literal>RESTRICT</literal></term>
+     <listitem>
+      <para>
+       Refuse to drop the access method if any objects depend on it.
+       This is the default.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Examples</title>
+ 
+   <para>
+    Drop the access method <literal>bloom</>:
+ <programlisting>
+ DROP ACCESS METHOD bloom;
+ </programlisting></para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>Compatibility</title>
+ 
+   <para>
+    <command>DROP ACCESS METHOD</command> is a
+    <productname>PostgreSQL</> extension.
+   </para>
+  </refsect1>
+ 
+  <refsect1>
+   <title>See Also</title>
+ 
+   <simplelist type="inline">
+    <member><xref linkend="sql-createaccessmethod"></member>
+   </simplelist>
+  </refsect1>
+ 
+ </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
new file mode 100644
index 03020df..8acdff1
*** a/doc/src/sgml/reference.sgml
--- b/doc/src/sgml/reference.sgml
***************
*** 80,85 ****
--- 80,86 ----
     &commit;
     &commitPrepared;
     &copyTable;
+    &createAccessMethod;
     &createAggregate;
     &createCast;
     &createCollation;
***************
*** 122,127 ****
--- 123,129 ----
     &delete;
     &discard;
     &do;
+    &dropAccessMethod;
     &dropAggregate;
     &dropCast;
     &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
new file mode 100644
index bda166a..053d3bb
*** a/src/backend/access/index/amapi.c
--- b/src/backend/access/index/amapi.c
*************** GetIndexAmRoutineByAmId(Oid amoid)
*** 62,67 ****
--- 62,74 ----
  			 amoid);
  	amform = (Form_pg_am) GETSTRUCT(tuple);
  
+ 	/* Check if it's index access method */
+ 	if (amform->amtype != 'i')
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 				 errmsg("access method \"%s\" type is not index",
+ 						NameStr(amform->amname))));
+ 
  	amhandler = amform->amhandler;
  
  	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index c48e37b..a86a488
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 20,25 ****
--- 20,26 ----
  #include "catalog/heap.h"
  #include "catalog/index.h"
  #include "catalog/objectaccess.h"
+ #include "catalog/pg_am.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
  #include "catalog/pg_attrdef.h"
*************** static const Oid object_classes[] = {
*** 160,166 ****
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId			/* OCLASS_TRANSFORM */
  };
  
  
--- 161,168 ----
  	ExtensionRelationId,		/* OCLASS_EXTENSION */
  	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
  	PolicyRelationId,			/* OCLASS_POLICY */
! 	TransformRelationId,		/* OCLASS_TRANSFORM */
! 	AccessMethodRelationId		/* OCLASS_AM */
  };
  
  
*************** doDeletion(const ObjectAddress *object, 
*** 1270,1275 ****
--- 1272,1280 ----
  
  		case OCLASS_TRANSFORM:
  			DropTransformById(object->objectId);
+ 
+ 		case OCLASS_AM:
+ 			RemoveAccessMethodById(object->objectId);
  			break;
  
  		default:
*************** getObjectClass(const ObjectAddress *obje
*** 2415,2420 ****
--- 2420,2428 ----
  
  		case TransformRelationId:
  			return OCLASS_TRANSFORM;
+ 
+ 		case AccessMethodRelationId:
+ 			return OCLASS_AM;
  	}
  
  	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644
index d2aaa6d..83e8b24
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
*************** static const ObjectPropertyType ObjectPr
*** 438,443 ****
--- 438,455 ----
  		Anum_pg_type_typacl,
  		ACL_KIND_TYPE,
  		true
+ 	},
+ 	{
+ 		AccessMethodRelationId,
+ 		AmOidIndexId,
+ 		AMOID,
+ 		AMNAME,
+ 		Anum_pg_am_amname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		true
  	}
  };
  
*************** static ObjectAddress get_object_address_
*** 674,679 ****
--- 686,693 ----
  							   List *objargs, bool missing_ok);
  static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
  						  bool missing_ok);
+ static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+ 						bool missing_ok);
  static const ObjectPropertyType *get_object_property_data(Oid class_id);
  
  static void getRelationDescription(StringInfo buffer, Oid relid);
*************** get_object_address(ObjectType objtype, L
*** 913,918 ****
--- 927,935 ----
  				address = get_object_address_defacl(objname, objargs,
  													missing_ok);
  				break;
+ 			case OBJECT_ACCESS_METHOD:
+ 				address = get_object_address_am(objtype, objname, missing_ok);
+ 				break;
  			default:
  				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
  				/* placate compiler, in case it thinks elog might return */
*************** get_object_address_opcf(ObjectType objty
*** 1489,1495 ****
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
--- 1506,1512 ----
  	ObjectAddress address;
  
  	/* XXX no missing_ok support here */
! 	amoid = get_am_oid(strVal(linitial(objname)), 'i', false);
  	objname = list_copy_tail(objname, 1);
  
  	switch (objtype)
*************** get_object_address_opcf(ObjectType objty
*** 1516,1521 ****
--- 1533,1597 ----
  }
  
  /*
+  * Find the ObjectAddress for an access method.
+  */
+ static ObjectAddress
+ get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+ {
+ 	ObjectAddress	address;
+ 	char		   *amname, *catalogname;
+ 	Type		tup;
+ 
+ 	switch (list_length(objname))
+ 	{
+ 		case 1:
+ 			amname = strVal(linitial(objname));
+ 			break;
+ 		case 2:
+ 			catalogname = strVal(linitial(objname));
+ 			amname = strVal(lsecond(objname));
+ 
+ 			/*
+ 			 * We check the catalog name and then ignore it.
+ 			 */
+ 			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				  errmsg("cross-database references are not implemented: %s",
+ 						 NameListToString(objname))));
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 				errmsg("improper access method name (too many dotted names): %s",
+ 					   NameListToString(objname))));
+ 			break;
+ 	}
+ 
+ 	address.classId = AccessMethodRelationId;
+ 	address.objectId = InvalidOid;
+ 	address.objectSubId = 0;
+ 
+ 	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+ 	if (!HeapTupleIsValid(tup))
+ 	{
+ 		/* Access method is missing, report error if needed */
+ 		if (!missing_ok)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 					 errmsg("access method \"%s\" does not exist",
+ 							amname)));
+ 		return address;
+ 	}
+ 
+ 	/* Access method is found, return its oid */
+ 	address.objectId = HeapTupleGetOid(tup);
+ 	ReleaseSysCache(tup);
+ 
+ 	return address;
+ }
+ 
+ /*
   * Find the ObjectAddress for an opclass/opfamily member.
   *
   * (The returned address corresponds to a pg_amop/pg_amproc object).
*************** check_object_ownership(Oid roleid, Objec
*** 2179,2184 ****
--- 2255,2261 ----
  			break;
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
+ 		case OBJECT_ACCESS_METHOD:
  			/* We treat these object types as being owned by superusers */
  			if (!superuser_arg(roleid))
  				ereport(ERROR,
*************** getObjectDescription(const ObjectAddress
*** 3129,3134 ****
--- 3206,3226 ----
  				break;
  			}
  
+ 		case OCLASS_AM:
+ 			{
+ 				HeapTuple	tup;
+ 
+ 				tup = SearchSysCache1(AMOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, _("access method %s"),
+ 							NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
  		default:
  			appendStringInfo(&buffer, "unrecognized object %u %u %d",
  							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
new file mode 100644
index b1ac704..6b3742c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** subdir = src/backend/commands
*** 12,18 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
--- 12,18 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = amcmds.o aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
  	collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
  	dbcommands.o define.o discard.o dropcmds.o \
  	event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
new file mode 100644
index ...6c6996f
*** a/src/backend/commands/amcmds.c
--- b/src/backend/commands/amcmds.c
***************
*** 0 ****
--- 1,277 ----
+ /*-------------------------------------------------------------------------
+  *
+  * amcmds.c
+  *	  Routines for SQL commands that manipulate access methods.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  *
+  * IDENTIFICATION
+  *	  src/backend/commands/amcmds.c
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include "access/genam.h"
+ #include "access/heapam.h"
+ #include "access/htup_details.h"
+ #include "access/xact.h"
+ #include "catalog/binary_upgrade.h"
+ #include "catalog/catalog.h"
+ #include "catalog/dependency.h"
+ #include "catalog/heap.h"
+ #include "catalog/indexing.h"
+ #include "catalog/objectaccess.h"
+ #include "catalog/pg_authid.h"
+ #include "catalog/pg_am.h"
+ #include "catalog/pg_collation.h"
+ #include "catalog/pg_constraint.h"
+ #include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_language.h"
+ #include "catalog/pg_namespace.h"
+ #include "catalog/pg_proc.h"
+ #include "catalog/pg_proc_fn.h"
+ #include "catalog/pg_range.h"
+ #include "catalog/pg_type.h"
+ #include "catalog/pg_type_fn.h"
+ #include "commands/dbcommands.h"
+ #include "commands/defrem.h"
+ #include "commands/tablecmds.h"
+ #include "commands/typecmds.h"
+ #include "executor/executor.h"
+ #include "miscadmin.h"
+ #include "nodes/makefuncs.h"
+ #include "optimizer/planner.h"
+ #include "optimizer/var.h"
+ #include "parser/parse_coerce.h"
+ #include "parser/parse_collate.h"
+ #include "parser/parse_expr.h"
+ #include "parser/parse_func.h"
+ #include "parser/parse_type.h"
+ #include "utils/acl.h"
+ #include "utils/builtins.h"
+ #include "utils/fmgroids.h"
+ #include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+ #include "utils/rel.h"
+ #include "utils/snapmgr.h"
+ #include "utils/syscache.h"
+ #include "utils/tqual.h"
+ 
+ /*
+  * Convert a handler function name passed from the parser to an Oid. This
+  * function either return valid function Oid or throw an error.
+  */
+ static Oid
+ lookup_index_am_handler_func(List *handler_name)
+ {
+ 	Oid			handlerOid;
+ 	Oid			funcargtypes[1] = {INTERNALOID};
+ 
+ 	if (handler_name == NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+ 				 errmsg("handler function is not specified")));
+ 
+ 	/* handlers have no arguments */
+ 	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+ 
+ 	/* check that handler has correct return type */
+ 	if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 				 errmsg("function %s must return type \"index_am_handler\"",
+ 						NameListToString(handler_name))));
+ 
+ 	return handlerOid;
+ }
+ 
+ 
+ /*
+  * CreateAcessMethod
+  *		Registers a new access method.
+  */
+ ObjectAddress
+ CreateAccessMethod(CreateAmStmt *stmt)
+ {
+ 	Relation		rel;
+ 	ObjectAddress	myself;
+ 	ObjectAddress	referenced;
+ 	Oid				amoid;
+ 	Oid				amhandler;
+ 	bool			nulls[Natts_pg_am];
+ 	Datum			values[Natts_pg_am];
+ 	HeapTuple		tup;
+ 
+ 	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	/* Must be super user */
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 			errmsg("permission denied to create access method \"%s\"",
+ 				   stmt->amname),
+ 			errhint("Must be superuser to create access method.")));
+ 
+ 	/* Check if name is busy */
+ 	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+ 	if (OidIsValid(amoid))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("access method \"%s\" already exists", stmt->amname)));
+ 	}
+ 
+ 	/*
+ 	 * Get handler function oid. Handler signature depends on access method
+ 	 * type.
+ 	 */
+ 	switch(stmt->amtype)
+ 	{
+ 		case 'i':
+ 			amhandler = lookup_index_am_handler_func(stmt->handler_name);
+ 			break;
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ 					 errmsg("wrong access method type \"%c\"", stmt->amtype)));
+ 			break;
+ 	}
+ 
+ 	/*
+ 	 * Insert tuple into pg_am.
+ 	 */
+ 	memset(values, 0, sizeof(values));
+ 	memset(nulls, false, sizeof(nulls));
+ 
+ 	values[Anum_pg_am_amname - 1] =
+ 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+ 	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+ 	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+ 
+ 	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+ 
+ 	amoid = simple_heap_insert(rel, tup);
+ 	CatalogUpdateIndexes(rel, tup);
+ 	heap_freetuple(tup);
+ 
+ 	myself.classId = AccessMethodRelationId;
+ 	myself.objectId = amoid;
+ 	myself.objectSubId = 0;
+ 
+ 	/* Record dependecy on handler function */
+ 	referenced.classId = ProcedureRelationId;
+ 	referenced.objectId = amhandler;
+ 	referenced.objectSubId = 0;
+ 
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
+ 	recordDependencyOnCurrentExtension(&myself, false);
+ 
+ 	heap_close(rel, RowExclusiveLock);
+ 
+ 	return myself;
+ }
+ 
+ /*
+  * Guts of access method deletion.
+  */
+ void
+ RemoveAccessMethodById(Oid amOid)
+ {
+ 	Relation	relation;
+ 	HeapTuple	tup;
+ 
+ 	if (!superuser())
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ 				 errmsg("must be superuser to drop access methods")));
+ 
+ 	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (!HeapTupleIsValid(tup))
+ 		elog(ERROR, "cache lookup failed for access method %u", amOid);
+ 
+ 	simple_heap_delete(relation, &tup->t_self);
+ 
+ 	ReleaseSysCache(tup);
+ 
+ 	heap_close(relation, RowExclusiveLock);
+ }
+ 
+ /*
+  * Convert single charater access method type into string for error reporting.
+  */
+ static char *
+ get_am_type_string(char amtype)
+ {
+ 	switch (amtype)
+ 	{
+ 		case 'i':
+ 			return "index";
+ 		default:
+ 			elog(ERROR, "invalid access method type '%c'", amtype);
+ 	}
+ }
+ 
+ /*
+  * get_am_oid - given an access method name and type, look up the OID
+  *
+  * If missing_ok is false, throw an error if access method not found.  If
+  * true, just return InvalidOid.
+  */
+ Oid
+ get_am_oid(const char *amname, char amtype, bool missing_ok)
+ {
+ 	HeapTuple	tup;
+ 	Oid			oid = InvalidOid;
+ 
+ 	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		if (amform->amtype != amtype)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("access method \"%s\" type is not %s",
+ 							NameStr(amform->amname),
+ 							get_am_type_string(amtype))));
+ 
+ 		oid = HeapTupleGetOid(tup);
+ 		ReleaseSysCache(tup);
+ 	}
+ 
+ 	if (!OidIsValid(oid) && !missing_ok)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("access method \"%s\" does not exist", amname)));
+ 	return oid;
+ }
+ 
+ /*
+  * get_am_name - given an access method OID name and type, look up the name
+  *
+  * Access method type is not required for lookup.  However it's useful to check
+  * the type to ensure it is what we're looking for.
+  */
+ char *
+ get_am_name(Oid amOid)
+ {
+ 	HeapTuple	tup;
+ 	char	   *result = NULL;
+ 
+ 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+ 	if (HeapTupleIsValid(tup))
+ 	{
+ 		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+ 
+ 		result = pstrdup(NameStr(amform->amname));
+ 		ReleaseSysCache(tup);
+ 	}
+ 	return result;
+ }
+ 
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
new file mode 100644
index 9e32f8d..3f52ad8
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** typedef enum
*** 86,91 ****
--- 86,92 ----
  
  /* XXX merge this with ObjectTypeMap? */
  static event_trigger_support_data event_trigger_support[] = {
+ 	{"ACCESS METHOD", true},
  	{"AGGREGATE", true},
  	{"CAST", true},
  	{"CONSTRAINT", true},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 1078,1083 ****
--- 1079,1085 ----
  		case OBJECT_EVENT_TRIGGER:
  			/* no support for event triggers on event triggers */
  			return false;
+ 		case OBJECT_ACCESS_METHOD:
  		case OBJECT_AGGREGATE:
  		case OBJECT_AMOP:
  		case OBJECT_AMPROC:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 1167,1172 ****
--- 1169,1175 ----
  		case OCLASS_DEFACL:
  		case OCLASS_EXTENSION:
  		case OCLASS_POLICY:
+ 		case OCLASS_AM:
  			return true;
  	}
  
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
new file mode 100644
index 8a66196..d8da8d1
*** a/src/backend/commands/opclasscmds.c
--- b/src/backend/commands/opclasscmds.c
*************** DefineOpClass(CreateOpClassStmt *stmt)
*** 678,683 ****
--- 678,689 ----
  	myself.objectId = opclassoid;
  	myself.objectSubId = 0;
  
+ 	/* dependency on access method */
+ 	referenced.classId = AccessMethodRelationId;
+ 	referenced.objectId = amoid;
+ 	referenced.objectSubId = 0;
+ 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ 
  	/* dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = namespaceoid;
*************** DefineOpFamily(CreateOpFamilyStmt *stmt)
*** 743,749 ****
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
--- 749,755 ----
  					   get_namespace_name(namespaceoid));
  
  	/* Get access method OID, throwing an error if it doesn't exist. */
! 	amoid = get_am_oid(stmt->amname, 'i', false);
  
  	/* XXX Should we make any privilege check against the AM? */
  
*************** RemoveAmProcEntryById(Oid entryOid)
*** 1663,1683 ****
  	heap_close(rel, RowExclusiveLock);
  }
  
- char *
- get_am_name(Oid amOid)
- {
- 	HeapTuple	tup;
- 	char	   *result = NULL;
- 
- 	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
- 	if (HeapTupleIsValid(tup))
- 	{
- 		result = pstrdup(NameStr(((Form_pg_am) GETSTRUCT(tup))->amname));
- 		ReleaseSysCache(tup);
- 	}
- 	return result;
- }
- 
  /*
   * Subroutine for ALTER OPERATOR CLASS SET SCHEMA/RENAME
   *
--- 1669,1674 ----
*************** IsThereOpFamilyInNamespace(const char *o
*** 1723,1744 ****
  						get_am_name(opfmethod),
  						get_namespace_name(opfnamespace))));
  }
- 
- /*
-  * get_am_oid - given an access method name, look up the OID
-  *
-  * If missing_ok is false, throw an error if access method not found.  If
-  * true, just return InvalidOid.
-  */
- Oid
- get_am_oid(const char *amname, bool missing_ok)
- {
- 	Oid			oid;
- 
- 	oid = GetSysCacheOid1(AMNAME, CStringGetDatum(amname));
- 	if (!OidIsValid(oid) && !missing_ok)
- 		ereport(ERROR,
- 				(errcode(ERRCODE_UNDEFINED_OBJECT),
- 				 errmsg("access method \"%s\" does not exist", amname)));
- 	return oid;
- }
--- 1714,1716 ----
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index a9e9cc3..85bd5ac
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyCreateTransformStmt(const CreateTra
*** 3828,3833 ****
--- 3828,3845 ----
  	return newnode;
  }
  
+ static CreateAmStmt *
+ _copyCreateAmStmt(const CreateAmStmt *from)
+ {
+ 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
+ 
+ 	COPY_STRING_FIELD(amname);
+ 	COPY_NODE_FIELD(handler_name);
+ 	COPY_SCALAR_FIELD(amtype);
+ 
+ 	return newnode;
+ }
+ 
  static CreateTrigStmt *
  _copyCreateTrigStmt(const CreateTrigStmt *from)
  {
*************** copyObject(const void *from)
*** 4819,4824 ****
--- 4831,4839 ----
  		case T_CreateTransformStmt:
  			retval = _copyCreateTransformStmt(from);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _copyCreateAmStmt(from);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _copyCreateTrigStmt(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index b9c3959..f19fde1
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalCreateTransformStmt(const CreateTr
*** 1855,1860 ****
--- 1855,1870 ----
  }
  
  static bool
+ _equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+ {
+ 	COMPARE_STRING_FIELD(amname);
+ 	COMPARE_NODE_FIELD(handler_name);
+ 	COMPARE_SCALAR_FIELD(amtype);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
  {
  	COMPARE_STRING_FIELD(trigname);
*************** equal(const void *a, const void *b)
*** 3146,3151 ****
--- 3156,3164 ----
  		case T_CreateTransformStmt:
  			retval = _equalCreateTransformStmt(a, b);
  			break;
+ 		case T_CreateAmStmt:
+ 			retval = _equalCreateAmStmt(a, b);
+ 			break;
  		case T_CreateTrigStmt:
  			retval = _equalCreateTrigStmt(a, b);
  			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
new file mode 100644
index b307b48..fe95580
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 263,269 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 263,269 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt DropAmStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 604,610 ****
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
--- 604,610 ----
  	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
  	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
  
! 	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE
  
  	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
  	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
*************** stmt :
*** 789,794 ****
--- 789,795 ----
  			| CommentStmt
  			| ConstraintsSetStmt
  			| CopyStmt
+ 			| CreateAmStmt
  			| CreateAsStmt
  			| CreateAssertStmt
  			| CreateCastStmt
*************** stmt :
*** 823,828 ****
--- 824,830 ----
  			| DeleteStmt
  			| DiscardStmt
  			| DoStmt
+ 			| DropAmStmt
  			| DropAssertStmt
  			| DropCastStmt
  			| DropFdwStmt
*************** row_security_cmd:
*** 4708,4713 ****
--- 4710,4764 ----
  
  /*****************************************************************************
   *
+  *		QUERY:
+  *             CREATE ACCESS METHOD name HANDLER handler_name
+  *
+  *****************************************************************************/
+ 
+ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name
+ 				{
+ 					CreateAmStmt *n = makeNode(CreateAmStmt);
+ 					n->amname = $4;
+ 					n->handler_name = $8;
+ 					n->amtype = 'i';
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ /*****************************************************************************
+  *
+  *		QUERY :
+  *				DROP ACCESS METHOD name
+  *
+  ****************************************************************************/
+ 
+ DropAmStmt: DROP ACCESS METHOD name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($4)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = false;
+ 					n->behavior = $5;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 				|  DROP ACCESS METHOD IF_P EXISTS name opt_drop_behavior
+ 				{
+ 					DropStmt *n = makeNode(DropStmt);
+ 					n->removeType = OBJECT_ACCESS_METHOD;
+ 					n->objects = list_make1(list_make1(makeString($6)));
+ 					n->arguments = NIL;
+ 					n->missing_ok = true;
+ 					n->behavior = $7;
+ 					n->concurrent = false;
+ 					$$ = (Node *) n;
+ 				}
+ 		;
+ 
+ 
+ /*****************************************************************************
+  *
   *		QUERIES :
   *				CREATE TRIGGER ...
   *				DROP TRIGGER ...
*************** unreserved_keyword:
*** 13778,13783 ****
--- 13829,13835 ----
  			| MATCH
  			| MATERIALIZED
  			| MAXVALUE
+ 			| METHOD
  			| MINUTE_P
  			| MINVALUE
  			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
new file mode 100644
index dc431c7..d92864c
*** a/src/backend/parser/parse_utilcmd.c
--- b/src/backend/parser/parse_utilcmd.c
*************** transformIndexConstraint(Constraint *con
*** 1709,1715 ****
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
--- 1709,1715 ----
  		 * else dump and reload will produce a different index (breaking
  		 * pg_upgrade in particular).
  		 */
! 		if (index_rel->rd_rel->relam != get_am_oid(DEFAULT_INDEX_TYPE, 'i', false))
  			ereport(ERROR,
  					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
  					 errmsg("index \"%s\" is not a btree", index_name),
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 045f7f0..4d0aac9
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
*************** ProcessUtilitySlow(Node *parsetree,
*** 1520,1525 ****
--- 1520,1529 ----
  				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
  				break;
  
+ 			case T_CreateAmStmt:
+ 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+ 				break;
+ 
  			default:
  				elog(ERROR, "unrecognized node type: %d",
  					 (int) nodeTag(parsetree));
*************** CreateCommandTag(Node *parsetree)
*** 2160,2165 ****
--- 2164,2172 ----
  				case OBJECT_TRANSFORM:
  					tag = "DROP TRANSFORM";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "DROP ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2256,2261 ****
--- 2263,2271 ----
  				case OBJECT_COLLATION:
  					tag = "CREATE COLLATION";
  					break;
+ 				case OBJECT_ACCESS_METHOD:
+ 					tag = "CREATE ACCESS METHOD";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2519,2524 ****
--- 2529,2538 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateAmStmt:
+ 			tag = "CREATE ACCESS METHOD";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 3076,3081 ****
--- 3090,3099 ----
  			lev = LOGSTMT_DDL;
  			break;
  
+ 		case T_CreateAmStmt:
+ 			lev = LOGSTMT_DDL;
+ 			break;
+ 
  			/* already-planned queries */
  		case T_PlannedStmt:
  			{
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
new file mode 100644
index 46c95b0..516af92
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** string_to_bytea_const(const char *str, s
*** 6012,6032 ****
   *-------------------------------------------------------------------------
   */
  
! /*
!  * deconstruct_indexquals is a simple function to examine the indexquals
!  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
!  * structs, one per qual expression.
!  */
! typedef struct
! {
! 	RestrictInfo *rinfo;		/* the indexqual itself */
! 	int			indexcol;		/* zero-based index column number */
! 	bool		varonleft;		/* true if index column is on left of qual */
! 	Oid			clause_op;		/* qual's operator OID, if relevant */
! 	Node	   *other_operand;	/* non-index operand of qual's operator */
! } IndexQualInfo;
! 
! static List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
--- 6012,6018 ----
   *-------------------------------------------------------------------------
   */
  
! List *
  deconstruct_indexquals(IndexPath *path)
  {
  	List	   *result = NIL;
*************** orderby_operands_eval_cost(PlannerInfo *
*** 6176,6210 ****
  	return qual_arg_cost;
  }
  
! /*
!  * genericcostestimate is a general-purpose estimator that can be used for
!  * most index types.  In some cases we use genericcostestimate as the base
!  * code and then incorporate additional index-type-specific knowledge in
!  * the type-specific calling function.  To avoid code duplication, we make
!  * genericcostestimate return a number of intermediate values as well as
!  * its preliminary estimates of the output cost values.  The GenericCosts
!  * struct includes all these values.
!  *
!  * Callers should initialize all fields of GenericCosts to zero.  In addition,
!  * they can set numIndexTuples to some positive value if they have a better
!  * than default way of estimating the number of leaf index tuples visited.
!  */
! typedef struct
! {
! 	/* These are the values the cost estimator must return to the planner */
! 	Cost		indexStartupCost;		/* index-related startup cost */
! 	Cost		indexTotalCost; /* total index-related scan cost */
! 	Selectivity indexSelectivity;		/* selectivity of index */
! 	double		indexCorrelation;		/* order correlation of index */
! 
! 	/* Intermediate values we obtain along the way */
! 	double		numIndexPages;	/* number of leaf pages visited */
! 	double		numIndexTuples; /* number of leaf tuples visited */
! 	double		spc_random_page_cost;	/* relevant random_page_cost value */
! 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
! } GenericCosts;
! 
! static void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
--- 6162,6168 ----
  	return qual_arg_cost;
  }
  
! void
  genericcostestimate(PlannerInfo *root,
  					IndexPath *path,
  					double loop_count,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index f798b15..1acd91a
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, int *numTab
*** 98,103 ****
--- 98,104 ----
  	int			numProcLangs;
  	int			numCasts;
  	int			numTransforms;
+ 	int			numAccessMethods;
  	int			numOpclasses;
  	int			numOpfamilies;
  	int			numConversions;
*************** getSchemaData(Archive *fout, int *numTab
*** 169,174 ****
--- 170,179 ----
  	oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
  
  	if (g_verbose)
+ 		write_msg(NULL, "reading user-defined access methods\n");
+ 	getAccessMethods(fout, &numAccessMethods);
+ 
+ 	if (g_verbose)
  		write_msg(NULL, "reading user-defined operator classes\n");
  	getOpclasses(fout, &numOpclasses);
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 64c2673..8999bcc
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpFunc(Archive *fout, Func
*** 173,178 ****
--- 173,179 ----
  static void dumpCast(Archive *fout, CastInfo *cast);
  static void dumpTransform(Archive *fout, TransformInfo *transform);
  static void dumpOpr(Archive *fout, OprInfo *oprinfo);
+ static void dumpAccessMethod(Archive *fout, AccessMethodInfo *oprinfo);
  static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo);
  static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo);
  static void dumpCollation(Archive *fout, CollInfo *convinfo);
*************** getConversions(Archive *fout, int *numCo
*** 4101,4106 ****
--- 4102,4183 ----
  }
  
  /*
+  * getAccessMethods:
+  *	  read all user-defined access methods in the system catalogs and return
+  *    them in the AccessMethodInfo* structure
+  *
+  *	numAccessMethods is set to the number of access methods read in
+  */
+ AccessMethodInfo *
+ getAccessMethods(Archive *fout, int *numAccessMethods)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PGresult   *res;
+ 	int			ntups;
+ 	int			i;
+ 	PQExpBuffer query;
+ 	AccessMethodInfo *aminfo;
+ 	int			i_tableoid;
+ 	int			i_oid;
+ 	int			i_amname;
+ 	int			i_amhandler;
+ 
+ 	/* Before 9.6, there are no user-defined access methods */
+ 	if (fout->remoteVersion < 90600)
+ 	{
+ 		*numAccessMethods = 0;
+ 		return NULL;
+ 	}
+ 
+ 	query = createPQExpBuffer();
+ 
+ 	/* Make sure we are in proper schema */
+ 	selectSourceSchema(fout, "pg_catalog");
+ 
+ 	/*
+ 	 * Select only user-defined access methods assuming all built-in access
+ 	 * methods have oid < 10000.
+ 	 */
+ 	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+ 					  "amhandler::pg_catalog.regproc AS amhandler "
+ 					  "FROM pg_am "
+ 					  "WHERE oid >= 10000");
+ 
+ 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ 
+ 	ntups = PQntuples(res);
+ 	*numAccessMethods = ntups;
+ 
+ 	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+ 
+ 	i_tableoid = PQfnumber(res, "tableoid");
+ 	i_oid = PQfnumber(res, "oid");
+ 	i_amname = PQfnumber(res, "amname");
+ 	i_amhandler = PQfnumber(res, "amhandler");
+ 
+ 	for (i = 0; i < ntups; i++)
+ 	{
+ 		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+ 		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ 		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ 		AssignDumpId(&aminfo[i].dobj);
+ 		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+ 		aminfo[i].dobj.namespace = NULL;
+ 		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+ 
+ 		/* Decide whether we want to dump it */
+ 		selectDumpableObject(&(aminfo[i].dobj), dopt);
+ 	}
+ 
+ 	PQclear(res);
+ 
+ 	destroyPQExpBuffer(query);
+ 
+ 	return aminfo;
+ }
+ 
+ 
+ /*
   * getOpclasses:
   *	  read all opclasses in the system catalogs and return them in the
   * OpclassInfo* structure
*************** dumpDumpableObject(Archive *fout, Dumpab
*** 8408,8413 ****
--- 8485,8493 ----
  		case DO_OPERATOR:
  			dumpOpr(fout, (OprInfo *) dobj);
  			break;
+ 		case DO_ACCESS_METHOD:
+ 			dumpAccessMethod(fout, (AccessMethodInfo *) dobj);
+ 			break;
  		case DO_OPCLASS:
  			dumpOpclass(fout, (OpclassInfo *) dobj);
  			break;
*************** convertTSFunction(Archive *fout, Oid fun
*** 11446,11451 ****
--- 11526,11584 ----
  	return result;
  }
  
+ /*
+  * dumpAccessMethod
+  *	  write out a single access method definition
+  */
+ static void
+ dumpAccessMethod(Archive *fout, AccessMethodInfo *aminfo)
+ {
+ 	DumpOptions *dopt = fout->dopt;
+ 	PQExpBuffer q;
+ 	PQExpBuffer delq;
+ 	PQExpBuffer labelq;
+ 	char	   *qamname;
+ 
+ 	/* Skip if not to be dumped */
+ 	if (!aminfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	q = createPQExpBuffer();
+ 	delq = createPQExpBuffer();
+ 	labelq = createPQExpBuffer();
+ 
+ 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
+ 
+ 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s HANDLER %s;\n",
+ 					  qamname, aminfo->amhandler);
+ 
+ 	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+ 					  qamname);
+ 
+ 	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+ 					  qamname);
+ 
+ 	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+ 				 aminfo->dobj.name,
+ 				 NULL,
+ 				 NULL,
+ 				 "",
+ 				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+ 				 q->data, delq->data, NULL,
+ 				 NULL, 0,
+ 				 NULL, NULL);
+ 
+ 	/* Dump Access Method Comments */
+ 	dumpComment(fout, labelq->data,
+ 				NULL, "",
+ 				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+ 
+ 	free(qamname);
+ 
+ 	destroyPQExpBuffer(q);
+ 	destroyPQExpBuffer(delq);
+ 	destroyPQExpBuffer(labelq);
+ }
  
  /*
   * dumpOpclass
*************** addBoundaryDependencies(DumpableObject *
*** 16227,16232 ****
--- 16360,16366 ----
  			case DO_FUNC:
  			case DO_AGG:
  			case DO_OPERATOR:
+ 			case DO_ACCESS_METHOD:
  			case DO_OPCLASS:
  			case DO_OPFAMILY:
  			case DO_COLLATION:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
new file mode 100644
index 9a1d8f8..0fb6087
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 48,53 ****
--- 48,54 ----
  	DO_FUNC,
  	DO_AGG,
  	DO_OPERATOR,
+ 	DO_ACCESS_METHOD,
  	DO_OPCLASS,
  	DO_OPFAMILY,
  	DO_COLLATION,
*************** typedef struct _oprInfo
*** 167,172 ****
--- 168,179 ----
  	Oid			oprcode;
  } OprInfo;
  
+ typedef struct _accessMethodInfo
+ {
+ 	DumpableObject dobj;
+ 	char	   *amhandler;
+ } AccessMethodInfo;
+ 
  typedef struct _opclassInfo
  {
  	DumpableObject dobj;
*************** extern TypeInfo *getTypes(Archive *fout,
*** 548,553 ****
--- 555,561 ----
  extern FuncInfo *getFuncs(Archive *fout, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, int *numAggregates);
  extern OprInfo *getOperators(Archive *fout, int *numOperators);
+ extern AccessMethodInfo *getAccessMethods(Archive *fout, int *numAccessMethods);
  extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
  extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
  extern CollInfo *getCollations(Archive *fout, int *numCollations);
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
new file mode 100644
index 78ff59c..3bd5bd8
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 45,50 ****
--- 45,51 ----
  	2,							/* DO_FUNC */
  	3,							/* DO_AGG */
  	3,							/* DO_OPERATOR */
+ 	3,							/* DO_ACCESS_METHOD */
  	4,							/* DO_OPCLASS */
  	4,							/* DO_OPFAMILY */
  	4,							/* DO_COLLATION */
*************** static const int newObjectTypePriority[]
*** 95,100 ****
--- 96,102 ----
  	6,							/* DO_FUNC */
  	7,							/* DO_AGG */
  	8,							/* DO_OPERATOR */
+ 	8,							/* DO_ACCESS_METHOD */
  	9,							/* DO_OPCLASS */
  	9,							/* DO_OPFAMILY */
  	3,							/* DO_COLLATION */
*************** describeDumpableObject(DumpableObject *o
*** 1329,1334 ****
--- 1331,1340 ----
  					 "OPERATOR %s  (ID %d OID %u)",
  					 obj->name, obj->dumpId, obj->catId.oid);
  			return;
+ 		case DO_ACCESS_METHOD:
+ 			snprintf(buf, bufsize,
+ 					 "");
+ 			return;
  		case DO_OPCLASS:
  			snprintf(buf, bufsize,
  					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
new file mode 100644
index 049bf9f..ac16740
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 153,162 ****
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM			/* pg_transform */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_TRANSFORM
  
  
  /* in dependency.c */
--- 153,163 ----
  	OCLASS_EXTENSION,			/* pg_extension */
  	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
  	OCLASS_POLICY,				/* pg_policy */
! 	OCLASS_TRANSFORM,			/* pg_transform */
! 	OCLASS_AM,					/* pg_am */
  } ObjectClass;
  
! #define LAST_OCLASS		OCLASS_AM
  
  
  /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
new file mode 100644
index f801c3e..605e154
*** a/src/include/catalog/pg_am.h
--- b/src/include/catalog/pg_am.h
*************** CATALOG(pg_am,2601)
*** 35,40 ****
--- 35,47 ----
  {
  	NameData	amname;			/* access method name */
  	regproc		amhandler;		/* handler function */
+ 
+ 	/*----------
+ 	 * Type of access method. Possible values are
+ 	 *		'i': Index access method
+ 	 *----------
+ 	 */
+ 	char		amtype;
  } FormData_pg_am;
  
  /* ----------------
*************** typedef FormData_pg_am *Form_pg_am;
*** 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
  
--- 55,86 ----
   *		compiler constants for pg_am
   * ----------------
   */
! #define Natts_pg_am						3
  #define Anum_pg_am_amname				1
  #define Anum_pg_am_amhandler			2
+ #define Anum_pg_am_amtype				3
  
  /* ----------------
   *		initial contents of pg_am
   * ----------------
   */
  
! DATA(insert OID = 403 (  btree		bthandler	i ));
  DESCR("b-tree index access method");
  #define BTREE_AM_OID 403
! DATA(insert OID = 405 (  hash		hashhandler	i ));
  DESCR("hash index access method");
  #define HASH_AM_OID 405
! DATA(insert OID = 783 (  gist		gisthandler	i ));
  DESCR("GiST index access method");
  #define GIST_AM_OID 783
! DATA(insert OID = 2742 (  gin		ginhandler	i ));
  DESCR("GIN index access method");
  #define GIN_AM_OID 2742
! DATA(insert OID = 4000 (  spgist	spghandler	i ));
  DESCR("SP-GiST index access method");
  #define SPGIST_AM_OID 4000
! DATA(insert OID = 3580 (  brin		brinhandler	i ));
  DESCR("block range index (BRIN) access method");
  #define BRIN_AM_OID 3580
  
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
new file mode 100644
index 54f67e9..1e21f03
*** a/src/include/commands/defrem.h
--- b/src/include/commands/defrem.h
*************** extern void IsThereOpClassInNamespace(co
*** 91,98 ****
  						  Oid opcnamespace);
  extern void IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
  						   Oid opfnamespace);
- extern Oid	get_am_oid(const char *amname, bool missing_ok);
- 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);
  
--- 91,96 ----
*************** extern Datum transformGenericOptions(Oid
*** 137,142 ****
--- 135,146 ----
  						List *options,
  						Oid fdwvalidator);
  
+ /* commands/amcmds.c */
+ extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+ extern void RemoveAccessMethodById(Oid amOid);
+ extern Oid	get_am_oid(const char *amname, char amtype, bool missing_ok);
+ extern char *get_am_name(Oid amOid);
+ 
  /* support routines in commands/define.c */
  
  extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index c407fa2..7d46763
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 386,391 ****
--- 386,392 ----
  	T_CreatePolicyStmt,
  	T_AlterPolicyStmt,
  	T_CreateTransformStmt,
+ 	T_CreateAmStmt,
  
  	/*
  	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
new file mode 100644
index 2fd0629..8b958b4
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef struct SetOperationStmt
*** 1379,1384 ****
--- 1379,1385 ----
  
  typedef enum ObjectType
  {
+ 	OBJECT_ACCESS_METHOD,
  	OBJECT_AGGREGATE,
  	OBJECT_AMOP,
  	OBJECT_AMPROC,
*************** typedef struct AlterPolicyStmt
*** 2070,2075 ****
--- 2071,2088 ----
  	Node	   *with_check;		/* the policy's WITH CHECK condition. */
  } AlterPolicyStmt;
  
+ /*----------------------
+  *		Create ACCESS METHOD Statement
+  *----------------------
+  */
+ typedef struct CreateAmStmt
+ {
+ 	NodeTag		type;
+ 	char	   *amname;			/* access method name */
+ 	List	   *handler_name;	/* handler function name */
+ 	char		amtype;			/* type of access method */
+ } CreateAmStmt;
+ 
  /* ----------------------
   *		Create TRIGGER Statement
   * ----------------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
new file mode 100644
index 6e1e820..7de3404
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("mapping", MAPPING, UNRESERVE
*** 239,244 ****
--- 239,245 ----
  PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
  PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
  PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
  PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
  PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
  PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h
new file mode 100644
index 06fbca7..7fb7466
*** a/src/include/utils/selfuncs.h
--- b/src/include/utils/selfuncs.h
*************** typedef enum
*** 95,100 ****
--- 95,142 ----
  	Pattern_Prefix_None, Pattern_Prefix_Partial, Pattern_Prefix_Exact
  } Pattern_Prefix_Status;
  
+ /*
+  * deconstruct_indexquals is a simple function to examine the indexquals
+  * attached to a proposed IndexPath.  It returns a list of IndexQualInfo
+  * structs, one per qual expression.
+  */
+ typedef struct
+ {
+ 	RestrictInfo *rinfo;		/* the indexqual itself */
+ 	int			indexcol;		/* zero-based index column number */
+ 	bool		varonleft;		/* true if index column is on left of qual */
+ 	Oid			clause_op;		/* qual's operator OID, if relevant */
+ 	Node	   *other_operand;	/* non-index operand of qual's operator */
+ } IndexQualInfo;
+ 
+ /*
+  * genericcostestimate is a general-purpose estimator that can be used for
+  * most index types.  In some cases we use genericcostestimate as the base
+  * code and then incorporate additional index-type-specific knowledge in
+  * the type-specific calling function.  To avoid code duplication, we make
+  * genericcostestimate return a number of intermediate values as well as
+  * its preliminary estimates of the output cost values.  The GenericCosts
+  * struct includes all these values.
+  *
+  * Callers should initialize all fields of GenericCosts to zero.  In addition,
+  * they can set numIndexTuples to some positive value if they have a better
+  * than default way of estimating the number of leaf index tuples visited.
+  */
+ typedef struct
+ {
+ 	/* These are the values the cost estimator must return to the planner */
+ 	Cost		indexStartupCost;		/* index-related startup cost */
+ 	Cost		indexTotalCost; /* total index-related scan cost */
+ 	Selectivity indexSelectivity;		/* selectivity of index */
+ 	double		indexCorrelation;		/* order correlation of index */
+ 
+ 	/* Intermediate values we obtain along the way */
+ 	double		numIndexPages;	/* number of leaf pages visited */
+ 	double		numIndexTuples; /* number of leaf tuples visited */
+ 	double		spc_random_page_cost;	/* relevant random_page_cost value */
+ 	double		num_sa_scans;	/* # indexscans from ScalarArrayOps */
+ } GenericCosts;
+ 
  /* Hooks for plugins to get control when we ask for stats */
  typedef bool (*get_relation_stats_hook_type) (PlannerInfo *root,
  														  RangeTblEntry *rte,
*************** extern double estimate_num_groups(Planne
*** 191,196 ****
--- 233,244 ----
  extern Selectivity estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey,
  						 double nbuckets);
  
+ extern List *deconstruct_indexquals(IndexPath *path);
+ extern void genericcostestimate(PlannerInfo *root, IndexPath *path,
+ 								double loop_count,
+ 								List *qinfos,
+ 								GenericCosts *costs);
+ 
  /* Functions in array_selfuncs.c */
  
  extern Selectivity scalararraysel_containment(PlannerInfo *root,
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
new file mode 100644
index ...47d6024
*** a/src/test/regress/expected/create_am.out
--- b/src/test/regress/expected/create_am.out
***************
*** 0 ****
--- 1,108 ----
+ --
+ -- Create access method tests
+ --
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ ERROR:  data type box has no default operator class for access method "gist2"
+ HINT:  You must specify an operator class for the index or define a default operator class for the data type.
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+                            QUERY PLAN                           
+ ----------------------------------------------------------------
+  Sort
+    Sort Key: ((home_base[0])[0])
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base @ '(2000,1000),(200,200)'::box)
+ (4 rows)
+ 
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+        home_base       
+ -----------------------
+  (337,455),(240,359)
+  (1444,403),(1346,344)
+ (2 rows)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+                          QUERY PLAN                          
+ -------------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base && '(1000,1000),(0,0)'::box)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+  count 
+ -------
+      2
+ (1 row)
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+                       QUERY PLAN                       
+ -------------------------------------------------------
+  Aggregate
+    ->  Index Only Scan using grect2ind on fast_emp4000
+          Index Cond: (home_base IS NULL)
+ (3 rows)
+ 
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+  count 
+ -------
+    278
+ (1 row)
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ ERROR:  cannot drop access method gist2 because other objects depend on it
+ DETAIL:  operator class box_ops for access method gist2 depends on access method gist2
+ index grect2ind depends on operator class box_ops for access method gist2
+ HINT:  Use DROP ... CASCADE to drop the dependent objects too.
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ NOTICE:  drop cascades to 2 other objects
+ DETAIL:  drop cascades to operator class box_ops for access method gist2
+ drop cascades to index grect2ind
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
new file mode 100644
index eb0bc88..2c5be4b
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** e_star|f
*** 44,50 ****
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|t
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
--- 44,50 ----
  emp|f
  equipment_r|f
  f_star|f
! fast_emp4000|f
  float4_tbl|f
  float8_tbl|f
  func_index_heap|t
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index bec0316..8be4b83
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: create_index create_view
*** 60,66 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
--- 60,66 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
  
  # ----------
  # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 7e9b319..1de3da8
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: drop_if_exists
*** 75,80 ****
--- 75,81 ----
  test: updatable_views
  test: rolenames
  test: roleattributes
+ test: create_am
  test: sanity_check
  test: errors
  test: select
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
new file mode 100644
index ...e2051c5
*** a/src/test/regress/sql/create_am.sql
--- b/src/test/regress/sql/create_am.sql
***************
*** 0 ****
--- 1,73 ----
+ --
+ -- Create access method tests
+ --
+ 
+ -- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+ CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+ 
+ -- Drop old index on fast_emp4000
+ DROP INDEX grect2ind;
+ 
+ -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Make operator class for boxes using gist2
+ CREATE OPERATOR CLASS box_ops DEFAULT
+ 	FOR TYPE box USING gist2 AS
+ 	OPERATOR 1	<<,
+ 	OPERATOR 2	&<,
+ 	OPERATOR 3	&&,
+ 	OPERATOR 4	&>,
+ 	OPERATOR 5	>>,
+ 	OPERATOR 6	~=,
+ 	OPERATOR 7	@>,
+ 	OPERATOR 8	<@,
+ 	OPERATOR 9	&<|,
+ 	OPERATOR 10	<<|,
+ 	OPERATOR 11	|>>,
+ 	OPERATOR 12	|&>,
+ 	OPERATOR 13	~,
+ 	OPERATOR 14	@,
+ 	FUNCTION 1	gist_box_consistent(internal, box, smallint, oid, internal),
+ 	FUNCTION 2	gist_box_union(internal, internal),
+ 	FUNCTION 3	gist_box_compress(internal),
+ 	FUNCTION 4	gist_box_decompress(internal),
+ 	FUNCTION 5	gist_box_penalty(internal, internal, internal),
+ 	FUNCTION 6	gist_box_picksplit(internal, internal),
+ 	FUNCTION 7	gist_box_same(box, box, internal),
+ 	FUNCTION 9	gist_box_fetch(internal);
+ 
+ -- Create gist2 index on fast_emp4000
+ CREATE INDEX grect2ind ON fast_emp4000 USING gist2 (home_base);
+ 
+ -- Now check the results from plain indexscan
+ SET enable_seqscan = OFF;
+ SET enable_indexscan = ON;
+ SET enable_bitmapscan = OFF;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ SELECT * FROM fast_emp4000
+     WHERE home_base @ '(200,200),(2000,1000)'::box
+     ORDER BY (home_base[0])[0];
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
+ 
+ EXPLAIN (COSTS OFF)
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
+ 
+ -- Try to drop access method: fail because of depending objects
+ DROP ACCESS METHOD gist2;
+ 
+ -- Drop access method cascade
+ DROP ACCESS METHOD gist2 CASCADE;
+ 
+ -- Reset optimizer options
+ RESET enable_seqscan;
+ RESET enable_indexscan;
+ RESET enable_bitmapscan;