From 84e10721a343d229da415891e8f263ad832d4c66 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Wed, 2 Mar 2016 15:32:02 +0100
Subject: [PATCH 1/3] create-am-infra

---
 src/backend/access/index/amapi.c     |   7 +
 src/backend/catalog/dependency.c     |  10 +-
 src/backend/catalog/objectaddress.c  | 113 ++++++++++++++-
 src/backend/commands/Makefile        |   2 +-
 src/backend/commands/amcmds.c        | 272 +++++++++++++++++++++++++++++++++++
 src/backend/commands/event_trigger.c |   3 +
 src/backend/commands/opclasscmds.c   |  42 +-----
 src/backend/nodes/copyfuncs.c        |  15 ++
 src/backend/nodes/equalfuncs.c       |  13 ++
 src/backend/parser/parse_utilcmd.c   |   3 +-
 src/backend/tcop/utility.c           |  18 +++
 src/bin/pg_dump/common.c             |   5 +
 src/bin/pg_dump/pg_dump.c            | 134 +++++++++++++++++
 src/bin/pg_dump/pg_dump.h            |   8 ++
 src/bin/pg_dump/pg_dump_sort.c       |   6 +
 src/include/catalog/dependency.h     |   5 +-
 src/include/catalog/pg_am.h          |  24 +++-
 src/include/commands/defrem.h        |   8 +-
 src/include/nodes/nodes.h            |   1 +
 src/include/nodes/parsenodes.h       |  13 ++
 20 files changed, 652 insertions(+), 50 deletions(-)
 create mode 100644 src/backend/commands/amcmds.c

diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
index bda166a..e9b9b3f 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\" type is not index",
+						NameStr(amform->amname))));
+
 	amhandler = amform->amhandler;
 
 	/* Complain if handler OID is invalid */
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index c48e37b..a86a488 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"
@@ -160,7 +161,8 @@ static const Oid object_classes[] = {
 	ExtensionRelationId,		/* OCLASS_EXTENSION */
 	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
 	PolicyRelationId,			/* OCLASS_POLICY */
-	TransformRelationId			/* OCLASS_TRANSFORM */
+	TransformRelationId,		/* OCLASS_TRANSFORM */
+	AccessMethodRelationId		/* OCLASS_AM */
 };
 
 
@@ -1270,6 +1272,9 @@ doDeletion(const ObjectAddress *object, int flags)
 
 		case OCLASS_TRANSFORM:
 			DropTransformById(object->objectId);
+
+		case OCLASS_AM:
+			RemoveAccessMethodById(object->objectId);
 			break;
 
 		default:
@@ -2415,6 +2420,9 @@ getObjectClass(const ObjectAddress *object)
 
 		case TransformRelationId:
 			return OCLASS_TRANSFORM;
+
+		case AccessMethodRelationId:
+			return OCLASS_AM;
 	}
 
 	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index d2aaa6d..2506344 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -438,6 +438,18 @@ static const ObjectPropertyType ObjectProperty[] =
 		Anum_pg_type_typacl,
 		ACL_KIND_TYPE,
 		true
+	},
+	{
+		AccessMethodRelationId,
+		AmOidIndexId,
+		AMOID,
+		AMNAME,
+		Anum_pg_am_amname,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		-1,
+		true
 	}
 };
 
@@ -640,6 +652,10 @@ static const struct object_type_map
 	/* OCLASS_TRANSFORM */
 	{
 		"transform", OBJECT_TRANSFORM
+	},
+	/* OCLASS_AM */
+	{
+		"access method", OBJECT_ACCESS_METHOD,
 	}
 };
 
@@ -674,6 +690,8 @@ static ObjectAddress get_object_address_usermapping(List *objname,
 							   List *objargs, bool missing_ok);
 static ObjectAddress get_object_address_defacl(List *objname, List *objargs,
 						  bool missing_ok);
