>From 7286e9f2bc9f110019fbc6532fc7ff6405c385f2 Mon Sep 17 00:00:00 2001
From: Petr Jelinek <pjmodos@pjmodos.net>
Date: Thu, 8 Jan 2015 23:26:39 +0100
Subject: [PATCH 2/3] seqam ddl v2

---
 doc/src/sgml/ref/allfiles.sgml       |   2 +
 doc/src/sgml/ref/create_seqam.sgml   | 174 ++++++++++++++++
 doc/src/sgml/ref/drop_seqam.sgml     |  87 ++++++++
 doc/src/sgml/reference.sgml          |   2 +
 src/backend/access/sequence/seqam.c  | 384 +++++++++++++++++++++++++++++++++++
 src/backend/catalog/dependency.c     |  11 +-
 src/backend/catalog/objectaddress.c  |  66 +++++-
 src/backend/commands/dropcmds.c      |   4 +
 src/backend/commands/event_trigger.c |   3 +
 src/backend/commands/sequence.c      |  17 ++
 src/backend/parser/gram.y            |  14 +-
 src/backend/tcop/utility.c           |  11 +
 src/include/access/seqam.h           |   3 +
 src/include/catalog/dependency.h     |   1 +
 src/include/catalog/pg_proc.h        |   4 +-
 src/include/nodes/parsenodes.h       |   1 +
 src/include/parser/kwlist.h          |   1 +
 17 files changed, 780 insertions(+), 5 deletions(-)
 create mode 100644 doc/src/sgml/ref/create_seqam.sgml
 create mode 100644 doc/src/sgml/ref/drop_seqam.sgml

diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 7aa3128..2e4a250 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -74,6 +74,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY createRole         SYSTEM "create_role.sgml">
 <!ENTITY createRule         SYSTEM "create_rule.sgml">
 <!ENTITY createSchema       SYSTEM "create_schema.sgml">
+<!ENTITY createSeqAM        SYSTEM "create_seqam.sgml">
 <!ENTITY createSequence     SYSTEM "create_sequence.sgml">
 <!ENTITY createServer       SYSTEM "create_server.sgml">
 <!ENTITY createTable        SYSTEM "create_table.sgml">
@@ -116,6 +117,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY dropRole           SYSTEM "drop_role.sgml">
 <!ENTITY dropRule           SYSTEM "drop_rule.sgml">
 <!ENTITY dropSchema         SYSTEM "drop_schema.sgml">
+<!ENTITY dropSeqAM          SYSTEM "drop_seqam.sgml">
 <!ENTITY dropSequence       SYSTEM "drop_sequence.sgml">
 <!ENTITY dropServer         SYSTEM "drop_server.sgml">
 <!ENTITY dropTable          SYSTEM "drop_table.sgml">
