From 919b3699a968628b9e1ba4b8238cfa3b984afb5b Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <emre@hasegeli.com>
Date: Sun, 13 Jun 2021 09:14:08 +0300
Subject: [PATCH] [v01] Implement INTERFACE access method

---
 src/backend/access/Makefile                |   3 +-
 src/backend/access/index/amapi.c           |  50 +------
 src/backend/access/interface/Makefile      |  18 +++
 src/backend/access/interface/ifamapi.c     | 154 +++++++++++++++++++++
 src/backend/access/interface/ordering.c    |  39 ++++++
 src/backend/catalog/Makefile               |   2 +-
 src/backend/catalog/index.c                |   4 +-
 src/backend/catalog/objectaddress.c        |   2 +-
 src/backend/commands/amcmds.c              | 126 +++++++++++++++--
 src/backend/commands/indexcmds.c           |  90 +++++++++---
 src/backend/commands/opclasscmds.c         |  15 +-
 src/backend/commands/tablecmds.c           |   2 +-
 src/backend/commands/typecmds.c            |   2 +-
 src/backend/nodes/copyfuncs.c              |   1 +
 src/backend/nodes/equalfuncs.c             |   1 +
 src/backend/parser/gram.y                  |  19 ++-
 src/backend/parser/parse_utilcmd.c         |   6 +-
 src/backend/utils/adt/pseudotypes.c        |   1 +
 src/backend/utils/adt/ruleutils.c          |   2 +-
 src/backend/utils/cache/typcache.c         |   4 +-
 src/bin/pg_dump/pg_dump.c                  |   3 +
 src/bin/psql/describe.c                    |   2 +
 src/include/access/ifam.h                  |  64 +++++++++
 src/include/catalog/pg_am.dat              |   3 +
 src/include/catalog/pg_am.h                |   1 +
 src/include/catalog/pg_amimplements.dat    |  17 +++
 src/include/catalog/pg_amimplements.h      |  51 +++++++
 src/include/catalog/pg_proc.dat            |  13 ++
 src/include/catalog/pg_type.dat            |   6 +
 src/include/commands/defrem.h              |   4 +-
 src/include/nodes/nodes.h                  |   1 +
 src/include/nodes/parsenodes.h             |   1 +
 src/include/parser/kwlist.h                |   2 +
 src/test/regress/expected/amutils.out      |   8 +-
 src/test/regress/expected/create_am.out    |  12 +-
 src/test/regress/expected/oidjoins.out     |   2 +
 src/test/regress/expected/psql.out         | 100 +++++++------
 src/test/regress/expected/sanity_check.out |   1 +
 src/test/regress/sql/create_am.sql         |   9 +-
 39 files changed, 691 insertions(+), 150 deletions(-)
 create mode 100644 src/backend/access/interface/Makefile
 create mode 100644 src/backend/access/interface/ifamapi.c
 create mode 100644 src/backend/access/interface/ordering.c
 create mode 100644 src/include/access/ifam.h
 create mode 100644 src/include/catalog/pg_amimplements.dat
 create mode 100644 src/include/catalog/pg_amimplements.h

diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 0880e0a8bb..0e02299c7c 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -1,14 +1,15 @@
 #
 # Makefile for the access methods module
 #
 # src/backend/access/Makefile
 #
 
 subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist \
+SUBDIRS	    = brin common gin gist hash heap index interface \
+			  nbtree rmgrdesc spgist \
 			  table tablesample transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
index d30bc43514..e9dbbf4e1e 100644
--- a/src/backend/access/index/amapi.c
+++ b/src/backend/access/index/amapi.c
@@ -9,22 +9,20 @@
  * IDENTIFICATION
  *	  src/backend/access/index/amapi.c
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "access/amapi.h"
 #include "access/htup_details.h"
 #include "catalog/pg_am.h"
-#include "catalog/pg_opclass.h"
-#include "utils/builtins.h"
 #include "utils/syscache.h"
 
 
 /*
  * GetIndexAmRoutine - call the specified access method handler routine to get
  * its IndexAmRoutine struct, which will be palloc'd in the caller's context.
  *
  * Note that if the amhandler function is built-in, this will not involve
  * any catalog access.  It's therefore safe to use this while bootstrapping
  * indexes for the system catalogs.  relcache.c relies on that.
@@ -71,73 +69,33 @@ GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
 	amform = (Form_pg_am) GETSTRUCT(tuple);
 
 	/* Check if it's an index access method as opposed to some other AM */
 	if (amform->amtype != AMTYPE_INDEX)
 	{
 		if (noerror)
 		{
 			ReleaseSysCache(tuple);
 			return NULL;
 		}
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("access method \"%s\" is not of type %s",
-						NameStr(amform->amname), "INDEX")));
+		elog(ERROR, "access method \"%s\" is not of type INDEX",
+			 NameStr(amform->amname));
 	}
 
 	amhandler = amform->amhandler;
 
 	/* Complain if handler OID is invalid */
 	if (!RegProcedureIsValid(amhandler))
 	{
 		if (noerror)
 		{
 			ReleaseSysCache(tuple);
 			return NULL;
 		}
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-				 errmsg("index access method \"%s\" does not have a handler",
-						NameStr(amform->amname))));
+		elog(ERROR, "access method \"%s\" does not have a handler",
+			 NameStr(amform->amname));
 	}
 
 	ReleaseSysCache(tuple);
 
 	/* And finally, call the handler function to get the API struct. */
 	return GetIndexAmRoutine(amhandler);
 }