+static ObjectAddress get_object_address_am(ObjectType objtype, List *objname,
+						bool missing_ok);
 static const ObjectPropertyType *get_object_property_data(Oid class_id);
 
 static void getRelationDescription(StringInfo buffer, Oid relid);
@@ -913,6 +931,9 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				address = get_object_address_defacl(objname, objargs,
 													missing_ok);
 				break;
+			case OBJECT_ACCESS_METHOD:
+				address = get_object_address_am(objtype, objname, missing_ok);
+				break;
 			default:
 				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 				/* placate compiler, in case it thinks elog might return */
@@ -1489,7 +1510,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_am_oid(strVal(linitial(objname)), AMTYPE_INDEX, false);
 	objname = list_copy_tail(objname, 1);
 
 	switch (objtype)
@@ -1516,6 +1537,65 @@ get_object_address_opcf(ObjectType objtype, List *objname, bool missing_ok)
 }
 
 /*
+ * Find the ObjectAddress for an access method.
+ */
+static ObjectAddress
+get_object_address_am(ObjectType objtype, List *objname, bool missing_ok)
+{
+	ObjectAddress	address;
+	char		   *amname, *catalogname;
+	Type		tup;
+
+	switch (list_length(objname))
+	{
+		case 1:
+			amname = strVal(linitial(objname));
+			break;
+		case 2:
+			catalogname = strVal(linitial(objname));
+			amname = strVal(lsecond(objname));
+
+			/*
+			 * We check the catalog name and then ignore it.
+			 */
+			if (strcmp(catalogname, get_database_name(MyDatabaseId)) != 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				  errmsg("cross-database references are not implemented: %s",
+						 NameListToString(objname))));
+			break;
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+				errmsg("improper access method name (too many dotted names): %s",
+					   NameListToString(objname))));
+			break;
+	}
+
+	address.classId = AccessMethodRelationId;
+	address.objectId = InvalidOid;
+	address.objectSubId = 0;
+
+	tup = SearchSysCache1(AMNAME, PointerGetDatum(amname));
+	if (!HeapTupleIsValid(tup))
+	{
+		/* Access method is missing, report error if needed */
+		if (!missing_ok)
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_OBJECT),
+					 errmsg("access method \"%s\" does not exist",
+							amname)));
+		return address;
+	}
+
+	/* Access method is found, return its oid */
+	address.objectId = HeapTupleGetOid(tup);
+	ReleaseSysCache(tup);
+
+	return address;
+}
+
+/*
  * Find the ObjectAddress for an opclass/opfamily member.
  *
  * (The returned address corresponds to a pg_amop/pg_amproc object).
@@ -2179,6 +2259,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 +3210,18 @@ getObjectDescription(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);
+				appendStringInfo(&buffer, _("access method %s"), amname);
+				break;
+			}
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
@@ -3610,6 +3703,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 +4663,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 b1ac704..6b3742c 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 0000000..c79cbb1
--- /dev/null
+++ b/src/backend/commands/amcmds.c
@@ -0,0 +1,272 @@
+/*-------------------------------------------------------------------------
+ *
+ * amcmds.c
+ *	  Routines for SQL commands that manipulate access methods.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/commands/amcmds.c
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/xact.h"
+#include "catalog/binary_upgrade.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/heap.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_collation.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_enum.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_proc_fn.h"
+#include "catalog/pg_range.h"
+#include "catalog/pg_type.h"
+#include "catalog/pg_type_fn.h"
+#include "commands/dbcommands.h"
+#include "commands/defrem.h"
+#include "commands/tablecmds.h"
+#include "commands/typecmds.h"
+#include "executor/executor.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "optimizer/planner.h"
+#include "optimizer/var.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_func.h"
+#include "parser/parse_type.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+#include "utils/snapmgr.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+/*
+ * Convert a handler function name passed from the parser to an Oid. This
+ * function either return valid function Oid or throw an error.
+ */
+static Oid
+lookup_am_handler_func(List *handler_name, Oid handler_type)
+{
+	Oid			handlerOid;
+	Oid			funcargtypes[1] = {INTERNALOID};
+
+	if (handler_name == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("handler function is not specified")));
+
+	/* handlers have no arguments */
+	handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false);
+
+	/* check that handler has correct return type */
+	if (get_func_rettype(handlerOid) != handler_type)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("function %s must return type \"%s\"",
+						NameListToString(handler_name),
+						format_type_be(handler_type))));
+
+	return handlerOid;
+}
+
+/*
+ * CreateAcessMethod
+ *		Registers a new access method.
+ */
+ObjectAddress
+CreateAccessMethod(CreateAmStmt *stmt)
+{
+	Relation		rel;
+	ObjectAddress	myself;
+	ObjectAddress	referenced;
+	Oid				amoid;
+	Oid				amhandler;
+	bool			nulls[Natts_pg_am];
+	Datum			values[Natts_pg_am];
+	HeapTuple		tup;
+
+	rel = heap_open(AccessMethodRelationId, RowExclusiveLock);
+
+	/* Must be super user */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+			errmsg("permission denied to create access method \"%s\"",
+				   stmt->amname),
+			errhint("Must be superuser to create access method.")));
+
+	/* Check if name is busy */
+	amoid = GetSysCacheOid1(AMNAME, CStringGetDatum(stmt->amname));
+	if (OidIsValid(amoid))
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("access method \"%s\" already exists", stmt->amname)));
+	}
+
+	/*
+	 * Get handler function oid. Handler signature depends on access method
+	 * type.
+	 */
+	switch(stmt->amtype)
+	{
+		default:
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("invalid access method type \"%c\"", stmt->amtype)));
+			break;
+	}
+
+	/*
+	 * Insert tuple into pg_am.
+	 */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+
+	values[Anum_pg_am_amname - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(stmt->amname));
+	values[Anum_pg_am_amhandler - 1] = ObjectIdGetDatum(amhandler);
+	values[Anum_pg_am_amtype - 1] = CharGetDatum(stmt->amtype);
+
+	tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+	amoid = simple_heap_insert(rel, tup);
+	CatalogUpdateIndexes(rel, tup);
+	heap_freetuple(tup);
+
+	myself.classId = AccessMethodRelationId;
+	myself.objectId = amoid;
+	myself.objectSubId = 0;
+
+	/* Record dependecy on handler function */
+	referenced.classId = ProcedureRelationId;
+	referenced.objectId = amhandler;
+	referenced.objectSubId = 0;
+
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	heap_close(rel, RowExclusiveLock);
+
+	return myself;
+}
+
+/*
+ * Guts of access method deletion.
+ */
+void
+RemoveAccessMethodById(Oid amOid)
+{
+	Relation	relation;
+	HeapTuple	tup;
+
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to drop access methods")));
+
+	relation = heap_open(AccessMethodRelationId, RowExclusiveLock);
+
+	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+	if (!HeapTupleIsValid(tup))
+		elog(ERROR, "cache lookup failed for access method %u", amOid);
+
+	simple_heap_delete(relation, &tup->t_self);
+
+	ReleaseSysCache(tup);
+
+	heap_close(relation, RowExclusiveLock);
+}
+
+/*
+ * Convert single charater access method type into string for error reporting.
+ */
+static char *
+get_am_type_string(char amtype)
+{
+	switch (amtype)
+	{
+		default:
+			elog(ERROR, "invalid access method type \"%c\"", amtype);
+	}
+}
+
+/*
+ * get_am_oid - given an access method name and type, look up the OID
+ *
+ * If missing_ok is false, throw an error if access method not found.  If
+ * true, just return InvalidOid.
+ */
+Oid
+get_am_oid(const char *amname, char amtype, bool missing_ok)
+{
+	HeapTuple	tup;
+	Oid			oid = InvalidOid;
+
+	tup = SearchSysCache1(AMNAME, CStringGetDatum(amname));
+	if (HeapTupleIsValid(tup))
+	{
+		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+
+		if (amform->amtype != amtype)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+					 errmsg("access method \"%s\" type is not %s",
+							NameStr(amform->amname),
+							get_am_type_string(amtype))));
+
+		oid = HeapTupleGetOid(tup);
+		ReleaseSysCache(tup);
+	}
+
+	if (!OidIsValid(oid) && !missing_ok)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("access method \"%s\" does not exist", amname)));
+	return oid;
+}
+
+/*
+ * get_am_name - given an access method OID name and type, look up the name
+ *
+ * Access method type is not required for lookup.  However it's useful to check
+ * the type to ensure it is what we're looking for.
+ */
+char *
+get_am_name(Oid amOid)
+{
+	HeapTuple	tup;
+	char	   *result = NULL;
+
+	tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
+	if (HeapTupleIsValid(tup))
+	{
+		Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
+
+		result = pstrdup(NameStr(amform->amname));
+		ReleaseSysCache(tup);
+	}
+	return result;
+}
+
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9e32f8d..3f52ad8 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 8a66196..19dac98 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_am_oid(stmt->amname, AMTYPE_INDEX, 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 a9e9cc3..85bd5ac 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3828,6 +3828,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)
 {
@@ -4819,6 +4831,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 b9c3959..f19fde1 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1855,6 +1855,16 @@ _equalCreateTransformStmt(const CreateTransformStmt *a, const CreateTransformStm
 }
 
 static bool
+_equalCreateAmStmt(const CreateAmStmt *a, const CreateAmStmt *b)
+{
+	COMPARE_STRING_FIELD(amname);
+	COMPARE_NODE_FIELD(handler_name);
+	COMPARE_SCALAR_FIELD(amtype);
+
+	return true;
+}
+
+static bool
 _equalCreateTrigStmt(const CreateTrigStmt *a, const CreateTrigStmt *b)
 {
 	COMPARE_STRING_FIELD(trigname);
@@ -3146,6 +3156,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/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index dc431c7..8ae5400 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1709,7 +1709,8 @@ 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_am_oid(DEFAULT_INDEX_TYPE,
+												   AMTYPE_INDEX, 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 045f7f0..4d0aac9 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/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index f798b15..1acd91a 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;
@@ -169,6 +170,10 @@ getSchemaData(Archive *fout, int *numTablesPtr)
 	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 64c2673..8999bcc 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -173,6 +173,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);
@@ -4101,6 +4102,82 @@ getConversions(Archive *fout, int *numConversions)
 }
 
 /*
+ * getAccessMethods:
+ *	  read all user-defined access methods in the system catalogs and return
+ *    them in the AccessMethodInfo* structure
+ *
+ *	numAccessMethods is set to the number of access methods read in
+ */
+AccessMethodInfo *
+getAccessMethods(Archive *fout, int *numAccessMethods)
+{
+	DumpOptions *dopt = fout->dopt;
+	PGresult   *res;
+	int			ntups;
+	int			i;
+	PQExpBuffer query;
+	AccessMethodInfo *aminfo;
+	int			i_tableoid;
+	int			i_oid;
+	int			i_amname;
+	int			i_amhandler;
+
+	/* Before 9.6, there are no user-defined access methods */
+	if (fout->remoteVersion < 90600)
+	{
+		*numAccessMethods = 0;
+		return NULL;
+	}
+
+	query = createPQExpBuffer();
+
+	/* Make sure we are in proper schema */
+	selectSourceSchema(fout, "pg_catalog");
+
+	/*
+	 * Select only user-defined access methods assuming all built-in access
+	 * methods have oid < 10000.
+	 */
+	appendPQExpBuffer(query, "SELECT tableoid, oid, amname, "
+					  "amhandler::pg_catalog.regproc AS amhandler "
+					  "FROM pg_am "
+					  "WHERE oid >= 10000");
+
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+	*numAccessMethods = ntups;
+
+	aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
+
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_amname = PQfnumber(res, "amname");
+	i_amhandler = PQfnumber(res, "amhandler");
+
+	for (i = 0; i < ntups; i++)
+	{
+		aminfo[i].dobj.objType = DO_ACCESS_METHOD;
+		aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+		aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+		AssignDumpId(&aminfo[i].dobj);
+		aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
+		aminfo[i].dobj.namespace = NULL;
+		aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
+
+		/* Decide whether we want to dump it */
+		selectDumpableObject(&(aminfo[i].dobj), dopt);
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return aminfo;
+}
+
+
+/*
  * getOpclasses:
  *	  read all opclasses in the system catalogs and return them in the
  * OpclassInfo* structure
@@ -8408,6 +8485,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 +11526,59 @@ 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 HANDLER %s;\n",
+					  qamname, aminfo->amhandler);
+
+	appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
+					  qamname);
+
+	appendPQExpBuffer(labelq, "ACCESS METHOD %s",
+					  qamname);
+
+	ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
+				 aminfo->dobj.name,
+				 NULL,
+				 NULL,
+				 "",
+				 false, "ACCESS METHOD", SECTION_PRE_DATA,
+				 q->data, delq->data, NULL,
+				 NULL, 0,
+				 NULL, NULL);
+
+	/* Dump Access Method Comments */
+	dumpComment(fout, labelq->data,
+				NULL, "",
+				aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
+
+	free(qamname);
+
+	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(delq);
+	destroyPQExpBuffer(labelq);
+}
 
 /*
  * dumpOpclass
@@ -16227,6 +16360,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 9a1d8f8..0fb6087 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,12 @@ typedef struct _oprInfo
 	Oid			oprcode;
 } OprInfo;
 
+typedef struct _accessMethodInfo
+{
+	DumpableObject dobj;
+	char	   *amhandler;
+} AccessMethodInfo;
+
 typedef struct _opclassInfo
 {
 	DumpableObject dobj;
@@ -548,6 +555,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 78ff59c..3bd5bd8 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -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,10 @@ 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,
+					 "");
+			return;
 		case DO_OPCLASS:
 			snprintf(buf, bufsize,
 					 "OPERATOR CLASS %s  (ID %d OID %u)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 049bf9f..ac16740 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -153,10 +153,11 @@ typedef enum ObjectClass
 	OCLASS_EXTENSION,			/* pg_extension */
 	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
 	OCLASS_POLICY,				/* pg_policy */
-	OCLASS_TRANSFORM			/* pg_transform */
+	OCLASS_TRANSFORM,			/* pg_transform */
+	OCLASS_AM,					/* pg_am */
 } ObjectClass;
 
-#define LAST_OCLASS		OCLASS_TRANSFORM
+#define LAST_OCLASS		OCLASS_AM
 
 
 /* in dependency.c */
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index f801c3e..f1bdee4 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -35,6 +35,13 @@ CATALOG(pg_am,2601)
 {
 	NameData	amname;			/* access method name */
 	regproc		amhandler;		/* handler function */
+
+	/*----------
+	 * Type of access method. Possible values are
+	 *		'i': Index access method
+	 *----------
+	 */
+	char		amtype;
 } FormData_pg_am;
 
 /* ----------------
@@ -48,31 +55,34 @@ 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
+
+#define		AMTYPE_INDEX				'i'		/* index */
 
 /* ----------------
  *		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 54f67e9..1e21f03 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,12 @@ 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_am_oid(const char *amname, char amtype, bool missing_ok);
+extern char *get_am_name(Oid amOid);
+
 /* support routines in commands/define.c */
 
 extern char *defGetString(DefElem *def);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c407fa2..7d46763 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -386,6 +386,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 2fd0629..8b958b4 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
  * ----------------------
-- 
1.9.1