diff --git a/doc/src/sgml/ref/create_seqam.sgml b/doc/src/sgml/ref/create_seqam.sgml
new file mode 100644
index 0000000..7892820
--- /dev/null
+++ b/doc/src/sgml/ref/create_seqam.sgml
@@ -0,0 +1,174 @@
+<!--
+doc/src/sgml/ref/create_seqam.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-CREATESEQAM">
+ <indexterm zone="sql-createseqam">
+  <primary>CREATE ACCESS METHOD FOR SEQUENCES</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>CREATE ACCESS METHOD FOR SEQUENCES</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE ACCESS METHOD FOR SEQUENCES</refname>
+  <refpurpose>define custom sequence access method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE ACCESS METHOD FOR SEQUENCES <replaceable class="parameter">name</replaceable> (
+    EXTRACOLUMNS = <replaceable class="parameter">_function</replaceable> ,
+    RELOPTIONS = <replaceable class="parameter">reloptions_function</replaceable> ,
+    INIT = <replaceable class="parameter">init_function</replaceable> ,
+    ALLOC = <replaceable class="parameter">alloc_function</replaceable> ,
+    SETVAL = <replaceable class="parameter">setval_function</replaceable> ,
+    DUMP = <replaceable class="parameter">dump_function</replaceable>,
+    RESTORE = <replaceable class="parameter">restore_function</replaceable>
+)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CREATE ACCESS METHOD FOR SEQUENCES</command> creates a sequence
+   access method. A sequence access method changes how values for SEQUENCEs
+   are generated.
+  </para>
+
+  <para>
+   You must be a superuser to use <command>CREATE ACCESS METHOD FOR SEQUENCES</command>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the sequence access method to be created. This name must be
+      unique within the database.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">extracolumns_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the function that returns extra columns for the sequence
+      access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">reloptions_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the function for parsing reloptions for the sequence access
+      method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">init_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the init function for the sequence access method.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">alloc_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the function which allocates new sequence id.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">setval_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the function which handles the <function>setval</function>
+      function call.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">dump_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the function used for dumping current state of the sequence
+      to a string. This function will be called by
+      <application>pg_dump</application>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">restore_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of the function used for restoring the state of the sequence
+      from a string. Calls to this function will be present in a database dumps
+      created by <application>pg_dump</application>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+  </variablelist>
+
+  <para>
+   The function names can be schema-qualified if necessary.  Argument types
+   are not given, since the argument list for each type of function is
+   predetermined.  All functions are required.
+  </para>
+
+  <para>
+   The arguments can appear in any order, not only the one shown above.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   See the <filename>contrib/gapless_seq</> for example on how to write new
+   sequence access methods and how to use this command.
+  </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   There is no
+   <command>CREATE ACCESS METHOD FOR SEQUENCES</command> statement in the SQL
+   standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-dropseqam"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/drop_seqam.sgml b/doc/src/sgml/ref/drop_seqam.sgml
new file mode 100644
index 0000000..959b777
--- /dev/null
+++ b/doc/src/sgml/ref/drop_seqam.sgml
@@ -0,0 +1,87 @@
+<!--
+doc/src/sgml/ref/drop_seqam.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-DROPSEQAM">
+ <indexterm zone="sql-dropseqam">
+  <primary>DROP ACCESS METHOD FOR SEQUENCES</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>DROP ACCESS METHOD FOR SEQUENCES</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP ACCESS METHOD FOR SEQUENCES</refname>
+  <refpurpose>remove a custom sequence access method</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP ACCESS METHOD FOR SEQUENCES [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP ACCESS METHOD FOR SEQUENCES</command> drop an existing
+   sequence access method.
+  </para>
+
+  <para>
+   You must be a superuser to use the
+   <command>DROP ACCESS METHOD FOR SEQUENCES</command>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+
+   <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the sequence access method does not exist.
+      A notice is issued in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing sequence access method to be removed.
+     </para>
+    </listitem>
+   </varlistentry>
+
+  </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   There is no
+   <command>DDROP ACCESS METHOD FOR SEQUENCES</command> statement in the SQL
+   standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createseqam"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 10c9a6d..aeff87e 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -102,6 +102,7 @@
    &createRole;
    &createRule;
    &createSchema;
+   &createSeqAM;
    &createSequence;
    &createServer;
    &createTable;
@@ -144,6 +145,7 @@
    &dropRole;
    &dropRule;
    &dropSchema;
+   &dropSeqAM;
    &dropSequence;
    &dropServer;
    &dropTable;
diff --git a/src/backend/access/sequence/seqam.c b/src/backend/access/sequence/seqam.c
index 7896d0f..109de2a 100644
--- a/src/backend/access/sequence/seqam.c
+++ b/src/backend/access/sequence/seqam.c
@@ -68,8 +68,20 @@
 #include "access/relscan.h"
 #include "access/transam.h"
 #include "access/xact.h"
+#include "catalog/dependency.h"
+#include "catalog/objectaccess.h"
+#include "catalog/objectaddress.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_seqam.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "miscadmin.h"
+#include "parser/parse_func.h"
+#include "parser/parse_type.h"
+#include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
@@ -398,6 +410,378 @@ check_default_seqam(char **newval, void **extra, GucSource source)
 
 
 /*
+ * Find seqam function by name and validate it.
+ */
+static Datum
+get_seqam_func(DefElem *defel, int attnum)
+{
+	List	   *funcName = defGetQualifiedName(defel);
+	Oid			typeId[6];
+	Oid			retTypeId;
+	int			nargs;
+	Oid			procOid;
+
+	typeId[0] = INTERNALOID;
+
+	switch (attnum)
+	{
+		case Anum_pg_seqam_seqamreloptions:
+			nargs = 2;
+			typeId[1] = BOOLOID;
+			retTypeId = BYTEAOID;
+			break;
+
+		case Anum_pg_seqam_seqaminit:
+			nargs = 5;
+			typeId[0] = OIDOID;
+			typeId[1] = INTERNALOID;
+			typeId[2] = BYTEAOID;
+			typeId[3] = INTERNALOID;
+			typeId[4] = INTERNALOID;
+			retTypeId = VOIDOID;
+			break;
+
+		case Anum_pg_seqam_seqamalloc:
+			nargs = 4;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INT8OID;
+			typeId[3] = INTERNALOID;
+			retTypeId = INT8OID;
+			break;
+
+		case Anum_pg_seqam_seqamsetval:
+			nargs = 3;
+			typeId[1] = INTERNALOID;
+			typeId[2] = INT8OID;
+			retTypeId = VOIDOID;
+			break;
+
+		case Anum_pg_seqam_seqamdump:
+			nargs = 2;
+			typeId[1] = INTERNALOID;
+			retTypeId = CSTRINGOID;
+			break;
+
+		case Anum_pg_seqam_seqamrestore:
+			nargs = 3;
+			typeId[1] = INTERNALOID;
+			typeId[2] = CSTRINGOID;
+			retTypeId = VOIDOID;
+			break;
+
+		default:
+			/* should not be here */
+			elog(ERROR, "unrecognized attribute for sequence access method: %d",
+				 attnum);
+			nargs = 0;			/* keep compiler quiet */
+	}
+
+	procOid = LookupFuncName(funcName, nargs, typeId, false);
+	if (get_func_rettype(procOid) != retTypeId)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("function %s should return type %s",
+						func_signature_string(funcName, nargs, NIL, typeId),
+						format_type_be(retTypeId))));
+
+	return ObjectIdGetDatum(procOid);
+}
+
+/*
+ * Transform list of ColumnDefs into types array and names array.
+ */
+static void
+interpret_seqam_column_list(List *columns, ArrayType **seqamcoltypes,
+							ArrayType **seqamcolnames)
+{
+	int			ncols = list_length(columns);
+	Datum	   *coltypes;
+	Datum	   *colnames;
+	ListCell   *l;
+	int			i;
+
+	if (ncols == 0)
+	{
+		*seqamcoltypes = NULL;
+		*seqamcolnames = NULL;
+		return;
+	}
+
+	coltypes = (Datum *) palloc(ncols * sizeof(Datum));
+	colnames = (Datum *) palloc(ncols * sizeof(Datum));
+
+	i = 0;
+	foreach (l, columns)
+	{
+		ColumnDef *d = (ColumnDef *) lfirst(l);
+
+		coltypes[i] = ObjectIdGetDatum(typenameTypeId(NULL, d->typeName));
+		colnames[i] = CStringGetTextDatum(d->colname);
+
+		i++;
+	}
+
+	*seqamcoltypes = construct_array(coltypes, ncols, OIDOID,
+									 sizeof(Oid), true, 'i');
+	*seqamcolnames = construct_array(colnames, ncols, TEXTOID, -1, false, 'i');
+}
+
+/*
+ * make pg_depend entries for a new pg_seqam entry
+ */
+static void
+makeSeqAMDependencies(HeapTuple tuple, ArrayType *seqamcoltypes)
+{
+	Form_pg_seqam	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	int				ncoltypes,
+					i;
+	Oid			   *coltypes;
+	ObjectAddress	myself,
+					referenced;
+
+
+	myself.classId = SeqAccessMethodRelationId;
+	myself.objectId = HeapTupleGetOid(tuple);
+	myself.objectSubId = 0;
+
+	/* Dependency on extension. */
+	recordDependencyOnCurrentExtension(&myself, false);
+
+	/* Dependencies on column types.
+	 *
+	 * The array is always built inside this module so it should be
+	 * correct.
+	 */
+	ncoltypes = ARR_DIMS(seqamcoltypes)[0];
+	Assert(ARR_NDIM(seqamcoltypes) == 1 && ncoltypes > 0 &&
+		   !ARR_HASNULL(seqamcoltypes) &&
+		   ARR_ELEMTYPE(seqamcoltypes) == OIDOID);
+	coltypes = (Oid *) ARR_DATA_PTR(seqamcoltypes);
+
+	for (i = 0; i < ncoltypes; i++)
+	{
+		referenced.classId = TypeRelationId;
+		referenced.objectId = coltypes[i];
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	/* Dependencies on functions. */
+	referenced.classId = ProcedureRelationId;
+	referenced.objectSubId = 0;
+
+	referenced.objectId = seqam->seqamreloptions;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqaminit;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamalloc;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamsetval;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamdump;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+	referenced.objectId = seqam->seqamrestore;
+	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+}
+
+/*
+ * Create a sequence access method record in pg_seqam catalog.
+ *
+ * Only superusers can create a sequence access methods.
+ */
+Oid
+DefineSeqAM(List *names, List *columns, List* definition)
+{
+	char	   *seqamname = strVal(linitial(names));
+	Oid			seqamoid;
+	ListCell   *pl;
+	Relation	rel;
+	Datum		values[Natts_pg_seqam];
+	bool		nulls[Natts_pg_seqam];
+	HeapTuple	tuple;
+	ArrayType  *seqamcoltypes,
+			   *seqamcolnames;
+
+	/* Must be super user. */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied to create sequence access method \"%s\"",
+						seqamname),
+				 errhint("Must be superuser to create a sequence access method.")));
+
+	/* Must not already exist. */
+	seqamoid = get_seqam_oid(seqamname, true);
+	if (OidIsValid(seqamoid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("sequence access method \"%s\" already exists",
+						seqamname)));
+
+	/* Initialize the values. */
+	memset(values, 0, sizeof(values));
+	memset(nulls, false, sizeof(nulls));
+
+	values[Anum_pg_seqam_seqamname - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(seqamname));
+
+	/* Get the column types and names. */
+	interpret_seqam_column_list(columns, &seqamcoltypes, &seqamcolnames);
+	if (seqamcoltypes != NULL)
+		values[Anum_pg_seqam_seqamcoltypes - 1] =
+			PointerGetDatum(seqamcoltypes);
+	else
+		nulls[Anum_pg_seqam_seqamcoltypes - 1] = true;
+	if (seqamcolnames != NULL)
+		values[Anum_pg_seqam_seqamcolnames - 1] =
+			PointerGetDatum(seqamcolnames);
+	else
+		nulls[Anum_pg_seqam_seqamcolnames - 1] = true;
+
+	/*
+	 * Loop over the definition list and extract the information we need.
+	 */
+	foreach(pl, definition)
+	{
+		DefElem    *defel = (DefElem *) lfirst(pl);
+
+		if (pg_strcasecmp(defel->defname, "reloptions") == 0)
+		{
+			values[Anum_pg_seqam_seqamreloptions - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamreloptions);
+		}
+		else if (pg_strcasecmp(defel->defname, "init") == 0)
+		{
+			values[Anum_pg_seqam_seqaminit - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqaminit);
+		}
+		else if (pg_strcasecmp(defel->defname, "alloc") == 0)
+		{
+			values[Anum_pg_seqam_seqamalloc - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamalloc);
+		}
+		else if (pg_strcasecmp(defel->defname, "setval") == 0)
+		{
+			values[Anum_pg_seqam_seqamsetval - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamsetval);
+		}
+		else if (pg_strcasecmp(defel->defname, "dump") == 0)
+		{
+			values[Anum_pg_seqam_seqamdump - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamdump);
+		}
+		else if (pg_strcasecmp(defel->defname, "restore") == 0)
+		{
+			values[Anum_pg_seqam_seqamrestore - 1] =
+				get_seqam_func(defel, Anum_pg_seqam_seqamrestore);
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("sequence access method parameter \"%s\" not recognized",
+						defel->defname)));
+	}
+
+	/*
+	 * Validation.
+	 */
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamreloptions - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method reloptions function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqaminit - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method init function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamalloc - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method alloc function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamsetval - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method setval function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamdump - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method dump function is required")));
+
+	if (!OidIsValid(DatumGetObjectId(values[Anum_pg_seqam_seqamrestore - 1])))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("sequence access method restore function is required")));
+
+	/*
+	 * Insert tuple into pg_seqam.
+	 */
+	rel = heap_open(SeqAccessMethodRelationId, RowExclusiveLock);
+
+	tuple = heap_form_tuple(rel->rd_att, values, nulls);
+
+	seqamoid = simple_heap_insert(rel, tuple);
+
+	CatalogUpdateIndexes(rel, tuple);
+
+	makeSeqAMDependencies(tuple, seqamcoltypes);
+
+	heap_freetuple(tuple);
+
+	/* Post creation hook */
+	InvokeObjectPostCreateHook(SeqAccessMethodRelationId, seqamoid, 0);
+
+	heap_close(rel, RowExclusiveLock);
+
+	return seqamoid;
+}
+
+/*
+ * Drop a sequence access method.
+ */
+void
+RemoveSeqAMById(Oid seqamoid)
+{
+	Relation	rel;
+	HeapTuple	tuple;
+	Form_pg_seqam seqam;
+
+	/*
+	 * Find the target tuple
+	 */
+	rel = heap_open(SeqAccessMethodRelationId, RowExclusiveLock);
+
+	tuple = SearchSysCache1(SEQAMOID, ObjectIdGetDatum(seqamoid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for sequence access method %u",
+			 seqamoid);
+
+	seqam = (Form_pg_seqam) GETSTRUCT(tuple);
+	/* Can't drop builtin local sequence access method. */
+	if (seqamoid == LOCAL_SEQAM_OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("permission denied for sequence access method %s",
+						NameStr(seqam->seqamname))));
+
+	/*
+	 * Remove the pg_seqam tuple (this will roll back if we fail below)
+	 */
+	simple_heap_delete(rel, &tuple->t_self);
+
+	ReleaseSysCache(tuple);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+/*
  * get_seqam_oid - given a sequence AM name, look up the OID
  *
  * If missing_ok is false, throw an error if SeqAM name not found.  If true,
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index bacb242..9340798 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -16,6 +16,7 @@
 
 #include "access/htup_details.h"
 #include "access/xact.h"
+#include "access/seqam.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -157,7 +158,8 @@ static const Oid object_classes[MAX_OCLASS] = {
 	DefaultAclRelationId,		/* OCLASS_DEFACL */
 	ExtensionRelationId,		/* OCLASS_EXTENSION */
 	EventTriggerRelationId,		/* OCLASS_EVENT_TRIGGER */
-	PolicyRelationId			/* OCLASS_POLICY */
+	PolicyRelationId,			/* OCLASS_POLICY */
+	SeqAccessMethodRelationId	/* OCLASS_SEQAM */
 };
 
 