-
-
-/*
- * Ask appropriate access method to validate the specified opclass.
- */
-Datum
-amvalidate(PG_FUNCTION_ARGS)
-{
-	Oid			opclassoid = PG_GETARG_OID(0);
-	bool		result;
-	HeapTuple	classtup;
-	Form_pg_opclass classform;
-	Oid			amoid;
-	IndexAmRoutine *amroutine;
-
-	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
-	if (!HeapTupleIsValid(classtup))
-		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
-	classform = (Form_pg_opclass) GETSTRUCT(classtup);
-
-	amoid = classform->opcmethod;
-
-	ReleaseSysCache(classtup);
-
-	amroutine = GetIndexAmRoutineByAmId(amoid, false);
-
-	if (amroutine->amvalidate == NULL)
-		elog(ERROR, "function amvalidate is not defined for index access method %u",
-			 amoid);
-
-	result = amroutine->amvalidate(opclassoid);
-
-	pfree(amroutine);
-
-	PG_RETURN_BOOL(result);
-}
diff --git a/src/backend/access/interface/Makefile b/src/backend/access/interface/Makefile
new file mode 100644
index 0000000000..1cc71b1685
--- /dev/null
+++ b/src/backend/access/interface/Makefile
@@ -0,0 +1,18 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/interface
+#
+# IDENTIFICATION
+#    src/backend/access/interface/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/interface
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = \
+	ifamapi.o ordering.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/interface/ifamapi.c b/src/backend/access/interface/ifamapi.c
new file mode 100644
index 0000000000..8822d51bc7
--- /dev/null
+++ b/src/backend/access/interface/ifamapi.c
@@ -0,0 +1,154 @@
+/*-------------------------------------------------------------------------
+ *
+ * ifamapi.c
+ *	  interface access method routines
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/interface/ifamapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/amapi.h"
+#include "access/htup_details.h"
+#include "access/ifam.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_opclass.h"
+#include "utils/fmgrprotos.h"
+#include "utils/syscache.h"
+
+/*
+ * GetInterfaceAmRoutine - call the specified access method handler routine
+ * to get its InterfaceAmRoutine struct, which will be palloc'd in the caller's
+ * context.
+ *
+ * Note that if the amhandler function is built-in, this will not involve
+ * any catalog access.  It's therefore safe to use this while bootstrapping
+ * indexes for the system catalogs.  relcache.c relies on that.
+ */
+static InterfaceAmRoutine *
+GetInterfaceAmRoutine(Oid amhandler)
+{
+	Datum		datum;
+	InterfaceAmRoutine *routine;
+
+	datum = OidFunctionCall0(amhandler);
+	routine = (InterfaceAmRoutine *) DatumGetPointer(datum);
+
+	if (routine == NULL || !IsA(routine, InterfaceAmRoutine))
+		elog(ERROR, "interface access method handler function %u did not return an InterfaceAmRoutine struct",
+			 amhandler);
+
+	return routine;
+}
+
+/*
+ * TranslateIndexToInterfaceAmRoutine - gets the IndexAmRoutine struct and
+ * turnes it to InterfaceAmRoutine, which will be palloc'd in the caller's
+ * context.
+ */
+static InterfaceAmRoutine *
+GetInterfaceAmRoutineFromIndexAmHander(Oid amhandler)
+{
+	IndexAmRoutine *indexroutine;
+	InterfaceAmRoutine *ifroutine;
+
+	indexroutine = GetIndexAmRoutine(amhandler);
+
+	ifroutine = makeNode(InterfaceAmRoutine);
+
+	ifroutine->amstrategies = indexroutine->amstrategies;
+	ifroutine->amsupport = indexroutine->amsupport;
+	ifroutine->amoptsprocnum = indexroutine->amoptsprocnum;
+	ifroutine->amcanorder = indexroutine->amcanorder;
+	ifroutine->amcanorderbyop = indexroutine->amcanorderbyop;
+	ifroutine->amvalidate = indexroutine->amvalidate;
+	ifroutine->amadjustmembers = indexroutine->amadjustmembers;
+
+	pfree(indexroutine);
+
+	return ifroutine;
+}
+
+/*
+ * GetInterfaceAmRoutineByAmId - look up the handler of the index access method
+ * with the given OID, and get its InterfaceAmRoutine struct.
+ */
+InterfaceAmRoutine *
+GetInterfaceAmRoutineByAmId(Oid amoid)
+{
+	char		amtype;
+	HeapTuple	tuple;
+	Form_pg_am	amform;
+	regproc		amhandler;
+
+	/* Get handler function OID for the access method */
+	tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(amoid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for access method %u",
+			 amoid);
+
+	amform = (Form_pg_am) GETSTRUCT(tuple);
+	amtype = amform->amtype;
+
+	/* Check if it's an index access method as opposed to some other AM */
+	if (amtype != AMTYPE_INTERFACE && amtype != AMTYPE_INDEX)
+		elog(ERROR, "access method \"%s\" is not of type INDEX or INTERFACE",
+			 NameStr(amform->amname));
+
+	amhandler = amform->amhandler;
+
+	/* Complain if handler OID is invalid */
+	if (!RegProcedureIsValid(amhandler))
+		elog(ERROR, "access method \"%s\" does not have a handler",
+			 NameStr(amform->amname));
+
+	ReleaseSysCache(tuple);
+
+	/* And finally, call the handler function to get the API struct */
+	if (amtype == AMTYPE_INTERFACE)
+		return GetInterfaceAmRoutine(amhandler);
+	else
+		return GetInterfaceAmRoutineFromIndexAmHander(amhandler);
+}
+
+/*
+ * Ask appropriate access method to validate the specified opclass.
+ */
+Datum
+amvalidate(PG_FUNCTION_ARGS)
+{
+	Oid			opclassoid = PG_GETARG_OID(0);
+	bool		result;
+	HeapTuple	classtup;
+	Form_pg_opclass classform;
+	Oid			amoid;
+	InterfaceAmRoutine *amroutine;
+
+	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
+	if (!HeapTupleIsValid(classtup))
+		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
+	classform = (Form_pg_opclass) GETSTRUCT(classtup);
+
+	amoid = classform->opcmethod;
+
+	ReleaseSysCache(classtup);
+
+	amroutine = GetInterfaceAmRoutineByAmId(amoid);
+
+	if (amroutine->amvalidate == NULL)
+		elog(ERROR, "function amvalidate is not defined for index access method %u",
+			 amoid);
+
+	result = amroutine->amvalidate(opclassoid);
+
+	pfree(amroutine);
+
+	PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/access/interface/ordering.c b/src/backend/access/interface/ordering.c
new file mode 100644
index 0000000000..ddd322af12
--- /dev/null
+++ b/src/backend/access/interface/ordering.c
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * ordering.c
+ *	  ordering interface access method routines
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/interface/ordering.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/ifam.h"
+#include "access/nbtree.h"
+#include "utils/fmgrprotos.h"
+
+/*
+ * Ordering handler function: return InterfaceAmRoutine with access method
+ * parameters and callbacks.
+ */
+Datum
+ordering_ifam_handler(PG_FUNCTION_ARGS)
+{
+	InterfaceAmRoutine *amroutine = makeNode(InterfaceAmRoutine);
+
+	amroutine->amstrategies = BTMaxStrategyNumber;
+	amroutine->amsupport = BTNProcs;
+	amroutine->amoptsprocnum = BTOPTIONS_PROC;
+	amroutine->amcanorder = true;
+	amroutine->amcanorderbyop = false;
+	amroutine->amvalidate = btvalidate;
+
+	PG_RETURN_POINTER(amroutine);
+}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 69f9dd51a7..ee2fe6e99e 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -47,21 +47,21 @@ OBJS = \
 
 include $(top_srcdir)/src/backend/common.mk
 
 # Note: the order of this list determines the order in which the catalog
 # header files are assembled into postgres.bki.  BKI_BOOTSTRAP catalogs
 # must appear first, and pg_statistic before pg_statistic_ext_data, and
 # there are reputedly other, undocumented ordering dependencies.
 CATALOG_HEADERS := \
 	pg_proc.h pg_type.h pg_attribute.h pg_class.h \
 	pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \
-	pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
+	pg_opfamily.h pg_opclass.h pg_am.h pg_amimplements.h pg_amop.h pg_amproc.h \
 	pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \
 	pg_statistic.h pg_statistic_ext.h pg_statistic_ext_data.h \
 	pg_rewrite.h pg_trigger.h pg_event_trigger.h pg_description.h \
 	pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
 	pg_database.h pg_db_role_setting.h pg_tablespace.h \
 	pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
 	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
 	pg_ts_parser.h pg_ts_template.h pg_extension.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
 	pg_foreign_table.h pg_policy.h pg_replication_origin.h \
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 50b7a16bce..b2b308202a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -384,21 +384,21 @@ ConstructTupleDescriptor(Relation heapRelation,
 			 * we actually need to compress a value, we'll use whatever the
 			 * current value of default_toast_compression is at that point in
 			 * time.
 			 */
 			to->attcompression = InvalidCompressionMethod;
 
 			ReleaseSysCache(tuple);
 
 			/*
 			 * Make sure the expression yields a type that's safe to store in
-			 * an index.  We need this defense because we have index opclasses
+			 * an index.  We need this defense because we have opclasses
 			 * for pseudo-types such as "record", and the actually stored type
 			 * had better be safe; eg, a named composite type is okay, an
 			 * anonymous record type is not.  The test is the same as for
 			 * whether a table column is of a safe type (which is why we
 			 * needn't check for the non-expression case).
 			 */
 			CheckAttributeType(NameStr(to->attname),
 							   to->atttypid, to->attcollation,
 							   NIL, 0);
 		}
@@ -659,21 +659,21 @@ UpdateIndexRelation(Oid indexoid,
  *		parent index; otherwise InvalidOid.
  * parentConstraintId: if creating a constraint on a partition, the OID
  *		of the constraint in the parent; otherwise InvalidOid.
  * relFileNode: normally, pass InvalidOid to get new storage.  May be
  *		nonzero to attach an existing valid build.
  * indexInfo: same info executor uses to insert into the index
  * indexColNames: column names to use for index (List of char *)
  * accessMethodObjectId: OID of index AM to use
  * tableSpaceId: OID of tablespace to use
  * collationObjectId: array of collation OIDs, one per index column
- * classObjectId: array of index opclass OIDs, one per index column
+ * classObjectId: array of opclass OIDs, one per index column
  * coloptions: array of per-index-column indoption settings
  * reloptions: AM-specific options
  * flags: bitmask that can include any combination of these bits:
  *		INDEX_CREATE_IS_PRIMARY
  *			the index is a primary key
  *		INDEX_CREATE_ADD_CONSTRAINT:
  *			invoke index_constraint_create also
  *		INDEX_CREATE_SKIP_BUILD:
  *			skip the index_build() step for the moment; caller must do it
  *			later (typically via reindex_index())
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 9882e549c4..beeda85c33 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1665,21 +1665,21 @@ get_object_address_type(ObjectType objtype, TypeName *typename, bool missing_ok)
 /*
  * Find the ObjectAddress for an opclass or opfamily.
  */
 static ObjectAddress
 get_object_address_opcf(ObjectType objtype, List *object, bool missing_ok)
 {
 	Oid			amoid;
 	ObjectAddress address;
 
 	/* XXX no missing_ok support here */
-	amoid = get_index_am_oid(strVal(linitial(object)), false);
+	amoid = get_interface_or_index_am_oid(strVal(linitial(object)), false);
 	object = list_copy_tail(object, 1);
 
 	switch (objtype)
 	{
 		case OBJECT_OPCLASS:
 			address.classId = OperatorClassRelationId;
 			address.objectId = get_opclass_oid(amoid, object, missing_ok);
 			address.objectSubId = 0;
 			break;
 		case OBJECT_OPFAMILY:
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index 188109e474..956e8bdec4 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -13,50 +13,55 @@
  */
 #include "postgres.h"
 
 #include "access/htup_details.h"
 #include "access/table.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_amimplements.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 void StoreCatalogAmimpments(Oid implementing, List *implements);
 static Oid	lookup_am_handler_func(List *handler_name, char amtype);
+static Oid get_am_type_oid(const char *amname, char amtype, char amtype2, bool missing_ok);
 static const char *get_am_type_string(char amtype);
 
 
 /*
  * CreateAccessMethod
  *		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;
+	List	   *implementsoids;
+	ListCell   *listptr;
 
 	rel = table_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.")));
@@ -70,20 +75,44 @@ CreateAccessMethod(CreateAmStmt *stmt)
 				(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_am_handler_func(stmt->handler_name, stmt->amtype);
 
+	/*
+	 * Determine the list of OIDs of the implemented access methods
+	 */
+	implementsoids = NIL;
+	foreach(listptr, stmt->implements)
+	{
+		char	   *name = strVal(lfirst(listptr));
+		Oid			oid = get_am_type_oid(name, AMTYPE_INTERFACE, '\0', false);
+
+		/* Reject duplications */
+		if (list_member_oid(implementsoids, oid))
+			ereport(ERROR,
+					(errcode(ERRCODE_DUPLICATE_TABLE),
+					 errmsg("access method \"%s\" would be implemented more than once",
+							name)));
+
+		implementsoids = lappend_oid(implementsoids, oid);
+	}
+
+	if (implementsoids != NIL && stmt->amtype != AMTYPE_INDEX)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("only index access methods can implement interfaces")));
+
 	/*
 	 * Insert tuple into pg_am.
 	 */
 	memset(values, 0, sizeof(values));
 	memset(nulls, false, sizeof(nulls));
 
 	amoid = GetNewOidWithIndex(rel, AmOidIndexId, Anum_pg_am_oid);
 	values[Anum_pg_am_oid - 1] = ObjectIdGetDatum(amoid);
 	values[Anum_pg_am_amname - 1] =
 		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
@@ -105,91 +134,157 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	referenced.objectSubId = 0;
 
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	recordDependencyOnCurrentExtension(&myself, false);
 
 	InvokeObjectPostCreateHook(AccessMethodRelationId, amoid, 0);
 
 	table_close(rel, RowExclusiveLock);
 
+	StoreCatalogAmimpments(amoid, implementsoids);
+
 	return myself;
 }
 
+/*
+ * StoreCatalogAmimplements
+ *      Updates the system catalogs with proper implements information.
+ */
+static void
+StoreCatalogAmimpments(Oid implementing, List *implements)
+{
+	int			seq;
+	ListCell   *listptr;
+	Relation	amirelation;
+	ObjectAddress childobject,
+				parentobject;
+	Datum		values[Natts_pg_amimplements];
+	bool		nulls[Natts_pg_amimplements];
+	HeapTuple	tuple;
+
+	AssertArg(OidIsValid(implementing));
+
+	/* Prepare to insert into pg_amimplements */
+	amirelation = table_open(AmimplementsRelationId, RowExclusiveLock);
+	values[Anum_pg_amimplements_amiamid - 1] = ObjectIdGetDatum(implementing);
+	memset(nulls, 0, sizeof(nulls));
+
+	/* Prepare the dependency objects */
+	parentobject.classId = AccessMethodRelationId;
+	parentobject.objectSubId = 0;
+	childobject.classId = AccessMethodRelationId;
+	childobject.objectId = implementing;
+	childobject.objectSubId = 0;
+
+	seq = 1;
+	foreach(listptr, implements)
+	{
+		Oid			oid = lfirst_oid(listptr);
+
+		AssertArg(OidIsValid(listptr));
+
+		/* Store pg_amimplements entry */
+		values[Anum_pg_amimplements_amiparent - 1] = ObjectIdGetDatum(oid);
+		values[Anum_pg_amimplements_amiseqno - 1] = Int32GetDatum(seq);
+		tuple = heap_form_tuple(RelationGetDescr(amirelation), values, nulls);
+		CatalogTupleInsert(amirelation, tuple);
+		heap_freetuple(tuple);
+
+		/* Store a dependency too */
+		parentobject.objectId = oid;
+		recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
+
+		seq++;
+	}
+
+	table_close(amirelation, 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.
+ * given types.
  */
 static Oid
-get_am_type_oid(const char *amname, char amtype, bool missing_ok)
+get_am_type_oid(const char *amname, char amtype, char amtype2, 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))));
+		{
+			if (amtype2 == '\0')
+				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))));
+			else if (amform->amtype != amtype2)
+				ereport(ERROR,
+						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+						 errmsg("access method \"%s\" is not of type %s or %s",
+								NameStr(amform->amname),
+								get_am_type_string(amtype),
+								get_am_type_string(amtype2))));
+		}
 
 		oid = amform->oid;
 		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.
