diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index 5f7befb858856fd75f6d598a62e9262f6ba681f7..b36889b856b275cd70d7b86204b824351c4e48a5 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -57,6 +57,12 @@
    to insert an appropriate row for themselves.
   </para>
 
+  <para>
+   Index access access methods can be defined and dropped using
+   <xref linkend="sql-create-access-method"> and
+    <xref linkend="sql-drop-access-method"> 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
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index bf95453b6c6d597ad8c66c8ccb04c35b5f68fd24..77667bdebd1e68028658a0274da3ad32e89b4def 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -52,6 +52,7 @@ Complete list of usable sgml source files in this directory.
 <!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">
@@ -94,6 +95,7 @@ Complete list of usable sgml source files in this directory.
 <!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 0000000000000000000000000000000000000000..3c091f8021094be133b8a5c021a61ba4cd5d193c
--- /dev/null
+++ b/doc/src/sgml/ref/create_access_method.sgml
@@ -0,0 +1,120 @@
+<!--
+doc/src/sgml/ref/create_access_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-create-access-method">
+ <indexterm zone="sql-create-access-method">
+  <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 <replaceable class="parameter">access_method_type</replaceable>
+    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>access_method_type</literal></term>
+    <listitem>
+     <para>
+      This clause specifies type of access method to define.
+      Only <literal>INDEX</literal> is supported at present.
+     </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 depends on the
+      type of access method; for <literal>INDEX</literal> access methods, it
+      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>heptree</> with
+   handler function <literal>heptree_handler</>:
+<programlisting>
+CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;
+</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-drop-access-method"></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 0000000000000000000000000000000000000000..97ed77ebda75729a1b002fa642e3bcd57e98d24a
--- /dev/null
+++ b/doc/src/sgml/ref/drop_access_method.sgml
@@ -0,0 +1,112 @@
+<!--
+doc/src/sgml/ref/drop_access_method.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-drop-access-method">
+ <indexterm zone="sql-drop-access-method">
+  <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>heptree</>:
+<programlisting>
+DROP ACCESS METHOD heptree;
+</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-create-access-method"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 03020dfec42ace90b4ad1695b66f7c475ad3ca46..8acdff1393fd2d1460f5e707c8ada4840bdf8620 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -80,6 +80,7 @@
    &commit;
    &commitPrepared;
    &copyTable;
+   &createAccessMethod;
    &createAggregate;
    &createCast;
    &createCollation;
@@ -122,6 +123,7 @@
    &delete;
    &discard;
    &do;
+   &dropAccessMethod;
    &dropAggregate;
    &dropCast;
    &dropCollation;
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
index bda166a9ef8ccfdeba31a776d266b8e3cbe455c6..d347ebcba45f7d8fa71ed98a7a496a65cbdba834 100644
--- a/src/backend/access/index/amapi.c
+++ b/src/backend/access/index/amapi.c
@@ -62,6 +62,13 @@ GetIndexAmRoutineByAmId(Oid amoid)
 			 amoid);
 	amform = (Form_pg_am) GETSTRUCT(tuple);
 
+	/* Check if it's index access method */
+	if (amform->amtype != AMTYPE_INDEX)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("access method \"%s\" is not of type %s",
+						NameStr(amform->amname), "INDEX")));
+
 	amhandler = amform->amhandler;
 
 	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index c48e37bf9a2424f892e1ddee71677ec66558ff29..17f9de1ff942f325fcb27aad45c1effe9efbc8f0 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -20,6 +20,7 @@
 #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"