@@ -1265,6 +1267,10 @@ doDeletion(const ObjectAddress *object, int flags)
 			RemovePolicyById(object->objectId);
 			break;
 
+		case OCLASS_SEQAM:
+			RemoveSeqAMById(object->objectId);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized object class: %u",
 				 object->classId);
@@ -2373,6 +2379,9 @@ getObjectClass(const ObjectAddress *object)
 
 		case PolicyRelationId:
 			return OCLASS_POLICY;
+
+		case SeqAccessMethodRelationId:
+			return OCLASS_SEQAM;
 	}
 
 	/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2412a24..decbd12 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -430,6 +430,18 @@ static const ObjectPropertyType ObjectProperty[] =
 		Anum_pg_type_typacl,
 		ACL_KIND_TYPE,
 		true
+	},
+	{
+		SeqAccessMethodRelationId,
+		SeqAMOidIndexId,
+		SEQAMOID,
+		SEQAMNAME,
+		Anum_pg_seqam_seqamname,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		InvalidAttrNumber,
+		-1,
+		true
 	}
 };
 
@@ -529,7 +541,9 @@ ObjectTypeMap[] =
 	/* OCLASS_EVENT_TRIGGER */
 	{ "event trigger", OBJECT_EVENT_TRIGGER },
 	/* OCLASS_POLICY */