+ * get_interface_or_index_am_oid - given an access method name, look up its OID
+ *		and verify it corresponds to an index or interface AM.
  */
 Oid
-get_index_am_oid(const char *amname, bool missing_ok)
+get_interface_or_index_am_oid(const char *amname, bool missing_ok)
 {
-	return get_am_type_oid(amname, AMTYPE_INDEX, missing_ok);
+	return get_am_type_oid(amname, AMTYPE_INTERFACE, AMTYPE_INDEX, missing_ok);
 }
 
 /*
  * get_table_am_oid - given an access method name, look up its OID
  *		and verify it corresponds to an table AM.
  */
 Oid
 get_table_am_oid(const char *amname, bool missing_ok)
 {
-	return get_am_type_oid(amname, AMTYPE_TABLE, missing_ok);
+	return get_am_type_oid(amname, AMTYPE_TABLE, '\0', 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);
+	return get_am_type_oid(amname, '\0', '\0', missing_ok);
 }
 
 /*
  * get_am_name - given an access method OID, look up its name.
  */
 char *
 get_am_name(Oid amOid)
 {
 	HeapTuple	tup;
 	char	   *result = NULL;
@@ -206,20 +301,22 @@ get_am_name(Oid amOid)
 }
 
 /*
  * Convert single-character access method type into string for error reporting.
  */
 static const char *
 get_am_type_string(char amtype)
 {
 	switch (amtype)
 	{
+		case AMTYPE_INTERFACE:
+			return "INTERFACE";
 		case AMTYPE_INDEX:
 			return "INDEX";
 		case AMTYPE_TABLE:
 			return "TABLE";
 		default:
 			/* shouldn't happen */
 			elog(ERROR, "invalid access method type '%c'", amtype);
 			return NULL;		/* keep compiler quiet */
 	}
 }
@@ -241,20 +338,23 @@ lookup_am_handler_func(List *handler_name, char amtype)
 		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_INTERFACE:
+			expectedType = INTERFACE_AM_HANDLEROID;
+			break;
 		case AMTYPE_INDEX:
 			expectedType = INDEX_AM_HANDLEROID;
 			break;
 		case AMTYPE_TABLE:
 			expectedType = TABLE_AM_HANDLEROID;
 			break;
 		default:
 			elog(ERROR, "unrecognized access method type \"%c\"", amtype);
 	}
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 76774dce06..e68ff3b6ab 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -19,20 +19,21 @@
 #include "access/heapam.h"
 #include "access/htup_details.h"
 #include "access/reloptions.h"
 #include "access/sysattr.h"
 #include "access/tableam.h"
 #include "access/xact.h"
 #include "catalog/catalog.h"
 #include "catalog/index.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_am.h"
+#include "catalog/pg_amimplements.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_opfamily.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/pg_type.h"
 #include "commands/comment.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/event_trigger.h"
@@ -1997,41 +1998,80 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
 
 			indexInfo->ii_OpclassOptions[attn] =
 				transformRelOptions((Datum) 0, attribute->opclassopts,
 									NULL, NULL, false, false);
 		}
 
 		attn++;
 	}
 }
 
+List *
+GetCompatibleAccessMethods(indexAccessMethodId)
+{
+	List	   *result;
+	Relation	rel;
+	ScanKeyData	skey[1];
+	SysScanDesc	scan;
+	HeapTuple	tup;
+
+	result = list_make1_oid(indexAccessMethodId);
+
+	rel = table_open(AmimplementsRelationId, AccessShareLock);
+
+	ScanKeyInit(&skey[0],
+				Anum_pg_amimplements_amiamid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(indexAccessMethodId));
+
+	scan = systable_beginscan(rel, AmimplementsAmidSeqnoIndexId, true,
+							  NULL, 1, skey);
+
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_amimplements amimplements = (Form_pg_amimplements) GETSTRUCT(tup);
+
+		result = lappend_oid(result, amimplements->amiparent);
+	}
+
+	systable_endscan(scan);
+
+	table_close(rel, AccessShareLock);
+
+	return result;
+}
+
 /*
  * Resolve possibly-defaulted operator class specification
  *
  * Note: This is used to resolve operator class specifications in index and
  * partition key definitions.
  */
 Oid
 ResolveOpClass(List *opclass, Oid attrType,
 			   const char *accessMethodName, Oid accessMethodId)
 {
 	char	   *schemaname;
 	char	   *opcname;
+	List	   *amoids;
+	ListCell   *listptr;
 	HeapTuple	tuple;
 	Form_pg_opclass opform;
 	Oid			opClassId,
 				opInputType;
 
+	amoids = GetCompatibleAccessMethods(accessMethodId);
+
 	if (opclass == NIL)
 	{
 		/* no operator class specified, so find the default */
-		opClassId = GetDefaultOpClass(attrType, accessMethodId);
+		opClassId = GetDefaultOpClass(attrType, amoids);
 		if (!OidIsValid(opClassId))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_OBJECT),
 					 errmsg("data type %s has no default operator class for access method \"%s\"",
 							format_type_be(attrType), accessMethodName),
 					 errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
 		return opClassId;
 	}
 
 	/*
@@ -2040,29 +2080,41 @@ ResolveOpClass(List *opclass, Oid attrType,
 
 	/* deconstruct the name list */
 	DeconstructQualifiedName(opclass, &schemaname, &opcname);
 
 	if (schemaname)
 	{
 		/* Look in specific schema only */
 		Oid			namespaceId;
 
 		namespaceId = LookupExplicitNamespace(schemaname, false);
-		tuple = SearchSysCache3(CLAAMNAMENSP,
-								ObjectIdGetDatum(accessMethodId),
-								PointerGetDatum(opcname),
-								ObjectIdGetDatum(namespaceId));
+
+		foreach(listptr, amoids)
+		{
+			tuple = SearchSysCache3(CLAAMNAMENSP,
+									ObjectIdGetDatum(lfirst_oid(listptr)),
+									PointerGetDatum(opcname),
+									ObjectIdGetDatum(namespaceId));
+			if (HeapTupleIsValid(tuple))
+				break;
+		}
 	}
 	else
 	{
 		/* Unqualified opclass name, so search the search path */
-		opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
+		foreach(listptr, amoids)
+		{
+			opClassId = OpclassnameGetOpcid(lfirst_oid(listptr), opcname);
+			if (OidIsValid(opClassId))
+				break;
+		}
+
 		if (!OidIsValid(opClassId))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_OBJECT),
 					 errmsg("operator class \"%s\" does not exist for access method \"%s\"",
 							opcname, accessMethodName)));
 		tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
 	}
 
 	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
@@ -2085,32 +2137,31 @@ ResolveOpClass(List *opclass, Oid attrType,
 						NameListToString(opclass), format_type_be(attrType))));
 
 	ReleaseSysCache(tuple);
 
 	return opClassId;
 }
 
 /*
  * GetDefaultOpClass
  *
- * Given the OIDs of a datatype and an access method, find the default
+ * Given the OIDs of a datatype and access methods, find the default
  * operator class, if any.  Returns InvalidOid if there is none.
  */
 Oid
-GetDefaultOpClass(Oid type_id, Oid am_id)
+GetDefaultOpClass(Oid type_id, List *am_ids)
 {
 	Oid			result = InvalidOid;
 	int			nexact = 0;
 	int			ncompatible = 0;
 	int			ncompatiblepreferred = 0;
 	Relation	rel;
-	ScanKeyData skey[1];
 	SysScanDesc scan;
 	HeapTuple	tup;
 	TYPCATEGORY tcategory;
 
 	/* If it's a domain, look at the base type instead */
 	type_id = getBaseType(type_id);
 
 	tcategory = TypeCategory(type_id);
 
 	/*
@@ -2120,34 +2171,39 @@ GetDefaultOpClass(Oid type_id, Oid am_id)
 	 *
 	 * We could find more than one binary-compatible match.  If just one is
 	 * for a preferred type, use that one; otherwise we fail, forcing the user
 	 * to specify which one he wants.  (The preferred-type special case is a
 	 * kluge for varchar: it's binary-compatible to both text and bpchar, so
 	 * we need a tiebreaker.)  If we find more than one exact match, then
 	 * someone put bogus entries in pg_opclass.
 	 */
 	rel = table_open(OperatorClassRelationId, AccessShareLock);
 
-	ScanKeyInit(&skey[0],
-				Anum_pg_opclass_opcmethod,
-				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(am_id));
-
-	scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
-							  NULL, 1, skey);
+	scan = systable_beginscan(rel, InvalidOid, false, NULL, 0, NULL);
 
 	while (HeapTupleIsValid(tup = systable_getnext(scan)))
 	{
+		bool		ammatch = false;
+		ListCell   *listptr;
 		Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
 
+		foreach(listptr, am_ids)
+		{
+			if (lfirst_oid(listptr) == opclass->opcmethod)
+			{
+				ammatch = true;
+				break;
+			}
+		}
+
 		/* ignore altogether if not a default opclass */
-		if (!opclass->opcdefault)
+		if (!ammatch || !opclass->opcdefault)
 			continue;
 		if (opclass->opcintype == type_id)
 		{
 			nexact++;
 			result = opclass->oid;
 		}
 		else if (nexact == 0 &&
 				 IsBinaryCoercible(type_id, opclass->opcintype))
 		{
 			if (IsPreferredType(tcategory, opclass->opcintype))
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index fad39e2b75..5da2cb01e1 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -13,20 +13,21 @@
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include <limits.h>
 
 #include "access/genam.h"
 #include "access/hash.h"
 #include "access/htup_details.h"
+#include "access/ifam.h"
 #include "access/nbtree.h"
 #include "access/sysattr.h"
 #include "access/table.h"
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
@@ -337,21 +338,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	int			maxOpNumber,	/* amstrategies value */
 				optsProcNumber, /* amoptsprocnum value */
 				maxProcNumber;	/* amsupport value */
 	bool		amstorage;		/* amstorage flag */
 	List	   *operators;		/* OpFamilyMember list for operators */
 	List	   *procedures;		/* OpFamilyMember list for support procs */
 	ListCell   *l;
 	Relation	rel;
 	HeapTuple	tup;
 	Form_pg_am	amform;
-	IndexAmRoutine *amroutine;
+	InterfaceAmRoutine *amroutine;
 	Datum		values[Natts_pg_opclass];
 	bool		nulls[Natts_pg_opclass];
 	AclResult	aclresult;
 	NameData	opcName;
 	ObjectAddress myself,
 				referenced;
 
 	/* Convert list of names to a name and namespace */
 	namespaceoid = QualifiedNameGetCreationNamespace(stmt->opclassname,
 													 &opcname);
@@ -365,21 +366,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	/* Get necessary info about access method */
 	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
 	if (!HeapTupleIsValid(tup))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
 				 errmsg("access method \"%s\" does not exist",
 						stmt->amname)));
 
 	amform = (Form_pg_am) GETSTRUCT(tup);
 	amoid = amform->oid;