@@ -141,6 +142,7 @@ static const Oid object_classes[] = {
 	OperatorRelationId,			/* OCLASS_OPERATOR */
 	OperatorClassRelationId,	/* OCLASS_OPCLASS */
 	OperatorFamilyRelationId,	/* OCLASS_OPFAMILY */
+	AccessMethodRelationId,		/* OCLASS_AM */
 	AccessMethodOperatorRelationId,		/* OCLASS_AMOP */
 	AccessMethodProcedureRelationId,	/* OCLASS_AMPROC */
 	RewriteRelationId,			/* OCLASS_REWRITE */
@@ -1199,6 +1201,10 @@ doDeletion(const ObjectAddress *object, int flags)
 			RemoveOpFamilyById(object->objectId);
 			break;
 
+		case OCLASS_AM:
+			RemoveAccessMethodById(object->objectId);
+			break;
+
 		case OCLASS_AMOP:
 			RemoveAmOpEntryById(object->objectId);
 			break;
@@ -2356,6 +2362,9 @@ getObjectClass(const ObjectAddress *object)
 		case OperatorFamilyRelationId:
 			return OCLASS_OPFAMILY;
 
+		case AccessMethodRelationId:
+			return OCLASS_AM;
+
 		case AccessMethodOperatorRelationId:
 			return OCLASS_AMOP;
 
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index d2aaa6ded92e6e2231d290c6a75884b1fe9cf3aa..cb3ba853f4ede38e341d6e8d4964e4b5030e915b 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -109,6 +109,18 @@ typedef struct
 
 static const ObjectPropertyType ObjectProperty[] =
 {
+	{
+		AccessMethodRelationId,
+		AmOidIndexId,
+		AMOID,
+		AMNAME,
+		Anum_pg_am_amname,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		-1,
+		true
+	},
 	{
 		CastRelationId,
 		CastOidIndexId,
@@ -561,6 +573,10 @@ static const struct object_type_map
 	{
 		"operator family", OBJECT_OPFAMILY
 	},
+	/* OCLASS_AM */
+	{
+		"access method", OBJECT_ACCESS_METHOD
+	},
 	/* OCLASS_AMOP */
 	{
 		"operator of access method", OBJECT_AMOP
@@ -795,6 +811,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 			case OBJECT_FDW:
 			case OBJECT_FOREIGN_SERVER:
 			case OBJECT_EVENT_TRIGGER:
+			case OBJECT_ACCESS_METHOD:
 				address = get_object_address_unqualified(objtype,
 														 objname, missing_ok);
 				break;
@@ -1019,6 +1036,9 @@ get_object_address_unqualified(ObjectType objtype,
 
 		switch (objtype)
 		{
+			case OBJECT_ACCESS_METHOD:
+				msg = gettext_noop("access method name cannot be qualified");
+				break;
 			case OBJECT_DATABASE:
 				msg = gettext_noop("database name cannot be qualified");
 				break;
@@ -1061,6 +1081,11 @@ get_object_address_unqualified(ObjectType objtype,
 	/* Translate name to OID. */
 	switch (objtype)
 	{
+		case OBJECT_ACCESS_METHOD:
+			address.classId = AccessMethodRelationId;
+			address.objectId = get_am_oid(name, missing_ok);
+			address.objectSubId = 0;
+			break;
 		case OBJECT_DATABASE:
 			address.classId = DatabaseRelationId;
 			address.objectId = get_database_oid(name, missing_ok);
@@ -1489,7 +1514,7 @@ get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
 	ObjectAddress address;
 
 	/* XXX no missing_ok support here */
-	amoid = get_am_oid(strVal(linitial(objname)), false);
+	amoid = get_index_am_oid(strVal(linitial(objname)), false);
 	objname = list_copy_tail(objname, 1);
 
 	switch (objtype)
@@ -2179,6 +2204,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 			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,
@@ -3129,6 +3155,21 @@ getObjectDescription(const ObjectAddress *object)
 				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,
@@ -3610,6 +3651,10 @@ getObjectTypeDescription(const ObjectAddress *object)
 			appendStringInfoString(&buffer, "transform");
 			break;
 
+		case OCLASS_AM:
+			appendStringInfoString(&buffer, "access method");
+			break;
+
 		default:
 			appendStringInfo(&buffer, "unrecognized %u", object->classId);
 			break;
@@ -4566,6 +4611,20 @@ getObjectIdentityParts(const ObjectAddress *object,
 			}
 			break;
 
+		case OCLASS_AM:
+			{
+				char	   *amname;
+
+				amname = get_am_name(object->objectId);
+				if (!amname)
+					elog(ERROR, "cache lookup failed for access method %u",
+						 object->objectId);
+				appendStringInfoString(&buffer, quote_identifier(amname));
+				if (objname)
+					*objname = list_make1(amname);
+			}
+			break;
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index b1ac704886feb047a8f6e89e4d3fbc9b1569fde9..6b3742c0a0847196b7696ee5cdb9698cd611ed86 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/commands
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o  \
+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 0000000000000000000000000000000000000000..7a937543916e2747fe4fe36494c5183767d768e9
--- /dev/null
+++ b/src/backend/commands/amcmds.c
@@ -0,0 +1,271 @@
+/*-------------------------------------------------------------------------
+ *
+ * amcmds.c
+ *	  Routines for SQL commands that manipulate access methods.
+ *
+ * Portions Copyright (c) 1996-2016, 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/heapam.h"
+#include "access/htup_details.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse_func.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+
+static Oid	lookup_index_am_handler_func(List *handler_name, char amtype);
+static char *get_am_type_string(char amtype);
+
+
+/*
+ * 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 an access method.")));
+
+	/* Check if name is used */
+	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+	if (OidIsValid(amoid))
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("access method \"%s\" already exists",
+						stmt->amname)));
+	}
+
+	/*
+	 * Get the handler function oid, verifying the AM type while at it.
+	 */
+	amhandler = lookup_index_am_handler_func(stmt->handler_name, stmt->amtype);
+
+	/*
+	 * 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 dependency 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);
+}
+
+/*
+ * get_am_type_oid
+ * 		Worker for various get_am_*_oid variants
+ *
+ * If missing_ok is false, throw an error if access method not found.  If
+ * true, just return InvalidOid.
+ *
+ * If amtype is not '\0', an error is raised if the AM found is not of the
+ * given type.
+ */
+static Oid
+get_am_type_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 (amtype != '\0' &&
+			amform->amtype != amtype)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("access method \"%s\" is not of type %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_index_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an index AM.
+ */
+Oid
+get_index_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, AMTYPE_INDEX, missing_ok);
+}
+
+/*
+ * get_am_oid - given an access method name, look up its OID.
+ * 		The type is not checked.
+ */
+Oid
+get_am_oid(const char *amname, bool missing_ok)
+{
+	return get_am_type_oid(amname, '\0', missing_ok);
+}
+
+/*
+ * get_am_name - given an access method OID name and type, look up its name.
+ */
+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;
+}
+
+/*
+ * Convert single charater access method type into string for error reporting.
+ */
+static char *
+get_am_type_string(char amtype)
+{
+	switch (amtype)
+	{
+		case AMTYPE_INDEX:
+			return "INDEX";
+		default:
+			/* shouldn't happen */
+			elog(ERROR, "invalid access method type '%c'", amtype);
+	}
+}
+
+/*
+ * Convert a handler function name to an Oid.  If the return type of the
+ * function doesn't match the given AM type, an error is raised.
+ *
+ * This function either return valid function Oid or throw an error.
+ */
+static Oid
+lookup_index_am_handler_func(List *handler_name, char amtype)
+{
+	Oid			handlerOid;
+	static const Oid funcargtypes[1] = {INTERNALOID};
+
+	if (handler_name == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("handler function is not specified")));
+
+	/* handlers have one argument of type internal */
+	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+
+	/* check that handler has the correct return type */
+	switch (amtype)
+	{
+		case AMTYPE_INDEX:
+			if (get_func_rettype(handlerOid) != INDEX_AM_HANDLEROID)
+				ereport(ERROR,
+						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+						 errmsg("function %s must return type \"%s\"",
+								NameListToString(handler_name),
+								"index_am_handler")));
+			break;
+		default:
+			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
+	}
+
+	return handlerOid;
+}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9e32f8d09b1fb746ecb65ddf1aebb5d8258d8bb1..3f52ad836b4a2be1d95047f0949607d8cbbb283d 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -86,6 +86,7 @@ typedef enum
 
 /* XXX merge this with ObjectTypeMap? */
 static event_trigger_support_data event_trigger_support[] = {
+	{"ACCESS METHOD", true},
 	{"AGGREGATE", true},
 	{"CAST", true},
 	{"CONSTRAINT", true},
@@ -1078,6 +1079,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		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:
@@ -1167,6 +1169,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
 		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
index 8a661968cd9f3bd32ccb2dc65aeab267a1760cfb..ac559fc9b41c3f324b8339ec16f52dd8cd4fdf9c 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -678,6 +678,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	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;
@@ -743,7 +749,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
 					   get_namespace_name(namespaceoid));
 
 	/* Get access method OID, throwing an error if it doesn't exist. */
-	amoid = get_am_oid(stmt->amname, false);
+	amoid = get_index_am_oid(stmt->amname, false);
 
 	/* XXX Should we make any privilege check against the AM? */
 
@@ -1663,21 +1669,6 @@ RemoveAmProcEntryById(Oid entryOid)
 	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
  *
@@ -1723,22 +1714,3 @@ IsThereOpFamilyInNamespace(const char *opfname, Oid opfmethod,
 						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;
-}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6b5d1d6efce66e7949d18e176c0f148b932fc28b..6378db8bbea1006f447190c8f23987f36a8d02e0 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3831,6 +3831,18 @@ _copyCreateTransformStmt(const CreateTransformStmt *from)
 	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)
 {
@@ -4822,6 +4834,9 @@ copyObject(const void *from)
 		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
index 87eb859e05e81004bb4eafb917d738d4a6a96d7b..854c062d32ff858b9cc371f027ec6b10a69b753e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1855,6 +1855,16 @@ _equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStm
 	return true;
 }
 