-	{ "policy", OBJECT_POLICY }
+	{ "policy", OBJECT_POLICY },
+	/* OCLASS_SEQAM */
+	{ "sequence access method", OBJECT_SEQAM }
 };
 
 
@@ -671,6 +685,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 			case OBJECT_FDW:
 			case OBJECT_FOREIGN_SERVER:
 			case OBJECT_EVENT_TRIGGER:
+			case OBJECT_SEQAM:
 				address = get_object_address_unqualified(objtype,
 														 objname, missing_ok);
 				break;
@@ -897,6 +912,9 @@ get_object_address_unqualified(ObjectType objtype,
 			case OBJECT_EVENT_TRIGGER:
 				msg = gettext_noop("event trigger name cannot be qualified");
 				break;
+			case OBJECT_SEQAM:
+				msg = gettext_noop("sequence access method name cannot be qualified");
+				break;
 			default:
 				elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 				msg = NULL;		/* placate compiler */
@@ -957,6 +975,11 @@ get_object_address_unqualified(ObjectType objtype,
 			address.objectId = get_event_trigger_oid(name, missing_ok);
 			address.objectSubId = 0;
 			break;
+		case OBJECT_SEQAM:
+			address.classId = SeqAccessMethodRelationId;
+			address.objectId = get_seqam_oid(name, missing_ok);
+			address.objectSubId = 0;
+			break;
 		default:
 			elog(ERROR, "unrecognized objtype: %d", (int) objtype);
 			/* placate compiler, which doesn't know elog won't return */
@@ -1721,6 +1744,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 			break;
 		case OBJECT_TSPARSER:
 		case OBJECT_TSTEMPLATE:
+		case OBJECT_SEQAM:
 			/* We treat these object types as being owned by superusers */
 			if (!superuser_arg(roleid))
 				ereport(ERROR,
@@ -2655,6 +2679,21 @@ getObjectDescription(const ObjectAddress *object)
 				break;
 			}
 
+		case OCLASS_SEQAM:
+			{
+				HeapTuple	tup;
+
+				tup = SearchSysCache1(SEQAMOID,
+									  ObjectIdGetDatum(object->objectId));
+				if (!HeapTupleIsValid(tup))
+					elog(ERROR, "cache lookup failed for sequence access method %u",
+						 object->objectId);
+				appendStringInfo(&buffer, _("sequence access method %s"),
+						NameStr(((Form_pg_seqam) GETSTRUCT(tup))->seqamname));
+				ReleaseSysCache(tup);
+				break;
+			}
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
@@ -3132,6 +3171,10 @@ getObjectTypeDescription(const ObjectAddress *object)
 			appendStringInfoString(&buffer, "policy");
 			break;
 
+		case OCLASS_SEQAM:
+			appendStringInfoString(&buffer, "sequence access method");
+			break;
+
 		default:
 			appendStringInfo(&buffer, "unrecognized %u", object->classId);
 			break;
@@ -4026,6 +4069,27 @@ getObjectIdentityParts(const ObjectAddress *object,
 				break;
 			}
 
+		case OCLASS_SEQAM:
+			{
+				char	   *seqamname;
+				HeapTuple	tup;
+				Form_pg_seqam seqamForm;
+
+				tup = SearchSysCache1(SEQAMOID,
+									  ObjectIdGetDatum(object->objectId));
+				if (!HeapTupleIsValid(tup))
+					elog(ERROR, "cache lookup failed for sequence access method %u",
+						 object->objectId);
+				seqamForm = (Form_pg_seqam) GETSTRUCT(tup);
+				seqamname = pstrdup(NameStr(seqamForm->seqamname));
+				ReleaseSysCache(tup);
+				appendStringInfoString(&buffer,
+							   quote_identifier(seqamname));
+				if (objname)
+					*objname = list_make1(seqamname);
+				break;
+			}
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index e5185ba..01d772a 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -421,6 +421,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
 				args = strVal(linitial(objargs));
 			}
 			break;
+		case OBJECT_SEQAM:
+			msg = gettext_noop("sequence access method \"%s\" does not exist, skipping");
+			name = NameListToString(objname);
+			break;
 		default:
 			elog(ERROR, "unexpected object type (%d)", (int) objtype);
 			break;
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index a33a5ad..0c4d63e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -72,6 +72,7 @@ typedef enum
 } event_trigger_command_tag_check_result;
 
 static event_trigger_support_data event_trigger_support[] = {
+	{"ACCESS METHOD FOR SEQUENCES", true},
 	{"AGGREGATE", true},
 	{"CAST", true},
 	{"CONSTRAINT", true},
@@ -1075,6 +1076,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_POLICY:
 		case OBJECT_RULE:
 		case OBJECT_SCHEMA:
+		case OBJECT_SEQAM:
 		case OBJECT_SEQUENCE:
 		case OBJECT_TABCONSTRAINT:
 		case OBJECT_TABLE:
@@ -1134,6 +1136,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
 		case OCLASS_DEFACL:
 		case OCLASS_EXTENSION:
 		case OCLASS_POLICY:
+		case OCLASS_SEQAM:
 			return true;
 
 		case MAX_OCLASS:
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index fc0bd83..f5f8de6 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1620,9 +1620,26 @@ seqrel_update_relam(Oid seqoid, Oid seqamid)
 
 	if (pgcform->relam != seqamid)
 	{
+		ObjectAddress	myself,
+						referenced;
+
 		pgcform->relam = seqamid;
 		simple_heap_update(rd, &ctup->t_self, ctup);
 		CatalogUpdateIndexes(rd, ctup);
+
+		/* Remove dependency on previous SeqAM */
+		deleteDependencyRecordsForClass(RelationRelationId, seqoid,
+										SeqAccessMethodRelationId,
+										DEPENDENCY_NORMAL);
+
+		/* Record dependency on new SeqAM */
+		myself.classId = RelationRelationId;
+		myself.objectId = seqoid;
+		myself.objectSubId = 0;
+		referenced.classId = SeqAccessMethodRelationId;
+		referenced.objectId = seqamid;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
 	heap_freetuple(ctup);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 8d6a402..15e98f1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -586,7 +586,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
 	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
 
-	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
+	MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P
+	MOVE
 
 	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
@@ -5167,6 +5168,15 @@ DefineStmt:
 					n->definition = list_make1(makeDefElem("from", (Node *) $5));
 					$$ = (Node *)n;
 				}
+			| CREATE ACCESS METHOD FOR SEQUENCES name '(' OptTableFuncElementList ')' AS definition
+				{
+					DefineStmt *n = makeNode(DefineStmt);
+					n->kind = OBJECT_SEQAM;
+					n->defnames = list_make1(makeString($6));
+					n->args = $8;
+					n->definition = $11;
+					$$ = (Node *)n;
+				}
 		;
 
 definition: '(' def_list ')'						{ $$ = $2; }
@@ -5625,6 +5635,7 @@ drop_type:	TABLE									{ $$ = OBJECT_TABLE; }
 			| TEXT_P SEARCH DICTIONARY				{ $$ = OBJECT_TSDICTIONARY; }
 			| TEXT_P SEARCH TEMPLATE				{ $$ = OBJECT_TSTEMPLATE; }
 			| TEXT_P SEARCH CONFIGURATION			{ $$ = OBJECT_TSCONFIGURATION; }
+			| ACCESS METHOD FOR SEQUENCES 			{ $$ = OBJECT_SEQAM; }
 		;
 
 any_name_list:
@@ -13356,6 +13367,7 @@ unreserved_keyword:
 			| MATCH
 			| MATERIALIZED
 			| MAXVALUE
+			| METHOD
 			| MINUTE_P
 			| MINVALUE
 			| MODE
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 3533cfa..3add752 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -18,6 +18,7 @@
 
 #include "access/htup_details.h"
 #include "access/reloptions.h"
+#include "access/seqam.h"
 #include "access/twophase.h"
 #include "access/xact.h"
 #include "access/xlog.h"
@@ -1106,6 +1107,10 @@ ProcessUtilitySlow(Node *parsetree,
 							Assert(stmt->args == NIL);
 							DefineCollation(stmt->defnames, stmt->definition);
 							break;
+						case OBJECT_SEQAM:
+							Assert(list_length(stmt->defnames) == 1);
+							DefineSeqAM(stmt->defnames, stmt->args, stmt->definition);
+							break;
 						default:
 							elog(ERROR, "unrecognized define stmt type: %d",
 								 (int) stmt->kind);
@@ -1960,6 +1965,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_POLICY:
 					tag = "DROP POLICY";
 					break;
+				case OBJECT_SEQAM:
+					tag = "DROP ACCESS METHOD FOR SEQUENCES";
+					break;
 				default:
 					tag = "???";
 			}
@@ -2056,6 +2064,9 @@ CreateCommandTag(Node *parsetree)
 				case OBJECT_COLLATION:
 					tag = "CREATE COLLATION";
 					break;
+				case OBJECT_SEQAM:
+					tag = "CREATE ACCESS METHOD FOR SEQUENCES";
+					break;
 				default:
 					tag = "???";
 			}