-	amroutine = GetIndexAmRoutineByAmId(amoid, false);
+	amroutine = GetInterfaceAmRoutineByAmId(amoid);
 	ReleaseSysCache(tup);
 
 	maxOpNumber = amroutine->amstrategies;
 	/* if amstrategies is zero, just enforce that op numbers fit in int16 */
 	if (maxOpNumber <= 0)
 		maxOpNumber = SHRT_MAX;
 	maxProcNumber = amroutine->amsupport;
 	optsProcNumber = amroutine->amoptsprocnum;
 	amstorage = amroutine->amstorage;
 
@@ -771,21 +772,21 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
 	namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
 													 &opfname);
 
 	/* Check we have creation rights in target namespace */
 	aclresult = pg_namespace_aclcheck(namespaceoid, GetUserId(), ACL_CREATE);
 	if (aclresult != ACLCHECK_OK)
 		aclcheck_error(aclresult, OBJECT_SCHEMA,
 					   get_namespace_name(namespaceoid));
 
 	/* Get access method OID, throwing an error if it doesn't exist. */
-	amoid = get_index_am_oid(stmt->amname, false);
+	amoid = get_interface_or_index_am_oid(stmt->amname, false);
 
 	/* XXX Should we make any privilege check against the AM? */
 
 	/*
 	 * Currently, we require superuser privileges to create an opfamily. See
 	 * comments in DefineOpClass.
 	 */
 	if (!superuser())
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -807,33 +808,33 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
 Oid
 AlterOpFamily(AlterOpFamilyStmt *stmt)
 {
 	Oid			amoid,			/* our AM's oid */
 				opfamilyoid;	/* oid of opfamily */
 	int			maxOpNumber,	/* amstrategies value */
 				optsProcNumber, /* amopclassopts value */
 				maxProcNumber;	/* amsupport value */
 	HeapTuple	tup;
 	Form_pg_am	amform;
-	IndexAmRoutine *amroutine;
+	InterfaceAmRoutine *amroutine;
 
 	/* Get necessary info about access method */
 	tup = SearchSysCache1(AMNAME, CStringGetDatum(stmt->amname));
 	if (!HeapTupleIsValid(tup))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_OBJECT),
 				 errmsg("access method \"%s\" does not exist",
 						stmt->amname)));
 
 	amform = (Form_pg_am) GETSTRUCT(tup);
 	amoid = amform->oid;
-	amroutine = GetIndexAmRoutineByAmId(amoid, false);
+	amroutine = GetInterfaceAmRoutineByAmId(amoid);
 	ReleaseSysCache(tup);
 
 	maxOpNumber = amroutine->amstrategies;
 	/* if amstrategies is zero, just enforce that op numbers fit in int16 */
 	if (maxOpNumber <= 0)
 		maxOpNumber = SHRT_MAX;
 	maxProcNumber = amroutine->amsupport;
 	optsProcNumber = amroutine->amoptsprocnum;
 
 	/* XXX Should we make any privilege check against the AM? */
@@ -866,21 +867,21 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
 }
 
 /*
  * ADD part of ALTER OP FAMILY
  */
 static void
 AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
 				 int maxOpNumber, int maxProcNumber, int optsProcNumber,
 				 List *items)
 {
-	IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
+	InterfaceAmRoutine *amroutine = GetInterfaceAmRoutineByAmId(amoid);
 	List	   *operators;		/* OpFamilyMember list for operators */
 	List	   *procedures;		/* OpFamilyMember list for support procs */
 	ListCell   *l;
 
 	operators = NIL;
 	procedures = NIL;
 
 	/*
 	 * Scan the "items" list to obtain additional info.
 	 */
@@ -1149,21 +1150,21 @@ assignOperTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 		/*
 		 * Ordering op, check index supports that.  (We could perhaps also
 		 * check that the operator returns a type supported by the sortfamily,
 		 * but that seems more trouble than it's worth here.  If it does not,
 		 * the operator will never be matchable to any ORDER BY clause, but no
 		 * worse consequences can ensue.  Also, trying to check that would
 		 * create an ordering hazard during dump/reload: it's possible that
 		 * the family has been created but not yet populated with the required
 		 * operators.)
 		 */
-		IndexAmRoutine *amroutine = GetIndexAmRoutineByAmId(amoid, false);
+		InterfaceAmRoutine *amroutine = GetInterfaceAmRoutineByAmId(amoid);
 
 		if (!amroutine->amcanorderbyop)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("access method \"%s\" does not support ordering operators",
 							get_am_name(amoid))));
 	}
 	else
 	{
 		/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 028e8ac46b..4f51cce331 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -16810,21 +16810,21 @@ ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNu
 		 * partitioning, we use a btree operator class; hash partitioning uses
 		 * a hash operator class.
 		 */
 		if (strategy == PARTITION_STRATEGY_HASH)
 			am_oid = HASH_AM_OID;
 		else
 			am_oid = BTREE_AM_OID;
 
 		if (!pelem->opclass)
 		{
-			partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
+			partopclass[attn] = GetDefaultOpClass(atttype, list_make1_oid(am_oid));
 
 			if (!OidIsValid(partopclass[attn]))
 			{
 				if (strategy == PARTITION_STRATEGY_HASH)
 					ereport(ERROR,
 							(errcode(ERRCODE_UNDEFINED_OBJECT),
 							 errmsg("data type %s has no default operator class for access method \"%s\"",
 									format_type_be(atttype), "hash"),
 							 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
 				else
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 58ec65c6af..bef0e6b511 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2302,21 +2302,21 @@ findRangeSubOpclass(List *opcname, Oid subtype)
 		opInputType = get_opclass_input_type(opcid);
 		if (!IsBinaryCoercible(subtype, opInputType))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATATYPE_MISMATCH),
 					 errmsg("operator class \"%s\" does not accept data type %s",
 							NameListToString(opcname),
 							format_type_be(subtype))));
 	}
 	else
 	{
-		opcid = GetDefaultOpClass(subtype, BTREE_AM_OID);
+		opcid = GetDefaultOpClass(subtype, list_make1_oid(BTREE_AM_OID));
 		if (!OidIsValid(opcid))
 		{
 			/* We spell the error message identically to ResolveOpClass */
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_OBJECT),
 					 errmsg("data type %s has no default operator class for access method \"%s\"",
 							format_type_be(subtype), "btree"),
 					 errhint("You must specify an operator class for the range type or define a default operator class for the subtype.")));
 		}
 	}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index bd87f23784..9a238a5f45 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4435,20 +4435,21 @@ _copyCreateTransformStmt(const CreateTransformStmt *from)
 }
 
 static CreateAmStmt *
 _copyCreateAmStmt(const CreateAmStmt *from)
 {
 	CreateAmStmt *newnode = makeNode(CreateAmStmt);
 
 	COPY_STRING_FIELD(amname);
 	COPY_NODE_FIELD(handler_name);
 	COPY_SCALAR_FIELD(amtype);
+	COPY_NODE_FIELD(implements);
 
 	return newnode;
 }
 
 static CreateTrigStmt *
 _copyCreateTrigStmt(const CreateTrigStmt *from)
 {
 	CreateTrigStmt *newnode = makeNode(CreateTrigStmt);
 
 	COPY_SCALAR_FIELD(replace);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index dba3e6b31e..c543fe8dc4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2044,20 +2044,21 @@ _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);