+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)
 {
@@ -3147,6 +3157,9 @@ equal(const void *a, const void *b)
 		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
index a74fb772e1496fcad618d16dcfca4491db618db9..12733528eb28b8181381ee5a618efad8bfa5e757 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -51,6 +51,7 @@
 
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_trigger.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
@@ -263,7 +264,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 		DeallocateStmt PrepareStmt ExecuteStmt
 		DropOwnedStmt ReassignOwnedStmt
 		AlterTSConfigurationStmt AlterTSDictionaryStmt
-		CreateMatViewStmt RefreshMatViewStmt
+		CreateMatViewStmt RefreshMatViewStmt CreateAmStmt
 
 %type <node>	select_no_parens select_with_parens select_clause
 				simple_select values_clause
@@ -604,7 +605,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	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
+	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
@@ -789,6 +790,7 @@ stmt :
 			| CommentStmt
 			| ConstraintsSetStmt
 			| CopyStmt
+			| CreateAmStmt
 			| CreateAsStmt
 			| CreateAssertStmt
 			| CreateCastStmt
@@ -4706,6 +4708,23 @@ row_security_cmd:
 		|	DELETE_P		{ $$ = "delete"; }
 		;
 
+/*****************************************************************************
+ *
+ *		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 = AMTYPE_INDEX;
+					$$ = (Node *) n;
+				}
+		;
+
 /*****************************************************************************
  *
  *		QUERIES :
@@ -5612,6 +5631,7 @@ drop_type:	TABLE									{ $$ = OBJECT_TABLE; }
 			| MATERIALIZED VIEW						{ $$ = OBJECT_MATVIEW; }
 			| INDEX									{ $$ = OBJECT_INDEX; }
 			| FOREIGN TABLE							{ $$ = OBJECT_FOREIGN_TABLE; }
+			| ACCESS METHOD							{ $$ = OBJECT_ACCESS_METHOD; }
 			| EVENT TRIGGER 						{ $$ = OBJECT_EVENT_TRIGGER; }
 			| COLLATION								{ $$ = OBJECT_COLLATION; }
 			| CONVERSION_P							{ $$ = OBJECT_CONVERSION; }
@@ -13778,6 +13798,7 @@ unreserved_keyword:
 			| MATCH
 			| MATERIALIZED
 			| MAXVALUE
+			| METHOD
 			| MINUTE_P
 			| MINVALUE
 			| MODE
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index dc431c7de0c4581019986ba555852728e313bbde..65284941ed98e6367277fd68a6ac5a9a942ee2e7 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1709,7 +1709,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 		 * 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))
+		if (index_rel->rd_rel->relam != get_index_am_oid(DEFAULT_INDEX_TYPE, 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
index 045f7f06ee27ffa517bd4576f3bd3e5af8dac286..4d0aac979fcc799507609cb83438a0ca927aa99e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1520,6 +1520,10 @@ ProcessUtilitySlow(Node *parsetree,
 				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
 				break;
 
+			case T_CreateAmStmt:
+				address = CreateAccessMethod((CreateAmStmt *) parsetree);
+				break;
+
 			default:
 				elog(ERROR, "unrecognized node type: %d",
 					 (int) nodeTag(parsetree));
@@ -2160,6 +2164,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_TRANSFORM:
 					tag = "DROP TRANSFORM";
 					break;
+				case OBJECT_ACCESS_METHOD:
+					tag = "DROP ACCESS METHOD";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2256,6 +2263,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_COLLATION:
 					tag = "CREATE COLLATION";
 					break;
+				case OBJECT_ACCESS_METHOD:
+					tag = "CREATE ACCESS METHOD";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2519,6 +2529,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "ALTER POLICY";
 			break;
 
+		case T_CreateAmStmt:
+			tag = "CREATE ACCESS METHOD";
+			break;
+
 		case T_PrepareStmt:
 			tag = "PREPARE";
 			break;
@@ -3076,6 +3090,10 @@ GetCommandLogLevel(Node *parsetree)
 			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
index d396ef142f9563ce5f620152983a873c082439c8..b2c57e87a5e5a0a8b3248db979506f9cb9290e3d 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6013,21 +6013,7 @@ string_to_bytea_const(const char *str, size_t str_len)
  *-------------------------------------------------------------------------
  */
 
-/*
- * 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 *
+List *
 deconstruct_indexquals(IndexPath *path)
 {
 	List	   *result = NIL;
@@ -6177,35 +6163,7 @@ orderby_operands_eval_cost(PlannerInfo *root, IndexPath *path)
 	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
+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
index f798b15e3c47ee6edf1902f9f6c7701c655ea43b..1acd91ab4449208185aa6924975cbcbbf43607f0 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -98,6 +98,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
 	int			numProcLangs;
 	int			numCasts;
 	int			numTransforms;
+	int			numAccessMethods;
 	int			numOpclasses;
 	int			numOpfamilies;
 	int			numConversions;
@@ -168,6 +169,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
 	oprinfo = getOperators(fout, &numOperators);
 	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
index 64c2673f9a598d748b611e6781a964c483ea6515..b3ef201a3ae2e45b78a493d8e6922284c54f8e73 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -45,6 +45,7 @@
 #include "access/attnum.h"
 #include "access/sysattr.h"
 #include "access/transam.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_default_acl.h"
@@ -173,6 +174,7 @@ static void dumpFunc(Archive *fout, FuncInfo *finfo);
 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);
@@ -1468,6 +1470,26 @@ selectDumpableProcLang(ProcLangInfo *plang, DumpOptions *dopt)
 		plang->dobj.dump = dopt->include_everything;
 }
 
+/*
+ * selectDumpableAccessMethod: policy-setting subroutine
+ *		Mark an access method as to be dumped or not
+ *
+ * Access methods do not belong to any particular namespace.  To identify
+ * built-in access methods, we must resort to checking whether the
+ * method's OID is in the range reserved for initdb.
+ */
+static void
+selectDumpableAccessMethod(AccessMethodInfo *method, DumpOptions *dopt)
+{
+	if (checkExtensionMembership(&method->dobj, dopt))
+		return;					/* extension membership overrides all else */
+
+	if (method->dobj.catId.oid < (Oid) FirstNormalObjectId)
+		method->dobj.dump = false;
+	else
+		method->dobj.dump = dopt->include_everything;
+}
+
 /*
  * selectDumpableExtension: policy-setting subroutine
  *		Mark an extension as to be dumped or not
@@ -4100,6 +4122,84 @@ getConversions(Archive *fout, int *numConversions)
 	return convinfo;
 }
 
+/*
+ * 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;
+	int			i_amtype;
+
+	/* 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, amtype, "
+					  "amhandler::pg_catalog.regproc AS amhandler "
+					  "FROM pg_am");
+
+	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");
+	i_amtype = PQfnumber(res, "amtype");
+
+	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));
+		aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
+
+		/* Decide whether we want to dump it */
+		selectDumpableAccessMethod(&(aminfo[i]), dopt);
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return aminfo;
+}
+
+
 /*
  * getOpclasses:
  *	  read all opclasses in the system catalogs and return them in the
@@ -8408,6 +8508,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
 		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;
@@ -11446,6 +11549,74 @@ convertTSFunction(Archive *fout, Oid funcOid)
 	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 ", qamname);
+
+	switch (aminfo->amtype)
+	{
+		case AMTYPE_INDEX:
+			appendPQExpBuffer(q, "TYPE INDEX ");
+			break;
+		default:
+			write_msg(NULL, "WARNING: invalid type %c of access method %s\n",
+					  aminfo->amtype, qamname);
+			destroyPQExpBuffer(q);
+			destroyPQExpBuffer(delq);
+			destroyPQExpBuffer(labelq);
+			return;
+	}
+
+	appendPQExpBuffer(q, "HANDLER %s;\n", 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
@@ -16227,6 +16398,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
 			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
index 9a1d8f863cc3d3ec83c9fe7a0d83ef46bbcf3ff1..66e693183ae40ef2d7806eb517745e82fcb1db0a 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -48,6 +48,7 @@ typedef enum
 	DO_FUNC,
 	DO_AGG,
 	DO_OPERATOR,
+	DO_ACCESS_METHOD,
 	DO_OPCLASS,
 	DO_OPFAMILY,
 	DO_COLLATION,
@@ -167,6 +168,13 @@ typedef struct _oprInfo
 	Oid			oprcode;
 } OprInfo;
 
+typedef struct _accessMethodInfo
+{
+	DumpableObject dobj;
+	char		amtype;
+	char	   *amhandler;
+} AccessMethodInfo;
+
 typedef struct _opclassInfo
 {
 	DumpableObject dobj;
@@ -548,6 +556,7 @@ extern TypeInfo *getTypes(Archive *fout, int *numTypes);
 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
index 78ff59c342998295ceaee04f1a074b90787842c7..36de6b625739c8da40dc5e5df61aa2aaa14259dc 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -28,8 +28,8 @@ static const char *modulename = gettext_noop("sorter");
  * by OID.  (This is a relatively crude hack to provide semi-reasonable
  * behavior for old databases without full dependency info.)  Note: collations,
  * extensions, text search, foreign-data, materialized view, event trigger,
- * policies, transforms, and default ACL objects can't really happen here, so the rather
- * bogus priorities for them don't matter.
+ * policies, transforms, access methods and default ACL objects can't really
+ * happen here, so the rather bogus priorities for them don't matter.
  *
  * NOTE: object-type priorities must match the section assignments made in
  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
@@ -45,6 +45,7 @@ static const int oldObjectTypePriority[] =
 	2,							/* DO_FUNC */
 	3,							/* DO_AGG */
 	3,							/* DO_OPERATOR */