diff --git a/src/include/access/seqam.h b/src/include/access/seqam.h
index 0a437ef..bd296c9 100644
--- a/src/include/access/seqam.h
+++ b/src/include/access/seqam.h
@@ -28,6 +28,9 @@ typedef struct SequenceHandle SequenceHandle;
 
 extern char *default_seqam;
 
+extern Oid DefineSeqAM(List *names, List *columns, List *definition);
+extern void RemoveSeqAMById(Oid seqamoid);
+
 extern int seqam_extra_columns(Oid seqamid, Oid **coltypes, char ***colnames);
 extern void seqam_init(Oid seqamid, Oid seqrelid, List *seqparams,
 					   List *reloptions, Datum *values, bool *nulls);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 6481ac8..9485d02 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -148,6 +148,7 @@ typedef enum ObjectClass
 	OCLASS_EXTENSION,			/* pg_extension */
 	OCLASS_EVENT_TRIGGER,		/* pg_event_trigger */
 	OCLASS_POLICY,				/* pg_policy */
+	OCLASS_SEQAM,				/* pg_seqam */
 	MAX_OCLASS					/* MUST BE LAST */
 } ObjectClass;
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 401250e..c11866b 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -1819,9 +1819,9 @@ DATA(insert OID = 1765 (  setval			PGNSP PGUID 12 1 0 0 0 f f f f t f v 3 0 20 "
 DESCR("set sequence value and is_called status");
 DATA(insert OID = 3078 (  pg_sequence_parameters	PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2249 "26" "{26,20,20,20,20,16}" "{i,o,o,o,o,o}" "{sequence_oid,start_value,minimum_value,maximum_value,increment,cycle_option}" _null_ pg_sequence_parameters _null_ _null_ _null_));
 DESCR("sequence parameters, for use by information schema");
-DATA(insert OID = 3261 (  pg_sequence_dump_state	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2275 "2205" _null_ _null_ _null_ _null_	pg_sequence_dump_state _null_ _null_ _null_ ));
+DATA(insert OID = 3277 (  pg_sequence_dump_state	PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 2275 "2205" _null_ _null_ _null_ _null_	pg_sequence_dump_state _null_ _null_ _null_ ));
 DESCR("Dump state of a sequence");
-DATA(insert OID = 3262 (  pg_sequence_restore_state	   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 2275" _null_ _null_ _null_ _null_	pg_sequence_restore_state _null_ _null_ _null_ ));
+DATA(insert OID = 3278 (  pg_sequence_restore_state	   PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 2278 "2205 2275" _null_ _null_ _null_ _null_	pg_sequence_restore_state _null_ _null_ _null_ ));
 DESCR("Restore state of a sequence");
 
 DATA(insert OID = 1579 (  varbit_in			PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1562 "2275 26 23" _null_ _null_ _null_ _null_ varbit_in _null_ _null_ _null_ ));
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2dd2f71..a9580e8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1231,6 +1231,7 @@ typedef enum ObjectType
 	OBJECT_ROLE,
 	OBJECT_RULE,
 	OBJECT_SCHEMA,
+	OBJECT_SEQAM,
 	OBJECT_SEQUENCE,
 	OBJECT_TABCONSTRAINT,
 	OBJECT_TABLE,
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7c243ec..ece52b0 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -236,6 +236,7 @@ PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
 PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
 PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
 PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
 PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
 PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
-- 
1.9.1