+	COMPARE_NODE_FIELD(implements);
 
 	return true;
 }
 
 static bool
 _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 {
 	COMPARE_SCALAR_FIELD(replace);
 	COMPARE_SCALAR_FIELD(isconstraint);
 	COMPARE_STRING_FIELD(trigname);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index eb24195438..a0a3a9d8f8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -415,20 +415,21 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 				target_list opt_target_list insert_column_list set_target_list
 				set_clause_list set_clause
 				def_list operator_def_list indirection opt_indirection
 				reloption_list TriggerFuncArgs opclass_item_list opclass_drop_list
 				opclass_purpose opt_opfamily transaction_mode_list_or_empty
 				OptTableFuncElementList TableFuncElementList opt_type_modifiers
 				prep_type_clause
 				execute_param_clause using_clause returning_clause
 				opt_enum_val_list enum_val_list table_func_column_list
 				create_generic_options alter_generic_options
+				OptImplement
 				relation_expr_list dostmt_opt_list
 				transform_element_list transform_type_list
 				TriggerTransitions TriggerReferencing
 				vacuum_relation_list opt_vacuum_relation_list
 				drop_option_list
 
 %type <node>	opt_routine_body
 %type <groupclause> group_clause
 %type <list>	group_by_list
 %type <node>	group_by_item empty_grouping_set rollup_clause cube_clause
@@ -661,24 +662,25 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXPRESSION
 	EXTENSION EXTERNAL EXTRACT
 
 	FALSE_P FAMILY FETCH FILTER FINALIZE FIRST_P FLOAT_P FOLLOWING FOR
 	FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
 
 	GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS
 
 	HANDLER HAVING HEADER_P HOLD HOUR_P
 
-	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE
+	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE
+	IMPLEMENTS IMPLICIT_P IMPORT_P IN_P INCLUDE
 	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
-	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
+	INTERFACE INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
 
 	JOIN
 
 	KEY
 
 	LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
 	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
@@ -5316,34 +5318,41 @@ row_security_cmd:
 		;
 
 /*****************************************************************************
  *
  *		QUERY:
  *             CREATE ACCESS METHOD name HANDLER handler_name
  *
  *****************************************************************************/
 
 CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name
+			  OptImplement
 				{
 					CreateAmStmt *n = makeNode(CreateAmStmt);
 					n->amname = $4;
 					n->handler_name = $8;
 					n->amtype = $6;
+					n->implements = $9;
 					$$ = (Node *) n;
 				}
 		;
 
 am_type:
-			INDEX			{ $$ = AMTYPE_INDEX; }
+			INTERFACE		{ $$ = AMTYPE_INTERFACE; }
+		|	INDEX			{ $$ = AMTYPE_INDEX; }
 		|	TABLE			{ $$ = AMTYPE_TABLE; }
 		;
 
+OptImplement: IMPLEMENTS '(' name_list ')'	{ $$ = $3; }
+			  | /*EMPTY*/					{ $$ = NIL; }
+			  ;
+
 /*****************************************************************************
  *
  *		QUERIES :
  *				CREATE TRIGGER ...
  *
  *****************************************************************************/
 
 CreateTrigStmt:
 			CREATE opt_or_replace TRIGGER name TriggerActionTime TriggerEvents ON
 			qualified_name TriggerReferencing TriggerForSpec TriggerWhen
@@ -15572,34 +15581,36 @@ unreserved_keyword:
 			| GRANTED
 			| GROUPS
 			| HANDLER
 			| HEADER_P
 			| HOLD
 			| HOUR_P
 			| IDENTITY_P
 			| IF_P
 			| IMMEDIATE
 			| IMMUTABLE
+			| IMPLEMENTS
 			| IMPLICIT_P
 			| IMPORT_P
 			| INCLUDE
 			| INCLUDING
 			| INCREMENT
 			| INDEX
 			| INDEXES
 			| INHERIT
 			| INHERITS
 			| INLINE_P
 			| INPUT_P
 			| INSENSITIVE
 			| INSERT
 			| INSTEAD
+			| INTERFACE
 			| INVOKER
 			| ISOLATION
 			| KEY
 			| LABEL
 			| LANGUAGE
 			| LARGE_P
 			| LAST_P
 			| LEAKPROOF
 			| LEVEL
 			| LISTEN
@@ -16123,40 +16134,42 @@ bare_label_keyword:
 			| GROUPING
 			| GROUPS
 			| HANDLER
 			| HEADER_P
 			| HOLD
 			| IDENTITY_P
 			| IF_P
 			| ILIKE
 			| IMMEDIATE
 			| IMMUTABLE
+			| IMPLEMENTS
 			| IMPLICIT_P
 			| IMPORT_P
 			| IN_P
 			| INCLUDE
 			| INCLUDING
 			| INCREMENT
 			| INDEX
 			| INDEXES
 			| INHERIT
 			| INHERITS
 			| INITIALLY
 			| INLINE_P
 			| INNER_P
 			| INOUT
 			| INPUT_P
 			| INSENSITIVE
 			| INSERT
 			| INSTEAD
 			| INT_P
 			| INTEGER
+			| INTERFACE
 			| INTERVAL
 			| INVOKER
 			| IS
 			| ISOLATION
 			| JOIN
 			| KEY
 			| LABEL
 			| LANGUAGE
 			| LARGE_P
 			| LAST_P
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index c9708e38f4..7f1f62fa6a 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -2034,21 +2034,21 @@ get_opclass(Oid opclass, Oid actual_datatype)
 {
 	List	   *result = NIL;
 	HeapTuple	ht_opc;
 	Form_pg_opclass opc_rec;
 
 	ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
 	if (!HeapTupleIsValid(ht_opc))
 		elog(ERROR, "cache lookup failed for opclass %u", opclass);
 	opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
 
-	if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
+	if (GetDefaultOpClass(actual_datatype, list_make1_oid(opc_rec->opcmethod)) != opclass)
 	{
 		/* For simplicity, we always schema-qualify the name */
 		char	   *nsp_name = get_namespace_name(opc_rec->opcnamespace);
 		char	   *opc_name = pstrdup(NameStr(opc_rec->opcname));
 
 		result = list_make2(makeString(nsp_name), makeString(opc_name));
 	}
 
 	ReleaseSysCache(ht_opc);
 	return result;
@@ -2316,21 +2316,21 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 					 errdetail("Cannot create a non-deferrable constraint using a deferrable index."),
 					 parser_errposition(cxt->pstate, constraint->location)));
 
 		/*
 		 * Insist on it being a btree.  That's the only kind that supports
 		 * uniqueness at the moment anyway; but we must have an index that
 		 * exactly matches what you'd get from plain ADD CONSTRAINT syntax,
 		 * else dump and reload will produce a different index (breaking
 		 * pg_upgrade in particular).
 		 */
-		if (index_rel->rd_rel->relam != get_index_am_oid(DEFAULT_INDEX_TYPE, false))
+		if (index_rel->rd_rel->relam != BTREE_AM_OID)
 			ereport(ERROR,
 					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 					 errmsg("index \"%s\" is not a btree", index_name),
 					 parser_errposition(cxt->pstate, constraint->location)));
 
 		/* Must get indclass the hard way */
 		indclassDatum = SysCacheGetAttr(INDEXRELID, index_rel->rd_indextuple,
 										Anum_pg_index_indclass, &isnull);
 		Assert(!isnull);
 		indclass = (oidvector *) DatumGetPointer(indclassDatum);