+	3,							/* DO_ACCESS_METHOD */
 	4,							/* DO_OPCLASS */
 	4,							/* DO_OPFAMILY */
 	4,							/* DO_COLLATION */
@@ -95,6 +96,7 @@ static const int newObjectTypePriority[] =
 	6,							/* DO_FUNC */
 	7,							/* DO_AGG */
 	8,							/* DO_OPERATOR */
+	8,							/* DO_ACCESS_METHOD */
 	9,							/* DO_OPCLASS */
 	9,							/* DO_OPFAMILY */
 	3,							/* DO_COLLATION */
@@ -1329,6 +1331,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 					 "OPERATOR %s  (ID %d OID %u)",
 					 obj->name, obj->dumpId, obj->catId.oid);
 			return;
+		case DO_ACCESS_METHOD:
+			snprintf(buf, bufsize,
+					 "ACCESS METHOD %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
+			return;
 		case DO_OPCLASS:
 			snprintf(buf, bufsize,
 					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 3568cb27e814705045424fa17c8866f465a69967..b690dc6b34f466e84f69026e928e69d48729ec9b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201603211
+#define CATALOG_VERSION_NO	201603231
 
 #endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 049bf9f5a2e90939957d9f8ddc23ba70e1937c77..d41abc4e48091891f32e56e34bbb269629ca8fac 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -134,6 +134,7 @@ typedef enum ObjectClass
 	OCLASS_OPERATOR,			/* pg_operator */
 	OCLASS_OPCLASS,				/* pg_opclass */
 	OCLASS_OPFAMILY,			/* pg_opfamily */
+	OCLASS_AM,					/* pg_am */
 	OCLASS_AMOP,				/* pg_amop */
 	OCLASS_AMPROC,				/* pg_amproc */
 	OCLASS_REWRITE,				/* pg_rewrite */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index f801c3ee5779a8675bed504c7936e858dd2fe1bc..11169237991e8a941813bbc5ea5f9f03c7e31291 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -35,6 +35,7 @@ CATALOG(pg_am,2601)
 {
 	NameData	amname;			/* access method name */
 	regproc		amhandler;		/* handler function */
+	char		amtype;			/* see AMTYPE_xxx constants below */
 } FormData_pg_am;
 
 /* ----------------
@@ -48,31 +49,38 @@ typedef FormData_pg_am *Form_pg_am;
  *		compiler constants for pg_am
  * ----------------
  */
-#define Natts_pg_am						2
+#define Natts_pg_am						3
 #define Anum_pg_am_amname				1
 #define Anum_pg_am_amhandler			2
+#define Anum_pg_am_amtype				3
+
+/* ----------------
+ *		compiler constant for amtype
+ * ----------------
+ */
+#define AMTYPE_INDEX					'i'		/* index access method */
 
 /* ----------------
  *		initial contents of pg_am
  * ----------------
  */
 
-DATA(insert OID = 403 (  btree		bthandler ));
+DATA(insert OID = 403 (  btree		bthandler	i ));
 DESCR("b-tree index access method");
 #define BTREE_AM_OID 403
-DATA(insert OID = 405 (  hash		hashhandler ));
+DATA(insert OID = 405 (  hash		hashhandler i ));
 DESCR("hash index access method");
 #define HASH_AM_OID 405
-DATA(insert OID = 783 (  gist		gisthandler ));
+DATA(insert OID = 783 (  gist		gisthandler i ));
 DESCR("GiST index access method");
 #define GIST_AM_OID 783
-DATA(insert OID = 2742 (  gin		ginhandler ));
+DATA(insert OID = 2742 (  gin		ginhandler	i ));
 DESCR("GIN index access method");
 #define GIN_AM_OID 2742
-DATA(insert OID = 4000 (  spgist	spghandler ));
+DATA(insert OID = 4000 (  spgist	spghandler	i ));
 DESCR("SP-GiST index access method");
 #define SPGIST_AM_OID 4000
-DATA(insert OID = 3580 (  brin		brinhandler ));
+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
index 54f67e9eea62a1d75c94f6c5fcd5ddbcddd15b1c..b064eb4836a74b57cd7a07a26738781b09285762 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -91,8 +91,6 @@ extern void IsThereOpClassInNamespace(const char *opcname, Oid opcmethod,
 						  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);
 
@@ -137,6 +135,13 @@ extern Datum transformGenericOptions(Oid catalogId,
 						List *options,
 						Oid fdwvalidator);
 
+/* commands/amcmds.c */
+extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
+extern void RemoveAccessMethodById(Oid amOid);
+extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_am_oid(const char *amname, 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
index 42c958258b698a952bba43c3f99ff68e1268a4c0..734df771eb2bb0f76b3fd9f3d22a5239f2154859 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -402,6 +402,7 @@ typedef enum NodeTag
 	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
index 2fd06295e5c230783d1ecd0a2ff4227455fa51b5..8b958b422cbcb9f13f4ac880207bd79e0d8dfd02 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1379,6 +1379,7 @@ typedef struct SetOperationStmt
 
 typedef enum ObjectType
 {
+	OBJECT_ACCESS_METHOD,
 	OBJECT_AGGREGATE,
 	OBJECT_AMOP,
 	OBJECT_AMPROC,
@@ -2070,6 +2071,18 @@ typedef struct AlterPolicyStmt
 	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
index 6e1e82027cbbcf4cd18abc451d3113b595e4dd6e..7de3404aa227f01b64cd4e5bb21dca281a3bf77a 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -239,6 +239,7 @@ PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
 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
index 06fbca719b56563cb4864ccd946508e8355f1de7..8e0d317468748d1ecb243206e51610dace2549c3 100644
--- a/src/include/utils/selfuncs.h
+++ b/src/include/utils/selfuncs.h
@@ -95,6 +95,48 @@ typedef enum
 	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,
@@ -191,6 +233,12 @@ extern double estimate_num_groups(PlannerInfo *root, List *groupExprs,
 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 0000000000000000000000000000000000000000..47d6024610dad14a906bd89f22a4bbb594d4f7bb
--- /dev/null
+++ b/src/test/regress/expected/create_am.out
@@ -0,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/object_address.out b/src/test/regress/expected/object_address.out
index 75751bebeff157d21297592d201b73fd19b2739b..5310c956febef25f2db578ba91a536d38c47282c 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -325,6 +325,10 @@ SELECT pg_get_object_address('event trigger', '{one}', '{}');
 ERROR:  event trigger "one" does not exist
 SELECT pg_get_object_address('event trigger', '{one,two}', '{}');
 ERROR:  event trigger name cannot be qualified
+SELECT pg_get_object_address('access method', '{one}', '{}');
+ERROR:  access method "one" does not exist
+SELECT pg_get_object_address('access method', '{one,two}', '{}');
+ERROR:  access method name cannot be qualified
 -- test successful cases
 WITH objects (type, name, args) AS (VALUES
 				('table', '{addr_nsp, gentable}'::text[], '{}'::text[]),
@@ -373,7 +377,8 @@ WITH objects (type, name, args) AS (VALUES
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}'),
-				('transform', '{int}', '{sql}')
+				('transform', '{int}', '{sql}'),
+				('access method', '{btree}', '{}')
         )
 SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
 	-- test roundtrip through pg_identify_object_as_address
@@ -405,6 +410,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  server                    |            | addr_fserv        | addr_fserv                                                           | t
  user mapping              |            |                   | regtest_addr_user on server integer                                  | t
  foreign-data wrapper      |            | addr_fdw          | addr_fdw                                                             | t
+ access method             |            | btree             | btree                                                                | t
  operator of access method |            |                   | operator 1 (integer, integer) of pg_catalog.integer_ops USING btree  | t
  function of access method |            |                   | function 2 (integer, integer) of pg_catalog.integer_ops USING btree  | t
  default value             |            |                   | for addr_nsp.gentable.b                                              | t
@@ -426,7 +432,7 @@ SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
  text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
  text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
  text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
-(41 rows)
+(42 rows)
 
 ---
 --- Cleanup resources
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index eb0bc88ef1fb27daee22dd4a2de684df35507417..2c5be4bae4bb0fa80db1b7d151e647023aa5a7f8 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -44,7 +44,7 @@ e_star|f
 emp|f
 equipment_r|f
 f_star|f
-fast_emp4000|t
+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
index bec03165a73213c5e916b4eb02febe96ff67d0b0..8be4b831a1efc8dd7cbf7f0edde19a333d7ee80b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -60,7 +60,7 @@ test: create_index create_view
 # ----------
 # 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
+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
index 7e9b319b0f7091ccd865df2f83ed21d1a76d8a41..1de3da8bac0a985bcaddd354cf3dee487e2f5fbd 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -75,6 +75,7 @@ test: drop_if_exists
 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 0000000000000000000000000000000000000000..e2051c5fcd32f7412dcd2fbabb9cbea9bc14382e
--- /dev/null
+++ b/src/test/regress/sql/create_am.sql
@@ -0,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;
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 68e7cb0eb3a55c66dd6b39b3f46afffa17da95de..b5662349bcd01250a421f65c2a809ec5a862ce7e 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -117,6 +117,8 @@ SELECT pg_get_object_address('extension', '{one}', '{}');
 SELECT pg_get_object_address('extension', '{one,two}', '{}');
 SELECT pg_get_object_address('event trigger', '{one}', '{}');
 SELECT pg_get_object_address('event trigger', '{one,two}', '{}');
+SELECT pg_get_object_address('access method', '{one}', '{}');
+SELECT pg_get_object_address('access method', '{one,two}', '{}');
 
 -- test successful cases
 WITH objects (type, name, args) AS (VALUES
@@ -166,7 +168,8 @@ WITH objects (type, name, args) AS (VALUES
 				-- extension
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}'),
-				('transform', '{int}', '{sql}')
+				('transform', '{int}', '{sql}'),
+				('access method', '{btree}', '{}')
         )
 SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
 	-- test roundtrip through pg_identify_object_as_address