@@ -2363,21 +2363,21 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 				 * While the index would still work as a constraint with
 				 * non-default settings, it might not provide exactly the same
 				 * uniqueness semantics as you'd get from a normally-created
 				 * constraint; and there's also the dump/reload problem
 				 * mentioned above.
 				 */
 				Datum		attoptions =
 				get_attoptions(RelationGetRelid(index_rel), i + 1);
 
 				defopclass = GetDefaultOpClass(attform->atttypid,
-											   index_rel->rd_rel->relam);
+											   list_make1_oid(index_rel->rd_rel->relam));
 				if (indclass->values[i] != defopclass ||
 					attform->attcollation != index_rel->rd_indcollation[i] ||
 					attoptions != (Datum) 0 ||
 					index_rel->rd_indoption[i] != 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_WRONG_OBJECT_TYPE),
 							 errmsg("index \"%s\" column number %d does not have default sorting behavior", index_name, i + 1),
 							 errdetail("Cannot create a primary key or unique constraint using such an index."),
 							 parser_errposition(cxt->pstate, constraint->location)));
 
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index c2f910d606..2a2095bdf1 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -375,17 +375,18 @@ PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(pg_ddl_command);
 
 /*
  * Dummy I/O functions for various other pseudotypes.
  */
 PSEUDOTYPE_DUMMY_IO_FUNCS(any);
 PSEUDOTYPE_DUMMY_IO_FUNCS(trigger);
 PSEUDOTYPE_DUMMY_IO_FUNCS(event_trigger);
 PSEUDOTYPE_DUMMY_IO_FUNCS(language_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
+PSEUDOTYPE_DUMMY_IO_FUNCS(interface_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3719755a0d..ab72cff899 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -11168,21 +11168,21 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 	Form_pg_opclass opcrec;
 	char	   *opcname;
 	char	   *nspname;
 
 	ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
 	if (!HeapTupleIsValid(ht_opc))
 		elog(ERROR, "cache lookup failed for opclass %u", opclass);
 	opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
 
 	if (!OidIsValid(actual_datatype) ||
-		GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
+		GetDefaultOpClass(actual_datatype, list_make1_oid(opcrec->opcmethod)) != opclass)
 	{
 		/* Okay, we need the opclass name.  Do we need to qualify it? */
 		opcname = NameStr(opcrec->opcname);
 		if (OpclassIsVisible(opclass))
 			appendStringInfo(buf, " %s", quote_identifier(opcname));
 		else
 		{
 			nspname = get_namespace_name(opcrec->opcnamespace);
 			appendStringInfo(buf, " %s.%s",
 							 quote_identifier(nspname),
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index de96e96c8f..ed6630f263 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -467,21 +467,21 @@ lookup_type_cache(Oid type_id, int flags)
 	 * requested.
 	 */
 	if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_LT_OPR | TYPECACHE_GT_OPR |
 				  TYPECACHE_CMP_PROC |
 				  TYPECACHE_EQ_OPR_FINFO | TYPECACHE_CMP_PROC_FINFO |
 				  TYPECACHE_BTREE_OPFAMILY)) &&
 		!(typentry->flags & TCFLAGS_CHECKED_BTREE_OPCLASS))
 	{
 		Oid			opclass;
 
-		opclass = GetDefaultOpClass(type_id, BTREE_AM_OID);
+		opclass = GetDefaultOpClass(type_id, list_make1_oid(BTREE_AM_OID));
 		if (OidIsValid(opclass))
 		{
 			typentry->btree_opf = get_opclass_family(opclass);
 			typentry->btree_opintype = get_opclass_input_type(opclass);
 		}
 		else
 		{
 			typentry->btree_opf = typentry->btree_opintype = InvalidOid;
 		}
 
@@ -508,21 +508,21 @@ lookup_type_cache(Oid type_id, int flags)
 		flags |= TYPECACHE_HASH_OPFAMILY;
 
 	if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO |
 				  TYPECACHE_HASH_EXTENDED_PROC |
 				  TYPECACHE_HASH_EXTENDED_PROC_FINFO |
 				  TYPECACHE_HASH_OPFAMILY)) &&
 		!(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
 	{
 		Oid			opclass;
 
-		opclass = GetDefaultOpClass(type_id, HASH_AM_OID);
+		opclass = GetDefaultOpClass(type_id, list_make1_oid(HASH_AM_OID));
 		if (OidIsValid(opclass))
 		{
 			typentry->hash_opf = get_opclass_family(opclass);
 			typentry->hash_opintype = get_opclass_input_type(opclass);
 		}
 		else
 		{
 			typentry->hash_opf = typentry->hash_opintype = InvalidOid;
 		}
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8f53cc7c3b..7ee1d3ef07 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -13075,20 +13075,23 @@ dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
 
 	q = createPQExpBuffer();
 	delq = createPQExpBuffer();
 
 	qamname = pg_strdup(fmtId(aminfo->dobj.name));
 
 	appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
 
 	switch (aminfo->amtype)
 	{
+		case AMTYPE_INTERFACE:
+			appendPQExpBufferStr(q, "TYPE INTERFACE ");
+			break;
 		case AMTYPE_INDEX:
 			appendPQExpBufferStr(q, "TYPE INDEX ");
 			break;
 		case AMTYPE_TABLE:
 			appendPQExpBufferStr(q, "TYPE TABLE ");
 			break;
 		default:
 			pg_log_warning("invalid type \"%c\" of access method \"%s\"",
 						   aminfo->amtype, qamname);
 			destroyPQExpBuffer(q);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 2abf255798..9764194109 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -162,24 +162,26 @@ describeAccessMethods(const char *pattern, bool verbose)
 					 formatPGVersionNumber(pset.sversion, false,
 										   sverbuf, sizeof(sverbuf)));
 		return true;
 	}
 
 	initPQExpBuffer(&buf);
 
 	printfPQExpBuffer(&buf,
 					  "SELECT amname AS \"%s\",\n"
 					  "  CASE amtype"
+					  " WHEN 'n' THEN '%s'"
 					  " WHEN 'i' THEN '%s'"
 					  " WHEN 't' THEN '%s'"
 					  " END AS \"%s\"",
 					  gettext_noop("Name"),
+					  gettext_noop("Interface"),
 					  gettext_noop("Index"),
 					  gettext_noop("Table"),
 					  gettext_noop("Type"));
 
 	if (verbose)
 	{
 		appendPQExpBuffer(&buf,
 						  ",\n  amhandler AS \"%s\",\n"
 						  "  pg_catalog.obj_description(oid, 'pg_am') AS \"%s\"",
 						  gettext_noop("Handler"),
diff --git a/src/include/access/ifam.h b/src/include/access/ifam.h
new file mode 100644
index 0000000000..c6bdc17667
--- /dev/null
+++ b/src/include/access/ifam.h
@@ -0,0 +1,64 @@
+/*-------------------------------------------------------------------------
+ *
+ * ifam.h
+ *	  API for Postgres interface access methods
+ *
+ * Copyright (c) 2015-2021, PostgreSQL Global Development Group
+ *
+ * src/include/access/ifam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef IFAM_H
+#define IFAM_H
+
+#include "access/genam.h"
+
+/* validate definition of an opclass for this AM */
+typedef bool (*amvalidate_function) (Oid opclassoid);
+
+/* validate operators and support functions to be added to an opclass/family */
+typedef void (*amadjustmembers_function) (Oid opfamilyoid,
+										  Oid opclassoid,
+										  List *operators,
+										  List *functions);
+
+/*
+ * API struct for an index AM.  Note this must be stored in a single palloc'd
+ * chunk of memory.
+ */
+typedef struct InterfaceAmRoutine
+{
+	NodeTag		type;
+
+	/*
+	 * Total number of strategies (operators) by which we can traverse/search
+	 * this AM.  Zero if AM does not have a fixed set of strategy assignments.
+	 */
+	uint16		amstrategies;
+	/* total number of support functions that this AM uses */
+	uint16		amsupport;
+	/* opclass options support function number or 0 */
+	uint16		amoptsprocnum;
+	/* does AM support ORDER BY indexed column's value? */
+	bool		amcanorder;
+	/* does AM support ORDER BY result of an operator on indexed column? */
+	bool		amcanorderbyop;
+	/* can index storage data type differ from column data type? */
+	bool		amstorage;
+
+	/*
+	 * If you add new properties to either the above or the below lists, then
+	 * they should also (usually) be exposed via the property API (see
+	 * InterfaceAMProperty at the top of the file, and utils/adt/amutils.c).
+	 */
+
+	/* interface functions */
+	amvalidate_function amvalidate;
+	amadjustmembers_function amadjustmembers;	/* can be NULL */
+} InterfaceAmRoutine;
+
+/* Functions in access/interface/ifamapi.c */
+extern InterfaceAmRoutine *GetInterfaceAmRoutineByAmId(Oid amoid);
+
+#endif							/* IFAM_H */
diff --git a/src/include/catalog/pg_am.dat b/src/include/catalog/pg_am.dat
index 6082f0e6a8..f30c8d1aa0 100644
--- a/src/include/catalog/pg_am.dat
+++ b/src/include/catalog/pg_am.dat
@@ -8,20 +8,23 @@
 #
 # src/include/catalog/pg_am.dat
 #
 #----------------------------------------------------------------------
 
 [
 
 { oid => '2', oid_symbol => 'HEAP_TABLE_AM_OID',
   descr => 'heap table access method',
   amname => 'heap', amhandler => 'heap_tableam_handler', amtype => 't' },
+{ oid => '4', oid_symbol => 'ORDERING_AM_OID',
+  descr => 'ordering interface',
+  amname => 'ordering', amhandler => 'ordering_ifam_handler', amtype => 'n' },
 { oid => '403', oid_symbol => 'BTREE_AM_OID',
   descr => 'b-tree index access method',
   amname => 'btree', amhandler => 'bthandler', amtype => 'i' },
 { oid => '405', oid_symbol => 'HASH_AM_OID',
   descr => 'hash index access method',
   amname => 'hash', amhandler => 'hashhandler', amtype => 'i' },
 { oid => '783', oid_symbol => 'GIST_AM_OID',
   descr => 'GiST index access method',
   amname => 'gist', amhandler => 'gisthandler', amtype => 'i' },
 { oid => '2742', oid_symbol => 'GIN_AM_OID',
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index ced86faef8..5fee05c95f 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -50,16 +50,17 @@ typedef FormData_pg_am *Form_pg_am;
 DECLARE_UNIQUE_INDEX(pg_am_name_index, 2651, on pg_am using btree(amname name_ops));
 #define AmNameIndexId  2651
 DECLARE_UNIQUE_INDEX_PKEY(pg_am_oid_index, 2652, on pg_am using btree(oid oid_ops));
 #define AmOidIndexId  2652
 
 #ifdef EXPOSE_TO_CLIENT_CODE
 
 /*
  * Allowed values for amtype
  */
+#define AMTYPE_INTERFACE				'n' /* interface access method */
 #define AMTYPE_INDEX					'i' /* index access method */
 #define AMTYPE_TABLE					't' /* table access method */
 
 #endif							/* EXPOSE_TO_CLIENT_CODE */
 
 #endif							/* PG_AM_H */
diff --git a/src/include/catalog/pg_amimplements.dat b/src/include/catalog/pg_amimplements.dat
new file mode 100644
index 0000000000..298617b526
--- /dev/null
+++ b/src/include/catalog/pg_amimplements.dat
@@ -0,0 +1,17 @@
+#----------------------------------------------------------------------
+#
+# pg_amimplements.dat
+#    Initial contents of the pg_amimplements system catalog.
+#
+# Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/include/catalog/pg_amimplements.dat
+#
+#----------------------------------------------------------------------
+
+[
+
+{ amiamid => 'btree', amiparent => 'ordering', amiseqno => '1' },
+
+]
diff --git a/src/include/catalog/pg_amimplements.h b/src/include/catalog/pg_amimplements.h
new file mode 100644
index 0000000000..246b625595
--- /dev/null
+++ b/src/include/catalog/pg_amimplements.h
@@ -0,0 +1,51 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_amimplements.h
+ *	  definition of the "implements" system catalog (pg_amimplements)
+ *
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/catalog/pg_amimplements.h
+ *
+ * NOTES
+ *	  The Catalog.pm module reads this file and derives schema
+ *	  information.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_AMIMPLEMENTS_H
+#define PG_AMIMPLEMENTS_H
+
+#include "catalog/genbki.h"
+#include "catalog/pg_amimplements_d.h"
+
+#include "nodes/pg_list.h"
+#include "storage/lock.h"
+
+/* ----------------
+ *		pg_amimplements definition.  cpp turns this into
+ *		typedef struct FormData_pg_amimplements
+ * ----------------
+ */
+CATALOG(pg_amimplements,4544,AmimplementsRelationId)
+{
+	Oid			amiamid BKI_LOOKUP(pg_am);
+	Oid			amiparent BKI_LOOKUP(pg_am);
+	int32		amiseqno;
+} FormData_pg_amimplements;
+
+/* ----------------
+ *		Form_pg_amimplements corresponds to a pointer to a tuple with
+ *		the format of pg_amimplements relation.
+ * ----------------
+ */
+typedef FormData_pg_amimplements *Form_pg_amimplements;
+
+DECLARE_UNIQUE_INDEX_PKEY(pg_amimplements_amid_seqno_index, 4545, on pg_amimplements using btree(amiamid oid_ops, amiseqno int4_ops));
+#define AmimplementsAmidSeqnoIndexId	4545
+DECLARE_INDEX(pg_amimplements_parent_index, 4546, on pg_amimplements using btree(amiparent oid_ops));
+#define AmimplementsParentIndexId	4546
+
+#endif							/* PG_AMIMPLEMENTS_H */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index acbcae4607..02c128e6c0 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -899,20 +899,26 @@
 { oid => '319', descr => 'convert float4 to int4',
   proname => 'int4', prorettype => 'int4', proargtypes => 'float4',
   prosrc => 'ftoi4' },
 
 # Table access method handlers
 { oid => '3', descr => 'row-oriented heap table access method handler',
   proname => 'heap_tableam_handler', provolatile => 'v',
   prorettype => 'table_am_handler', proargtypes => 'internal',
   prosrc => 'heap_tableam_handler' },
 
+# Interface access method handlers
+{ oid => '5', descr => 'ordering interface access method handler',
+  proname => 'ordering_ifam_handler', provolatile => 'v',
+  prorettype => 'interface_am_handler', proargtypes => 'internal',
+  prosrc => 'ordering_ifam_handler' },
+
 # Index access method handlers
 { oid => '330', descr => 'btree index access method handler',
   proname => 'bthandler', provolatile => 'v', prorettype => 'index_am_handler',
   proargtypes => 'internal', prosrc => 'bthandler' },
 { oid => '331', descr => 'hash index access method handler',
   proname => 'hashhandler', provolatile => 'v',
   prorettype => 'index_am_handler', proargtypes => 'internal',
   prosrc => 'hashhandler' },
 { oid => '332', descr => 'gist index access method handler',
   proname => 'gisthandler', provolatile => 'v',
@@ -7298,20 +7304,27 @@
   proargtypes => 'cstring', prosrc => 'anynonarray_in' },
 { oid => '2778', descr => 'I/O',
   proname => 'anynonarray_out', prorettype => 'cstring',
   proargtypes => 'anynonarray', prosrc => 'anynonarray_out' },
 { oid => '3116', descr => 'I/O',
   proname => 'fdw_handler_in', proisstrict => 'f', prorettype => 'fdw_handler',
   proargtypes => 'cstring', prosrc => 'fdw_handler_in' },
 { oid => '3117', descr => 'I/O',
   proname => 'fdw_handler_out', prorettype => 'cstring',
   proargtypes => 'fdw_handler', prosrc => 'fdw_handler_out' },
+{ oid => '561', descr => 'I/O',
+  proname => 'interface_am_handler_in', proisstrict => 'f',
+  prorettype => 'interface_am_handler', proargtypes => 'cstring',
+  prosrc => 'interface_am_handler_in' },
+{ oid => '562', descr => 'I/O',
+  proname => 'interface_am_handler_out', prorettype => 'cstring',
+  proargtypes => 'interface_am_handler', prosrc => 'interface_am_handler_out' },
 { oid => '326', descr => 'I/O',
   proname => 'index_am_handler_in', proisstrict => 'f',
   prorettype => 'index_am_handler', proargtypes => 'cstring',
   prosrc => 'index_am_handler_in' },
 { oid => '327', descr => 'I/O',
   proname => 'index_am_handler_out', prorettype => 'cstring',
   proargtypes => 'index_am_handler', prosrc => 'index_am_handler_out' },
 { oid => '3311', descr => 'I/O',
   proname => 'tsm_handler_in', proisstrict => 'f', prorettype => 'tsm_handler',
   proargtypes => 'cstring', prosrc => 'tsm_handler_in' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 41074c994b..acaab8e0f8 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -613,20 +613,26 @@
   descr => 'pseudo-type representing a polymorphic base type that is an enum',
   typname => 'anyenum', typlen => '4', typbyval => 't', typtype => 'p',
   typcategory => 'P', typinput => 'anyenum_in', typoutput => 'anyenum_out',
   typreceive => '-', typsend => '-', typalign => 'i' },
 { oid => '3115',
   descr => 'pseudo-type for the result of an FDW handler function',
   typname => 'fdw_handler', typlen => '4', typbyval => 't', typtype => 'p',
   typcategory => 'P', typinput => 'fdw_handler_in',
   typoutput => 'fdw_handler_out', typreceive => '-', typsend => '-',
   typalign => 'i' },
+{ oid => '560',
+  descr => 'pseudo-type for the result of an interface AM handler function',
+  typname => 'interface_am_handler', typlen => '4', typbyval => 't', typtype => 'p',
+  typcategory => 'P', typinput => 'interface_am_handler_in',
+  typoutput => 'interface_am_handler_out', typreceive => '-', typsend => '-',
+  typalign => 'i' },
 { oid => '325',
   descr => 'pseudo-type for the result of an index AM handler function',
   typname => 'index_am_handler', typlen => '4', typbyval => 't', typtype => 'p',
   typcategory => 'P', typinput => 'index_am_handler_in',
   typoutput => 'index_am_handler_out', typreceive => '-', typsend => '-',
   typalign => 'i' },
 { oid => '3310',
   descr => 'pseudo-type for the result of a tablesample method function',
   typname => 'tsm_handler', typlen => '4', typbyval => 't', typtype => 'p',
   typcategory => 'P', typinput => 'tsm_handler_in',
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 42bf1c7519..32715b8fa6 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -37,21 +37,21 @@ extern ObjectAddress DefineIndex(Oid relationId,
 extern void ExecReindex(ParseState *pstate, ReindexStmt *stmt, bool isTopLevel);
 extern char *makeObjectName(const char *name1, const char *name2,
 							const char *label);
 extern char *ChooseRelationName(const char *name1, const char *name2,
 								const char *label, Oid namespaceid,
 								bool isconstraint);
 extern bool CheckIndexCompatible(Oid oldId,
 								 const char *accessMethodName,
 								 List *attributeList,
 								 List *exclusionOpNames);
-extern Oid	GetDefaultOpClass(Oid type_id, Oid am_id);
+extern Oid	GetDefaultOpClass(Oid type_id, List *am_ids);
 extern Oid	ResolveOpClass(List *opclass, Oid attrType,
 						   const char *accessMethodName, Oid accessMethodId);
 
 /* commands/functioncmds.c */
 extern ObjectAddress CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt);
 extern void RemoveFunctionById(Oid funcOid);
 extern ObjectAddress AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt);
 extern ObjectAddress CreateCast(CreateCastStmt *stmt);
 extern ObjectAddress CreateTransform(CreateTransformStmt *stmt);
 extern void IsThereFunctionInNamespace(const char *proname, int pronargs,
@@ -130,21 +130,21 @@ extern ObjectAddress AlterUserMapping(AlterUserMappingStmt *stmt);
 extern Oid	RemoveUserMapping(DropUserMappingStmt *stmt);
 extern void CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid);
 extern void ImportForeignSchema(ImportForeignSchemaStmt *stmt);
 extern Datum transformGenericOptions(Oid catalogId,
 									 Datum oldOptions,
 									 List *options,
 									 Oid fdwvalidator);
 
 /* commands/amcmds.c */
 extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt);
-extern Oid	get_index_am_oid(const char *amname, bool missing_ok);
+extern Oid	get_interface_or_index_am_oid(const char *amname, bool missing_ok);
 extern Oid	get_table_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);
 extern double defGetNumeric(DefElem *def);
 extern bool defGetBoolean(DefElem *def);
 extern int32 defGetInt32(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d9e417bcd7..6e8529997e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -511,20 +511,21 @@ typedef enum NodeTag
 	 * purposes (usually because they are involved in APIs where we want to
 	 * pass multiple object types through the same pointer).
 	 */
 	T_TriggerData,				/* in commands/trigger.h */
 	T_EventTriggerData,			/* in commands/event_trigger.h */
 	T_ReturnSetInfo,			/* in nodes/execnodes.h */
 	T_WindowObjectData,			/* private in nodeWindowAgg.c */
 	T_TIDBitmap,				/* in nodes/tidbitmap.h */
 	T_InlineCodeBlock,			/* in nodes/parsenodes.h */
 	T_FdwRoutine,				/* in foreign/fdwapi.h */
+	T_InterfaceAmRoutine,		/* in access/ifam.h */
 	T_IndexAmRoutine,			/* in access/amapi.h */
 	T_TableAmRoutine,			/* in access/tableam.h */
 	T_TsmRoutine,				/* in access/tsmapi.h */
 	T_ForeignKeyCacheInfo,		/* in utils/rel.h */
 	T_CallContext,				/* in nodes/parsenodes.h */
 	T_SupportRequestSimplify,	/* in nodes/supportnodes.h */
 	T_SupportRequestSelectivity,	/* in nodes/supportnodes.h */
 	T_SupportRequestCost,		/* in nodes/supportnodes.h */
 	T_SupportRequestRows,		/* in nodes/supportnodes.h */
 	T_SupportRequestIndexCondition	/* in nodes/supportnodes.h */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index def9651b34..8c639d892a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2509,20 +2509,21 @@ typedef struct 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 */
+	List	   *implements;		/* implemented interface access methods */
 } CreateAmStmt;
 
 /* ----------------------
  *		Create TRIGGER Statement
  * ----------------------
  */
 typedef struct CreateTrigStmt
 {
 	NodeTag		type;
 	bool		replace;		/* replace trigger if already exists */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index f836acf876..b5cb568d74 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -192,40 +192,42 @@ PG_KEYWORD("groups", GROUPS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("having", HAVING, RESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("hold", HOLD, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("hour", HOUR_P, UNRESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("identity", IDENTITY_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("if", IF_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("immediate", IMMEDIATE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("immutable", IMMUTABLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("implements", IMPLEMENTS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("implicit", IMPLICIT_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("import", IMPORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("in", IN_P, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("include", INCLUDE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inherits", INHERITS, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inline", INLINE_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inner", INNER_P, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("inout", INOUT, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("input", INPUT_P, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("insensitive", INSENSITIVE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("insert", INSERT, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("instead", INSTEAD, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("int", INT_P, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("integer", INTEGER, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("interface", INTERFACE, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("intersect", INTERSECT, RESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("interval", INTERVAL, COL_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("into", INTO, RESERVED_KEYWORD, AS_LABEL)
 PG_KEYWORD("invoker", INVOKER, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
 PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
 PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
 PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/test/regress/expected/amutils.out b/src/test/regress/expected/amutils.out
index 7ab6113c61..0b5381a65a 100644
--- a/src/test/regress/expected/amutils.out
+++ b/src/test/regress/expected/amutils.out
@@ -142,20 +142,26 @@ select amname, prop, pg_indexam_has_property(a.oid, prop) as p
  brin   | can_multi_col | t
  brin   | can_exclude   | f
  brin   | can_include   | f
  brin   | bogus         | 
  btree  | can_order     | t
  btree  | can_unique    | t
  btree  | can_multi_col | t
  btree  | can_exclude   | t
  btree  | can_include   | t
  btree  | bogus         | 
+ btree2 | can_order     | t
+ btree2 | can_unique    | t
+ btree2 | can_multi_col | t
+ btree2 | can_exclude   | t
+ btree2 | can_include   | t
+ btree2 | bogus         | 
  gin    | can_order     | f
  gin    | can_unique    | f
  gin    | can_multi_col | t
  gin    | can_exclude   | f
  gin    | can_include   | f
  gin    | bogus         | 
  gist   | can_order     | f
  gist   | can_unique    | f
  gist   | can_multi_col | t
  gist   | can_exclude   | t
@@ -166,21 +172,21 @@ select amname, prop, pg_indexam_has_property(a.oid, prop) as p
  hash   | can_multi_col | f
  hash   | can_exclude   | t
  hash   | can_include   | f
  hash   | bogus         | 
  spgist | can_order     | f
  spgist | can_unique    | f
  spgist | can_multi_col | f
  spgist | can_exclude   | t
  spgist | can_include   | t
  spgist | bogus         | 
-(36 rows)
+(42 rows)
 
 --
 -- additional checks for pg_index_column_has_property
 --
 CREATE TEMP TABLE foo (f1 int, f2 int, f3 int, f4 int);
 CREATE INDEX fooindex ON foo (f1 desc, f2 asc, f3 nulls first, f4 nulls last);
 select col, prop, pg_index_column_has_property(o, col, prop)
   from (values ('fooindex'::regclass)) v1(o),
        (values (1,'orderable'),(2,'asc'),(3,'desc'),
                (4,'nulls_first'),(5,'nulls_last'),
diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out
index 0dfb26c301..c662f613c1 100644
--- a/src/test/regress/expected/create_am.out
+++ b/src/test/regress/expected/create_am.out
@@ -1,15 +1,25 @@
 --
 -- Create access method tests
 --
--- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+-- Add synonyms to btree and gist
+CREATE ACCESS METHOD btree2 TYPE INDEX HANDLER bthandler IMPLEMENTS (ordering);
 CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
+-- Verify IMPLEMENTS
+CREATE ACCESS METHOD bogus TYPE INTERFACE HANDLER heap_tableam_handler IMPLEMENTS (ordering);
+ERROR:  function heap_tableam_handler must return type interface_am_handler
+CREATE ACCESS METHOD bogus TYPE TABLE HANDLER heap_tableam_handler IMPLEMENTS (ordering);
+ERROR:  only index access methods can implement interfaces
+CREATE ACCESS METHOD bogus TYPE INDEX HANDLER bthandler IMPLEMENTS (ordering, ordering);
+ERROR:  access method "ordering" would be implemented more than once
+CREATE ACCESS METHOD bogus TYPE INDEX HANDLER bthandler IMPLEMENTS (heap);
+ERROR:  access method "heap" is not of type INTERFACE
 -- Verify return type checks for handlers
 CREATE ACCESS METHOD bogus TYPE INDEX HANDLER int4in;
 ERROR:  function int4in(internal) does not exist
 CREATE ACCESS METHOD bogus TYPE INDEX HANDLER heap_tableam_handler;
 ERROR:  function heap_tableam_handler must return type index_am_handler
 -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
 CREATE INDEX grect2ind2 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
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 1461e947cd..e90ff0747a 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -116,20 +116,22 @@ NOTICE:  checking pg_operator {oprjoin} => pg_proc {oid}
 NOTICE:  checking pg_opfamily {opfmethod} => pg_am {oid}
 NOTICE:  checking pg_opfamily {opfnamespace} => pg_namespace {oid}
 NOTICE:  checking pg_opfamily {opfowner} => pg_authid {oid}
 NOTICE:  checking pg_opclass {opcmethod} => pg_am {oid}
 NOTICE:  checking pg_opclass {opcnamespace} => pg_namespace {oid}
 NOTICE:  checking pg_opclass {opcowner} => pg_authid {oid}
 NOTICE:  checking pg_opclass {opcfamily} => pg_opfamily {oid}
 NOTICE:  checking pg_opclass {opcintype} => pg_type {oid}
 NOTICE:  checking pg_opclass {opckeytype} => pg_type {oid}
 NOTICE:  checking pg_am {amhandler} => pg_proc {oid}
+NOTICE:  checking pg_amimplements {amiamid} => pg_am {oid}
+NOTICE:  checking pg_amimplements {amiparent} => pg_am {oid}
 NOTICE:  checking pg_amop {amopfamily} => pg_opfamily {oid}
 NOTICE:  checking pg_amop {amoplefttype} => pg_type {oid}
 NOTICE:  checking pg_amop {amoprighttype} => pg_type {oid}
 NOTICE:  checking pg_amop {amopopr} => pg_operator {oid}
 NOTICE:  checking pg_amop {amopmethod} => pg_am {oid}
 NOTICE:  checking pg_amop {amopsortfamily} => pg_opfamily {oid}
 NOTICE:  checking pg_amproc {amprocfamily} => pg_opfamily {oid}
 NOTICE:  checking pg_amproc {amproclefttype} => pg_type {oid}
 NOTICE:  checking pg_amproc {amprocrighttype} => pg_type {oid}
 NOTICE:  checking pg_amproc {amproc} => pg_proc {oid}
diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out
index 1b2f6bc418..c13d59b15c 100644
--- a/src/test/regress/expected/psql.out
+++ b/src/test/regress/expected/psql.out
@@ -4891,45 +4891,49 @@ TOAST table "pg_toast.pg_toast_2619"
  chunk_id   | oid
  chunk_seq  | integer
  chunk_data | bytea
 Owning table: "pg_catalog.pg_statistic"
 Indexes:
     "pg_toast_2619_index" PRIMARY KEY, btree (chunk_id, chunk_seq)
 
 -- check printing info about access methods
 \dA
 List of access methods
-  Name  | Type  
---------+-------
- brin   | Index
- btree  | Index
- gin    | Index
- gist   | Index
- hash   | Index
- heap   | Table
- heap2  | Table
- spgist | Index
-(8 rows)
+   Name   |   Type    
+----------+-----------
+ brin     | Index
+ btree    | Index
+ btree2   | Index
+ gin      | Index
+ gist     | Index
+ hash     | Index
+ heap     | Table
+ heap2    | Table
+ ordering | Interface
+ spgist   | Index
+(10 rows)
 
 \dA *
 List of access methods
-  Name  | Type  
---------+-------
- brin   | Index
- btree  | Index
- gin    | Index
- gist   | Index
- hash   | Index
- heap   | Table
- heap2  | Table
- spgist | Index
-(8 rows)
+   Name   |   Type    
+----------+-----------
+ brin     | Index
+ btree    | Index
+ btree2   | Index
+ gin      | Index
+ gist     | Index
+ hash     | Index
+ heap     | Table
+ heap2    | Table
+ ordering | Interface
+ spgist   | Index
+(10 rows)
 
 \dA h*
 List of access methods
  Name  | Type  
 -------+-------
  hash  | Index
  heap  | Table
  heap2 | Table
 (3 rows)
 
@@ -4940,46 +4944,50 @@ List of access methods
 (0 rows)
 
 \dA foo bar
 List of access methods
  Name | Type 
 ------+------
 (0 rows)
 
 \dA: extra argument "bar" ignored
 \dA+
-                             List of access methods
-  Name  | Type  |       Handler        |              Description               
---------+-------+----------------------+----------------------------------------
- brin   | Index | brinhandler          | block range index (BRIN) access method
- btree  | Index | bthandler            | b-tree index access method
- gin    | Index | ginhandler           | GIN index access method
- gist   | Index | gisthandler          | GiST index access method
- hash   | Index | hashhandler          | hash index access method
- heap   | Table | heap_tableam_handler | heap table access method
- heap2  | Table | heap_tableam_handler | 
- spgist | Index | spghandler           | SP-GiST index access method
-(8 rows)
+                                List of access methods
+   Name   |   Type    |        Handler        |              Description               
+----------+-----------+-----------------------+----------------------------------------
+ brin     | Index     | brinhandler           | block range index (BRIN) access method
+ btree    | Index     | bthandler             | b-tree index access method
+ btree2   | Index     | bthandler             | 
+ gin      | Index     | ginhandler            | GIN index access method
+ gist     | Index     | gisthandler           | GiST index access method
+ hash     | Index     | hashhandler           | hash index access method
+ heap     | Table     | heap_tableam_handler  | heap table access method
+ heap2    | Table     | heap_tableam_handler  | 
+ ordering | Interface | ordering_ifam_handler | ordering interface
+ spgist   | Index     | spghandler            | SP-GiST index access method
+(10 rows)
 
 \dA+ *
-                             List of access methods
-  Name  | Type  |       Handler        |              Description               
---------+-------+----------------------+----------------------------------------
- brin   | Index | brinhandler          | block range index (BRIN) access method
- btree  | Index | bthandler            | b-tree index access method
- gin    | Index | ginhandler           | GIN index access method
- gist   | Index | gisthandler          | GiST index access method
- hash   | Index | hashhandler          | hash index access method
- heap   | Table | heap_tableam_handler | heap table access method
- heap2  | Table | heap_tableam_handler | 
- spgist | Index | spghandler           | SP-GiST index access method
-(8 rows)
+                                List of access methods
+   Name   |   Type    |        Handler        |              Description               
+----------+-----------+-----------------------+----------------------------------------
+ brin     | Index     | brinhandler           | block range index (BRIN) access method
+ btree    | Index     | bthandler             | b-tree index access method
+ btree2   | Index     | bthandler             | 
+ gin      | Index     | ginhandler            | GIN index access method
+ gist     | Index     | gisthandler           | GiST index access method
+ hash     | Index     | hashhandler           | hash index access method
+ heap     | Table     | heap_tableam_handler  | heap table access method
+ heap2    | Table     | heap_tableam_handler  | 
+ ordering | Interface | ordering_ifam_handler | ordering interface
+ spgist   | Index     | spghandler            | SP-GiST index access method
+(10 rows)
 
 \dA+ h*
                      List of access methods
  Name  | Type  |       Handler        |       Description        
 -------+-------+----------------------+--------------------------
  hash  | Index | hashhandler          | hash index access method
  heap  | Table | heap_tableam_handler | heap table access method
  heap2 | Table | heap_tableam_handler | 
 (3 rows)
 
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index d9ce961be2..f5b3cf98b1 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -96,20 +96,21 @@ nummultirange_test|t
 numrange_test|t
 onek|t
 onek2|t
 path_tbl|f
 person|f
 persons|f
 persons2|t
 persons3|t
 pg_aggregate|t
 pg_am|t
+pg_amimplements|t
 pg_amop|t
 pg_amproc|t
 pg_attrdef|t
 pg_attribute|t
 pg_auth_members|t
 pg_authid|t
 pg_cast|t
 pg_class|t
 pg_collation|t
 pg_constraint|t
diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql
index 9a359466ce..c102449641 100644
--- a/src/test/regress/sql/create_am.sql
+++ b/src/test/regress/sql/create_am.sql
@@ -1,17 +1,24 @@
 --
 -- Create access method tests
 --
 
--- Make gist2 over gisthandler. In fact, it would be a synonym to gist.
+-- Add synonyms to btree and gist
+CREATE ACCESS METHOD btree2 TYPE INDEX HANDLER bthandler IMPLEMENTS (ordering);
 CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler;
 
+-- Verify IMPLEMENTS
+CREATE ACCESS METHOD bogus TYPE INTERFACE HANDLER heap_tableam_handler IMPLEMENTS (ordering);
+CREATE ACCESS METHOD bogus TYPE TABLE HANDLER heap_tableam_handler IMPLEMENTS (ordering);
+CREATE ACCESS METHOD bogus TYPE INDEX HANDLER bthandler IMPLEMENTS (ordering, ordering);
+CREATE ACCESS METHOD bogus TYPE INDEX HANDLER bthandler IMPLEMENTS (heap);
+
 -- Verify return type checks for handlers
 CREATE ACCESS METHOD bogus TYPE INDEX HANDLER int4in;
 CREATE ACCESS METHOD bogus TYPE INDEX HANDLER heap_tableam_handler;
 
 
 -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist
 CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base);
 
 -- Make operator class for boxes using gist2
 CREATE OPERATOR CLASS box_ops DEFAULT
-- 
2.30.1 (Apple Git-130)

